From d0955ab56f70cd322ccebd06e66b2515ce3c9998 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 1 Mar 2019 04:45:51 -0700 Subject: [PATCH 0001/1778] Fix Scrutinizer warning --- src/main/java/io/github/classgraph/ClassRefTypeSignature.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassRefTypeSignature.java b/src/main/java/io/github/classgraph/ClassRefTypeSignature.java index 620b75000..b75b29b2b 100644 --- a/src/main/java/io/github/classgraph/ClassRefTypeSignature.java +++ b/src/main/java/io/github/classgraph/ClassRefTypeSignature.java @@ -285,7 +285,7 @@ public boolean equalsIgnoringTypeParams(final TypeSignature other) { protected String toStringInternal(final boolean useSimpleNames) { final StringBuilder buf = new StringBuilder(); // Only append the base class name if not using simple names, or if there are no suffixes - if (!useSimpleNames || suffixes.size() == 0) { + if (!useSimpleNames || suffixes.isEmpty()) { buf.append(useSimpleNames ? ClassInfo.getSimpleName(className) : className); if (!typeArguments.isEmpty()) { buf.append('<'); @@ -300,7 +300,7 @@ protected String toStringInternal(final boolean useSimpleNames) { } } // Only use the last suffix if using simple names - for (int i = useSimpleNames && suffixes.size() > 0 ? suffixes.size() - 1 : 0; i < suffixes.size(); i++) { + for (int i = useSimpleNames && !suffixes.isEmpty() ? suffixes.size() - 1 : 0; i < suffixes.size(); 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 From 51e5e041ee9ce032b4b0e716177f02c222f95cc9 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 2 Mar 2019 22:58:54 -0700 Subject: [PATCH 0002/1778] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4fe571e13..ecf72415f 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ClassGraph Logo       Duke Award Logo -ClassGraph (formerly **FastClasspathScanner**) is an uber-fast, ultra-lightweight, parallelized classpath scanner, module scanner, and build-time/runtime annotation processor for Java, Scala, Kotlin and other JVM languages. +ClassGraph (formerly **FastClasspathScanner**) is an uber-fast, ultra-lightweight, parallelized classpath and module 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. | |-----------------------------| From 8a0f8cf83d2037e4ef724b1f523ef98bdb990931 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 2 Mar 2019 22:59:30 -0700 Subject: [PATCH 0003/1778] Update project description --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5caad0585..4b296f8a7 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ 4.8.12-SNAPSHOT ClassGraph - The uber-fast, ultra-lightweight classpath and module path scanner for JVM languages. + The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. https://github.com/classgraph/classgraph From 550cd92c511fffd874daa4b19c206d51bf0399b5 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 3 Mar 2019 00:20:15 -0700 Subject: [PATCH 0004/1778] Fix moduleInfoSource comment (#328) --- pom.xml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 4b296f8a7..544b867fd 100644 --- a/pom.xml +++ b/pom.xml @@ -236,11 +236,7 @@ - /** - ClassGraph, the uber-fast, ultra-lightweight, parallelized - Java classpath scanner, - module scanner, and annotation processor for JVM - languages. https://github.com/classgraph/classgraph */ + /** ${project.name}: ${project.description} ( ${project.url} ) */ module io.github.classgraph { exports io.github.classgraph; From f955e578fe0b7cb8e8bfcbb8ea04322d52f3ae50 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 3 Mar 2019 03:17:49 -0700 Subject: [PATCH 0005/1778] Speed up fallback classloading by using ByteBuffer instead of byte[] --- .../java/io/github/classgraph/ClassGraphClassLoader.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java index 8b3fd533a..c99dff3ac 100644 --- a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java +++ b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java @@ -31,6 +31,7 @@ import java.io.IOException; import java.io.InputStream; import java.net.URL; +import java.nio.ByteBuffer; import java.util.Enumeration; import nonapi.io.github.classgraph.utils.JarUtils; @@ -110,8 +111,8 @@ protected Class findClass(final String className) // 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 - final byte[] resourceContent = resource.load(); - return defineClass(className, resourceContent, 0, resourceContent.length); + final ByteBuffer resourceByteBuffer = resource.read(); + return defineClass(className, resourceByteBuffer, null); } catch (final IOException e) { throw new ClassNotFoundException("Could not load classfile for class " + className + " : " + e); } finally { From 6623de17286d36f0deb5607aebd79dc3d85a6b31 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 3 Mar 2019 04:35:09 -0700 Subject: [PATCH 0006/1778] Fix Javadoc --- src/main/java/io/github/classgraph/Resource.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/github/classgraph/Resource.java b/src/main/java/io/github/classgraph/Resource.java index f442c2976..cfbf9580e 100644 --- a/src/main/java/io/github/classgraph/Resource.java +++ b/src/main/java/io/github/classgraph/Resource.java @@ -389,9 +389,9 @@ public URL getClasspathElementURL() { /** * Get the classpath element {@link File}. * - * @return The {@link File} for the classpath element package root dir or jar that this {@Resource} was found - * within, or null if this {@link Resource} was found in a module backed by a "jrt:" URI, or a module - * with an unknown location. + * @return The {@link File} for the classpath element package root dir or jar that this {@link Resource} was + * found within, or null if this {@link Resource} was found in a module backed by a "jrt:" URI, or a + * module with an unknown location. */ public File getClasspathElementFile() { return classpathElement.getFile(); From 0f838da757aa03630feee0f7b092a74773b29abe Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 5 Mar 2019 01:03:15 -0700 Subject: [PATCH 0007/1778] Add missing .equals() and .hashCode() methods --- .../classgraph/AnnotationParameterValue.java | 3 ++ .../classgraph/ObjectTypedValueWrapper.java | 38 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/src/main/java/io/github/classgraph/AnnotationParameterValue.java b/src/main/java/io/github/classgraph/AnnotationParameterValue.java index 76efb2324..0ec92633a 100644 --- a/src/main/java/io/github/classgraph/AnnotationParameterValue.java +++ b/src/main/java/io/github/classgraph/AnnotationParameterValue.java @@ -256,6 +256,9 @@ public int compareTo(final AnnotationParameterValue o) { */ @Override public boolean equals(final Object obj) { + if (this == obj) { + return true; + } if (!(obj instanceof AnnotationParameterValue)) { return false; } diff --git a/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java b/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java index 644d64c1a..590332963 100644 --- a/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java +++ b/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java @@ -29,6 +29,8 @@ package io.github.classgraph; import java.lang.reflect.Array; +import java.util.Arrays; +import java.util.Objects; import java.util.Set; /** A union type, used for typesafe serialization/deserialization to/from JSON. Only one field is ever set. */ @@ -109,6 +111,8 @@ public ObjectTypedValueWrapper() { super(); } + // ------------------------------------------------------------------------------------------------------------- + /** * Constructor. * @@ -548,4 +552,38 @@ void findReferencedClassNames(final Set referencedClassNames) { } } } + + // ------------------------------------------------------------------------------------------------------------- + + @Override + public int hashCode() { + return Objects.hash(enumValue, classRef, annotationInfo, stringValue, integerValue, longValue, shortValue, + booleanValue, characterValue, floatValue, doubleValue, byteValue, Arrays.hashCode(stringArrayValue), + Arrays.hashCode(intArrayValue), Arrays.hashCode(longArrayValue), Arrays.hashCode(shortArrayValue), + Arrays.hashCode(booleanArrayValue), Arrays.hashCode(charArrayValue), + Arrays.hashCode(floatArrayValue), Arrays.hashCode(doubleArrayValue), + Arrays.hashCode(byteArrayValue), Arrays.hashCode(objectArrayValue)); + } + + @Override + public boolean equals(final Object other) { + if (other == this) { + return true; + } else if (!(other instanceof ObjectTypedValueWrapper)) { + return false; + } + final ObjectTypedValueWrapper o = (ObjectTypedValueWrapper) other; + return Objects.equals(enumValue, o.enumValue) && Objects.equals(classRef, o.classRef) + && Objects.equals(annotationInfo, o.annotationInfo) && Objects.equals(stringValue, o.stringValue) + && Objects.equals(integerValue, o.integerValue) && Objects.equals(longValue, o.longValue) + && Objects.equals(shortValue, o.shortValue) && Objects.equals(booleanValue, o.booleanValue) + && Objects.equals(characterValue, o.characterValue) && Objects.equals(floatValue, o.floatValue) + && Objects.equals(doubleValue, o.doubleValue) && Objects.equals(byteValue, o.byteValue) + && Arrays.equals(stringArrayValue, o.stringArrayValue) + && Arrays.equals(intArrayValue, o.intArrayValue) && Arrays.equals(longArrayValue, o.longArrayValue) + && Arrays.equals(shortArrayValue, o.shortArrayValue) + && Arrays.equals(floatArrayValue, o.floatArrayValue) + && Arrays.equals(byteArrayValue, o.byteArrayValue) + && Arrays.deepEquals(objectArrayValue, o.objectArrayValue); + } } \ No newline at end of file From c59e7ba76f9abc1ec4385d4f2674ebcb337861b7 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 5 Mar 2019 01:22:49 -0700 Subject: [PATCH 0008/1778] Add missing equals() and hashCode() methods --- .../classgraph/MethodParameterInfo.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/main/java/io/github/classgraph/MethodParameterInfo.java b/src/main/java/io/github/classgraph/MethodParameterInfo.java index 2608f561a..53306c5ff 100644 --- a/src/main/java/io/github/classgraph/MethodParameterInfo.java +++ b/src/main/java/io/github/classgraph/MethodParameterInfo.java @@ -30,7 +30,9 @@ import java.lang.annotation.Repeatable; import java.lang.reflect.Modifier; +import java.util.Arrays; import java.util.Collections; +import java.util.Objects; /** * Information on the parameters of a method. @@ -240,6 +242,32 @@ protected void setScanResult(final ScanResult scanResult) { // ------------------------------------------------------------------------------------------------------------- + /* (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 MethodParameterInfo)) { + return false; + } + final MethodParameterInfo other = (MethodParameterInfo) obj; + return Objects.equals(methodInfo, other.methodInfo) + && Objects.deepEquals(annotationInfo, other.annotationInfo) && modifiers == other.modifiers + && Objects.equals(typeDescriptor, other.typeDescriptor) + && Objects.equals(typeSignature, other.typeSignature) && Objects.equals(name, other.name); + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return Objects.hash(methodInfo, Arrays.hashCode(annotationInfo), typeDescriptor, typeSignature, name) + + modifiers; + } + /** * Convert modifiers into a string representation, e.g. "public static final". * From 4f906edab4e2827e6b12b52018ba7bfbb7fc6eaf Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 5 Mar 2019 01:23:26 -0700 Subject: [PATCH 0009/1778] Make equals() methods consistent --- .../java/io/github/classgraph/AnnotationClassRef.java | 4 +++- .../java/io/github/classgraph/AnnotationEnumValue.java | 4 +++- src/main/java/io/github/classgraph/AnnotationInfo.java | 8 +++++--- src/main/java/io/github/classgraph/ClassInfo.java | 6 +----- src/main/java/io/github/classgraph/FieldInfo.java | 6 +----- src/main/java/io/github/classgraph/MethodInfo.java | 6 +----- .../io/github/classgraph/ObjectTypedValueWrapper.java | 6 ++++++ 7 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/main/java/io/github/classgraph/AnnotationClassRef.java b/src/main/java/io/github/classgraph/AnnotationClassRef.java index 50a6d0ccb..46fedbcca 100644 --- a/src/main/java/io/github/classgraph/AnnotationClassRef.java +++ b/src/main/java/io/github/classgraph/AnnotationClassRef.java @@ -198,7 +198,9 @@ public int hashCode() { */ @Override public boolean equals(final Object obj) { - if (!(obj instanceof AnnotationClassRef)) { + if (this == obj) { + return true; + } else if (!(obj instanceof AnnotationClassRef)) { return false; } return getTypeSignature().equals(((AnnotationClassRef) obj).getTypeSignature()); diff --git a/src/main/java/io/github/classgraph/AnnotationEnumValue.java b/src/main/java/io/github/classgraph/AnnotationEnumValue.java index 94e05e1a1..9dc144b17 100644 --- a/src/main/java/io/github/classgraph/AnnotationEnumValue.java +++ b/src/main/java/io/github/classgraph/AnnotationEnumValue.java @@ -161,7 +161,9 @@ public int compareTo(final AnnotationEnumValue o) { */ @Override public boolean equals(final Object o) { - if (!(o instanceof AnnotationEnumValue)) { + if (o == this) { + return true; + } else if (!(o instanceof AnnotationEnumValue)) { return false; } return compareTo((AnnotationEnumValue) o) == 0; diff --git a/src/main/java/io/github/classgraph/AnnotationInfo.java b/src/main/java/io/github/classgraph/AnnotationInfo.java index 4c0c38042..d4c8f4b07 100644 --- a/src/main/java/io/github/classgraph/AnnotationInfo.java +++ b/src/main/java/io/github/classgraph/AnnotationInfo.java @@ -502,11 +502,13 @@ public int compareTo(final AnnotationInfo o) { */ @Override public boolean equals(final Object obj) { - if (!(obj instanceof AnnotationInfo)) { + if (obj == this) { + return true; + } else if (!(obj instanceof AnnotationInfo)) { return false; } - final AnnotationInfo o = (AnnotationInfo) obj; - return this.compareTo(o) == 0; + final AnnotationInfo other = (AnnotationInfo) obj; + return this.compareTo(other) == 0; } /* (non-Javadoc) diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index 824395541..d3e98c600 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -2664,11 +2664,7 @@ public int compareTo(final ClassInfo o) { public boolean equals(final Object obj) { if (this == obj) { return true; - } - if (obj == null) { - return false; - } - if (this.getClass() != obj.getClass()) { + } else if (!(obj instanceof ClassInfo)) { return false; } final ClassInfo other = (ClassInfo) obj; diff --git a/src/main/java/io/github/classgraph/FieldInfo.java b/src/main/java/io/github/classgraph/FieldInfo.java index e09228ff0..94e552778 100644 --- a/src/main/java/io/github/classgraph/FieldInfo.java +++ b/src/main/java/io/github/classgraph/FieldInfo.java @@ -423,11 +423,7 @@ protected void findReferencedClassNames(final Set classNames) { public boolean equals(final Object obj) { if (this == obj) { return true; - } - if (obj == null) { - return false; - } - if (this.getClass() != obj.getClass()) { + } else if (!(obj instanceof FieldInfo)) { return false; } final FieldInfo other = (FieldInfo) obj; diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index f99ce0327..6764ebec0 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -680,11 +680,7 @@ protected void findReferencedClassNames(final Set classNames) { public boolean equals(final Object obj) { if (this == obj) { return true; - } - if (obj == null) { - return false; - } - if (this.getClass() != obj.getClass()) { + } else if (!(obj instanceof MethodInfo)) { return false; } final MethodInfo other = (MethodInfo) obj; diff --git a/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java b/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java index 590332963..804058dc2 100644 --- a/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java +++ b/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java @@ -555,6 +555,9 @@ void findReferencedClassNames(final Set referencedClassNames) { // ------------------------------------------------------------------------------------------------------------- + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ @Override public int hashCode() { return Objects.hash(enumValue, classRef, annotationInfo, stringValue, integerValue, longValue, shortValue, @@ -565,6 +568,9 @@ public int hashCode() { Arrays.hashCode(byteArrayValue), Arrays.hashCode(objectArrayValue)); } + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ @Override public boolean equals(final Object other) { if (other == this) { From 3e39b7c5ec90c534ae344348922d7792b4a306c2 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 5 Mar 2019 01:49:32 -0700 Subject: [PATCH 0010/1778] Fix compareTo() --- .../classgraph/AnnotationParameterValue.java | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/github/classgraph/AnnotationParameterValue.java b/src/main/java/io/github/classgraph/AnnotationParameterValue.java index 0ec92633a..61de57327 100644 --- a/src/main/java/io/github/classgraph/AnnotationParameterValue.java +++ b/src/main/java/io/github/classgraph/AnnotationParameterValue.java @@ -234,21 +234,32 @@ void toStringParamValueOnly(final StringBuilder buf) { } } + /** + * To string, param value only. + * + * @return the string. + */ + private String toStringParamValueOnly() { + final StringBuilder buf = new StringBuilder(); + toStringParamValueOnly(buf); + return buf.toString(); + } + /* (non-Javadoc) * @see java.lang.Comparable#compareTo(java.lang.Object) */ @Override - public int compareTo(final AnnotationParameterValue o) { - final int diff = name.compareTo(o.getName()); + public int compareTo(final AnnotationParameterValue other) { + final int diff = name.compareTo(other.getName()); if (diff != 0) { return diff; } - // Use toString() order and get() (which can be slow) as a last-ditch effort -- only happens + // Use toString() order (which can be slow) as a last-ditch effort -- only happens // if the annotation has multiple parameters of the same name but different value. final Object p0 = getValue(); - final Object p1 = o.getValue(); + final Object p1 = other.getValue(); return p0 == null || p1 == null ? (p0 == null ? 0 : 1) - (p1 == null ? 0 : 1) - : p0.toString().compareTo(p1.toString()); + : toStringParamValueOnly().compareTo(other.toStringParamValueOnly()); } /* (non-Javadoc) @@ -258,13 +269,12 @@ public int compareTo(final AnnotationParameterValue o) { public boolean equals(final Object obj) { if (this == obj) { return true; - } - if (!(obj instanceof AnnotationParameterValue)) { + } else if (!(obj instanceof AnnotationParameterValue)) { return false; } - final AnnotationParameterValue o = (AnnotationParameterValue) obj; - return this.compareTo(o) == 0 && (value == null) == (o.value == null) - && (value == null || value.equals(o.value)); + final AnnotationParameterValue other = (AnnotationParameterValue) obj; + return this.name.equals(other.name) && (value == null) == (other.value == null) + && (value == null || value.equals(other.value)); } /* (non-Javadoc) From b6769b8686835fc4e7cdfe6d01282b1b30368eda Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 5 Mar 2019 02:00:34 -0700 Subject: [PATCH 0011/1778] Consistency fixes for equals() methods --- .../io/github/classgraph/AnnotationClassRef.java | 2 +- .../io/github/classgraph/AnnotationEnumValue.java | 8 ++++---- .../io/github/classgraph/AnnotationInfoList.java | 9 ++++----- .../classgraph/AnnotationParameterValue.java | 2 +- .../io/github/classgraph/ArrayTypeSignature.java | 9 ++++----- .../io/github/classgraph/BaseTypeSignature.java | 7 ++++++- src/main/java/io/github/classgraph/ClassInfo.java | 2 +- .../java/io/github/classgraph/ClassInfoList.java | 9 ++++----- .../github/classgraph/ClassRefTypeSignature.java | 5 ++--- .../io/github/classgraph/ClassTypeSignature.java | 5 ++--- .../io/github/classgraph/ClasspathElementDir.java | 8 ++++---- .../github/classgraph/ClasspathElementModule.java | 8 ++++---- .../io/github/classgraph/ClasspathElementZip.java | 8 ++++---- src/main/java/io/github/classgraph/FieldInfo.java | 2 +- .../java/io/github/classgraph/MethodInfo.java | 2 +- .../io/github/classgraph/MethodTypeSignature.java | 4 +++- .../java/io/github/classgraph/ModuleInfo.java | 8 ++++---- src/main/java/io/github/classgraph/ModuleRef.java | 8 +++++--- .../java/io/github/classgraph/PackageInfo.java | 8 ++++---- src/main/java/io/github/classgraph/Resource.java | 6 ++++-- .../java/io/github/classgraph/TypeArgument.java | 4 +++- .../java/io/github/classgraph/TypeParameter.java | 4 +++- .../github/classgraph/TypeVariableSignature.java | 4 +++- .../fastzipfilereader/FastZipEntry.java | 3 +-- .../fastzipfilereader/PhysicalZipFile.java | 5 ++--- .../fastzipfilereader/ZipFileSlice.java | 9 ++++----- .../classgraph/json/ParameterizedTypeImpl.java | 15 +++++---------- .../classgraph/json/ReferenceEqualityKey.java | 9 +++++++-- 28 files changed, 91 insertions(+), 82 deletions(-) diff --git a/src/main/java/io/github/classgraph/AnnotationClassRef.java b/src/main/java/io/github/classgraph/AnnotationClassRef.java index 46fedbcca..98a8e9cd2 100644 --- a/src/main/java/io/github/classgraph/AnnotationClassRef.java +++ b/src/main/java/io/github/classgraph/AnnotationClassRef.java @@ -198,7 +198,7 @@ public int hashCode() { */ @Override public boolean equals(final Object obj) { - if (this == obj) { + if (obj == this) { return true; } else if (!(obj instanceof AnnotationClassRef)) { return false; diff --git a/src/main/java/io/github/classgraph/AnnotationEnumValue.java b/src/main/java/io/github/classgraph/AnnotationEnumValue.java index 9dc144b17..6f2ca0f48 100644 --- a/src/main/java/io/github/classgraph/AnnotationEnumValue.java +++ b/src/main/java/io/github/classgraph/AnnotationEnumValue.java @@ -160,13 +160,13 @@ public int compareTo(final AnnotationEnumValue o) { * @see java.lang.Object#equals(java.lang.Object) */ @Override - public boolean equals(final Object o) { - if (o == this) { + public boolean equals(final Object obj) { + if (obj == this) { return true; - } else if (!(o instanceof AnnotationEnumValue)) { + } else if (!(obj instanceof AnnotationEnumValue)) { return false; } - return compareTo((AnnotationEnumValue) o) == 0; + return compareTo((AnnotationEnumValue) obj) == 0; } /* (non-Javadoc) diff --git a/src/main/java/io/github/classgraph/AnnotationInfoList.java b/src/main/java/io/github/classgraph/AnnotationInfoList.java index bef4cb9df..eeb19e1d6 100644 --- a/src/main/java/io/github/classgraph/AnnotationInfoList.java +++ b/src/main/java/io/github/classgraph/AnnotationInfoList.java @@ -394,14 +394,13 @@ public AnnotationInfoList getRepeatable(final String name) { * @see java.util.ArrayList#equals(java.lang.Object) */ @Override - public boolean equals(final Object o) { - if (this == o) { + public boolean equals(final Object obj) { + if (this == obj) { return true; - } - if (!(o instanceof AnnotationInfoList)) { + } else if (!(obj instanceof AnnotationInfoList)) { return false; } - final AnnotationInfoList other = (AnnotationInfoList) o; + final AnnotationInfoList other = (AnnotationInfoList) obj; if ((directlyRelatedAnnotations == null) != (other.directlyRelatedAnnotations == null)) { return false; } diff --git a/src/main/java/io/github/classgraph/AnnotationParameterValue.java b/src/main/java/io/github/classgraph/AnnotationParameterValue.java index 61de57327..e36c22684 100644 --- a/src/main/java/io/github/classgraph/AnnotationParameterValue.java +++ b/src/main/java/io/github/classgraph/AnnotationParameterValue.java @@ -267,7 +267,7 @@ public int compareTo(final AnnotationParameterValue other) { */ @Override public boolean equals(final Object obj) { - if (this == obj) { + if (obj == this) { return true; } else if (!(obj instanceof AnnotationParameterValue)) { return false; diff --git a/src/main/java/io/github/classgraph/ArrayTypeSignature.java b/src/main/java/io/github/classgraph/ArrayTypeSignature.java index aa7f6fa32..bf7fce372 100644 --- a/src/main/java/io/github/classgraph/ArrayTypeSignature.java +++ b/src/main/java/io/github/classgraph/ArrayTypeSignature.java @@ -128,14 +128,13 @@ public int hashCode() { */ @Override public boolean equals(final Object obj) { - if (this == obj) { + if (obj == this) { return true; - } - if (!(obj instanceof ArrayTypeSignature)) { + } else if (!(obj instanceof ArrayTypeSignature)) { return false; } - final ArrayTypeSignature o = (ArrayTypeSignature) obj; - return o.elementTypeSignature.equals(this.elementTypeSignature) && o.numDims == this.numDims; + final ArrayTypeSignature other = (ArrayTypeSignature) obj; + return other.elementTypeSignature.equals(this.elementTypeSignature) && other.numDims == this.numDims; } /* (non-Javadoc) diff --git a/src/main/java/io/github/classgraph/BaseTypeSignature.java b/src/main/java/io/github/classgraph/BaseTypeSignature.java index 1cd4d75cb..2f389f4cd 100644 --- a/src/main/java/io/github/classgraph/BaseTypeSignature.java +++ b/src/main/java/io/github/classgraph/BaseTypeSignature.java @@ -199,7 +199,12 @@ public int hashCode() { */ @Override public boolean equals(final Object obj) { - return obj instanceof BaseTypeSignature && ((BaseTypeSignature) obj).baseType.equals(this.baseType); + if (obj == this) { + return true; + } else if (!(obj instanceof BaseTypeSignature)) { + return false; + } + return ((BaseTypeSignature) obj).baseType.equals(this.baseType); } /* (non-Javadoc) diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index d3e98c600..eb1e78bfc 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -2662,7 +2662,7 @@ public int compareTo(final ClassInfo o) { */ @Override public boolean equals(final Object obj) { - if (this == obj) { + if (obj == this) { return true; } else if (!(obj instanceof ClassInfo)) { return false; diff --git a/src/main/java/io/github/classgraph/ClassInfoList.java b/src/main/java/io/github/classgraph/ClassInfoList.java index 316cd9584..84a3b4bd3 100644 --- a/src/main/java/io/github/classgraph/ClassInfoList.java +++ b/src/main/java/io/github/classgraph/ClassInfoList.java @@ -791,14 +791,13 @@ public void generateGraphVizDotFile(final File file) throws IOException { * @see java.util.ArrayList#equals(java.lang.Object) */ @Override - public boolean equals(final Object o) { - if (this == o) { + public boolean equals(final Object obj) { + if (this == obj) { return true; - } - if (!(o instanceof ClassInfoList)) { + } else if (!(obj instanceof ClassInfoList)) { return false; } - final ClassInfoList other = (ClassInfoList) o; + final ClassInfoList other = (ClassInfoList) obj; if ((directlyRelatedClasses == null) != (other.directlyRelatedClasses == null)) { return false; } diff --git a/src/main/java/io/github/classgraph/ClassRefTypeSignature.java b/src/main/java/io/github/classgraph/ClassRefTypeSignature.java index b75b29b2b..e89c28e11 100644 --- a/src/main/java/io/github/classgraph/ClassRefTypeSignature.java +++ b/src/main/java/io/github/classgraph/ClassRefTypeSignature.java @@ -246,10 +246,9 @@ public int hashCode() { */ @Override public boolean equals(final Object obj) { - if (this == obj) { + if (obj == this) { return true; - } - if (!(obj instanceof ClassRefTypeSignature)) { + } else if (!(obj instanceof ClassRefTypeSignature)) { return false; } final ClassRefTypeSignature o = (ClassRefTypeSignature) obj; diff --git a/src/main/java/io/github/classgraph/ClassTypeSignature.java b/src/main/java/io/github/classgraph/ClassTypeSignature.java index 9ce063745..454a3f967 100644 --- a/src/main/java/io/github/classgraph/ClassTypeSignature.java +++ b/src/main/java/io/github/classgraph/ClassTypeSignature.java @@ -221,10 +221,9 @@ public int hashCode() { */ @Override public boolean equals(final Object obj) { - if (this == obj) { + if (obj == this) { return true; - } - if (!(obj instanceof ClassTypeSignature)) { + } else if (!(obj instanceof ClassTypeSignature)) { return false; } final ClassTypeSignature o = (ClassTypeSignature) obj; diff --git a/src/main/java/io/github/classgraph/ClasspathElementDir.java b/src/main/java/io/github/classgraph/ClasspathElementDir.java index 210ee030c..eba1ae6f0 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementDir.java @@ -499,13 +499,13 @@ public String toString() { * @see java.lang.Object#equals(java.lang.Object) */ @Override - public boolean equals(final Object o) { - if (o == this) { + public boolean equals(final Object obj) { + if (obj == this) { return true; - } else if (!(o instanceof ClasspathElementDir)) { + } else if (!(obj instanceof ClasspathElementDir)) { return false; } - final ClasspathElementDir other = (ClasspathElementDir) o; + final ClasspathElementDir other = (ClasspathElementDir) obj; return this.classpathEltDir.equals(other.classpathEltDir); } diff --git a/src/main/java/io/github/classgraph/ClasspathElementModule.java b/src/main/java/io/github/classgraph/ClasspathElementModule.java index 65830de8f..ae5adf781 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementModule.java +++ b/src/main/java/io/github/classgraph/ClasspathElementModule.java @@ -410,13 +410,13 @@ public String toString() { * @see java.lang.Object#equals(java.lang.Object) */ @Override - public boolean equals(final Object o) { - if (o == this) { + public boolean equals(final Object obj) { + if (obj == this) { return true; - } else if (!(o instanceof ClasspathElementModule)) { + } else if (!(obj instanceof ClasspathElementModule)) { return false; } - final ClasspathElementModule other = (ClasspathElementModule) o; + final ClasspathElementModule other = (ClasspathElementModule) obj; return this.getModuleNameOrEmpty().equals(other.getModuleNameOrEmpty()); } diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index 17d7a5933..78b0734be 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -532,13 +532,13 @@ public String toString() { * @see java.lang.Object#equals(java.lang.Object) */ @Override - public boolean equals(final Object o) { - if (o == this) { + public boolean equals(final Object obj) { + if (obj == this) { return true; - } else if (!(o instanceof ClasspathElementZip)) { + } else if (!(obj instanceof ClasspathElementZip)) { return false; } - final ClasspathElementZip other = (ClasspathElementZip) o; + final ClasspathElementZip other = (ClasspathElementZip) obj; return this.getZipFilePath().equals(other.getZipFilePath()); } diff --git a/src/main/java/io/github/classgraph/FieldInfo.java b/src/main/java/io/github/classgraph/FieldInfo.java index 94e552778..eb0fef3d4 100644 --- a/src/main/java/io/github/classgraph/FieldInfo.java +++ b/src/main/java/io/github/classgraph/FieldInfo.java @@ -421,7 +421,7 @@ protected void findReferencedClassNames(final Set classNames) { */ @Override public boolean equals(final Object obj) { - if (this == obj) { + if (obj == this) { return true; } else if (!(obj instanceof FieldInfo)) { return false; diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index 6764ebec0..ec3c0c9a9 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -678,7 +678,7 @@ protected void findReferencedClassNames(final Set classNames) { */ @Override public boolean equals(final Object obj) { - if (this == obj) { + if (obj == this) { return true; } else if (!(obj instanceof MethodInfo)) { return false; diff --git a/src/main/java/io/github/classgraph/MethodTypeSignature.java b/src/main/java/io/github/classgraph/MethodTypeSignature.java index 1dfbe9354..276d15412 100644 --- a/src/main/java/io/github/classgraph/MethodTypeSignature.java +++ b/src/main/java/io/github/classgraph/MethodTypeSignature.java @@ -279,7 +279,9 @@ public int hashCode() { */ @Override public boolean equals(final Object obj) { - if (!(obj instanceof MethodTypeSignature)) { + if (obj == this) { + return true; + } else if (!(obj instanceof MethodTypeSignature)) { return false; } final MethodTypeSignature o = (MethodTypeSignature) obj; diff --git a/src/main/java/io/github/classgraph/ModuleInfo.java b/src/main/java/io/github/classgraph/ModuleInfo.java index 351fa2562..1017eb97c 100644 --- a/src/main/java/io/github/classgraph/ModuleInfo.java +++ b/src/main/java/io/github/classgraph/ModuleInfo.java @@ -287,13 +287,13 @@ public int hashCode() { * @see java.lang.Object#equals(java.lang.Object) */ @Override - public boolean equals(final Object o) { - if (this == o) { + public boolean equals(final Object obj) { + if (obj == this) { return true; - } else if (!(o instanceof ModuleInfo)) { + } else if (!(obj instanceof ModuleInfo)) { return false; } - return this.compareTo((ModuleInfo) o) == 0; + return this.compareTo((ModuleInfo) obj) == 0; } /* (non-Javadoc) diff --git a/src/main/java/io/github/classgraph/ModuleRef.java b/src/main/java/io/github/classgraph/ModuleRef.java index 784a5e121..11e04e4b4 100644 --- a/src/main/java/io/github/classgraph/ModuleRef.java +++ b/src/main/java/io/github/classgraph/ModuleRef.java @@ -265,11 +265,13 @@ public ClassLoader getClassLoader() { */ @Override public boolean equals(final Object obj) { - if (!(obj instanceof ModuleRef)) { + if (obj == this) { + return true; + } else if (!(obj instanceof ModuleRef)) { return false; } - final ModuleRef mr = (ModuleRef) obj; - return mr.reference.equals(this.reference) && mr.layer.equals(this.layer); + final ModuleRef modRef = (ModuleRef) obj; + return modRef.reference.equals(this.reference) && modRef.layer.equals(this.layer); } /* (non-Javadoc) diff --git a/src/main/java/io/github/classgraph/PackageInfo.java b/src/main/java/io/github/classgraph/PackageInfo.java index dea0ae9a2..5824035cf 100644 --- a/src/main/java/io/github/classgraph/PackageInfo.java +++ b/src/main/java/io/github/classgraph/PackageInfo.java @@ -308,13 +308,13 @@ public int hashCode() { * @see java.lang.Object#equals(java.lang.Object) */ @Override - public boolean equals(final Object o) { - if (this == o) { + public boolean equals(final Object obj) { + if (obj == this) { return true; - } else if (!(o instanceof PackageInfo)) { + } else if (!(obj instanceof PackageInfo)) { return false; } - return this.name.equals(((PackageInfo) o).name); + return this.name.equals(((PackageInfo) obj).name); } /* (non-Javadoc) diff --git a/src/main/java/io/github/classgraph/Resource.java b/src/main/java/io/github/classgraph/Resource.java index cfbf9580e..66b31dc8d 100644 --- a/src/main/java/io/github/classgraph/Resource.java +++ b/src/main/java/io/github/classgraph/Resource.java @@ -498,7 +498,7 @@ public String toString() { if (toString != null) { return toString; } else { - return toString = getURL().toString(); + return toString = getURI().toString(); } } @@ -515,7 +515,9 @@ public int hashCode() { */ @Override public boolean equals(final Object obj) { - if (!(obj instanceof Resource)) { + if (obj == this) { + return true; + } else if (!(obj instanceof Resource)) { return false; } return this.toString().equals(obj.toString()); diff --git a/src/main/java/io/github/classgraph/TypeArgument.java b/src/main/java/io/github/classgraph/TypeArgument.java index 62c0c2f25..86b2dfefd 100644 --- a/src/main/java/io/github/classgraph/TypeArgument.java +++ b/src/main/java/io/github/classgraph/TypeArgument.java @@ -222,7 +222,9 @@ public int hashCode() { */ @Override public boolean equals(final Object obj) { - if (!(obj instanceof TypeArgument)) { + if (obj == this) { + return true; + } else if (!(obj instanceof TypeArgument)) { return false; } final TypeArgument o = (TypeArgument) obj; diff --git a/src/main/java/io/github/classgraph/TypeParameter.java b/src/main/java/io/github/classgraph/TypeParameter.java index e90e4c39a..5c252b692 100644 --- a/src/main/java/io/github/classgraph/TypeParameter.java +++ b/src/main/java/io/github/classgraph/TypeParameter.java @@ -211,7 +211,9 @@ public int hashCode() { */ @Override public boolean equals(final Object obj) { - if (!(obj instanceof TypeParameter)) { + if (obj == this) { + return true; + } else if (!(obj instanceof TypeParameter)) { return false; } final TypeParameter o = (TypeParameter) obj; diff --git a/src/main/java/io/github/classgraph/TypeVariableSignature.java b/src/main/java/io/github/classgraph/TypeVariableSignature.java index 9b2367b92..55300b02c 100644 --- a/src/main/java/io/github/classgraph/TypeVariableSignature.java +++ b/src/main/java/io/github/classgraph/TypeVariableSignature.java @@ -187,7 +187,9 @@ public int hashCode() { */ @Override public boolean equals(final Object obj) { - if (!(obj instanceof TypeVariableSignature)) { + if (obj == this) { + return true; + } else if (!(obj instanceof TypeVariableSignature)) { return false; } final TypeVariableSignature o = (TypeVariableSignature) obj; 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 4660f9aa0..46ff9aca6 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/FastZipEntry.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/FastZipEntry.java @@ -641,8 +641,7 @@ public int compareTo(final FastZipEntry o) { public boolean equals(final Object obj) { if (this == obj) { return true; - } - if (!(obj instanceof FastZipEntry)) { + } else if (!(obj instanceof FastZipEntry)) { return false; } final FastZipEntry other = (FastZipEntry) obj; 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 9c0315b5c..2a0adaf4a 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/PhysicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/PhysicalZipFile.java @@ -250,10 +250,9 @@ public int hashCode() { */ @Override public boolean equals(final Object obj) { - if (this == obj) { + if (obj == this) { return true; - } - if (!(obj instanceof PhysicalZipFile)) { + } else if (!(obj instanceof PhysicalZipFile)) { return false; } return file.equals(((PhysicalZipFile) obj).file); diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSlice.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSlice.java index d7b665d8d..ba0a4ddac 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSlice.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSlice.java @@ -209,14 +209,13 @@ public int hashCode() { * @see java.lang.Object#equals(java.lang.Object) */ @Override - public boolean equals(final Object o) { - if (this == o) { + public boolean equals(final Object obj) { + if (obj == this) { return true; - } - if (!(o instanceof ZipFileSlice)) { + } else if (!(obj instanceof ZipFileSlice)) { return false; } - final ZipFileSlice other = (ZipFileSlice) o; + final ZipFileSlice other = (ZipFileSlice) obj; return startOffsetWithinPhysicalZipFile == other.startOffsetWithinPhysicalZipFile && len == other.len && this.physicalZipFile.equals(other.physicalZipFile); } 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 e2e12a4d7..26c76fcdb 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/ParameterizedTypeImpl.java +++ b/src/main/java/nonapi/io/github/classgraph/json/ParameterizedTypeImpl.java @@ -102,19 +102,14 @@ public Type getOwnerType() { * @see java.lang.Object#equals(java.lang.Object) */ @Override - public boolean equals(final Object o) { - if (this == o) { + public boolean equals(final Object obj) { + if (obj == this) { return true; - } - if (!(o instanceof ParameterizedType)) { + } else if (!(obj instanceof ParameterizedType)) { return false; } - final ParameterizedType other = (ParameterizedType) o; - - final Type otherOwnerType = other.getOwnerType(); - final Type otherRawType = other.getRawType(); - - return Objects.equals(ownerType, otherOwnerType) && Objects.equals(rawType, otherRawType) + final ParameterizedType other = (ParameterizedType) obj; + return Objects.equals(ownerType, other.getOwnerType()) && Objects.equals(rawType, other.getRawType()) && Arrays.equals(actualTypeArguments, other.getActualTypeArguments()); } diff --git a/src/main/java/nonapi/io/github/classgraph/json/ReferenceEqualityKey.java b/src/main/java/nonapi/io/github/classgraph/json/ReferenceEqualityKey.java index b24509dfd..28d21f714 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/ReferenceEqualityKey.java +++ b/src/main/java/nonapi/io/github/classgraph/json/ReferenceEqualityKey.java @@ -62,8 +62,13 @@ public int hashCode() { * @see java.lang.Object#equals(java.lang.Object) */ @Override - public boolean equals(final Object other) { - return other instanceof ReferenceEqualityKey && wrappedKey == ((ReferenceEqualityKey) other).wrappedKey; + public boolean equals(final Object obj) { + if (obj == this) { + return true; + } else if (!(obj instanceof ReferenceEqualityKey)) { + return false; + } + return wrappedKey == ((ReferenceEqualityKey) obj).wrappedKey; } /* (non-Javadoc) From 67c3a7d878eef9980d137b60dc06ae46eca067b0 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 5 Mar 2019 02:01:14 -0700 Subject: [PATCH 0012/1778] [maven-release-plugin] prepare release classgraph-4.8.12 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 544b867fd..d76d019de 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.12-SNAPSHOT + 4.8.12 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.12 From fbb322cdf2f3afe48dc241d22eee66716ef243cf Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 5 Mar 2019 02:01:21 -0700 Subject: [PATCH 0013/1778] [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 d76d019de..2b7e42bac 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.12 + 4.8.13-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.12 + HEAD From 48fa5d17fbeb46a00b6a31ac8e7478febcc9b3d5 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 5 Mar 2019 02:06:36 -0700 Subject: [PATCH 0014/1778] Optimization for compareTo() method --- .../java/io/github/classgraph/AnnotationParameterValue.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/io/github/classgraph/AnnotationParameterValue.java b/src/main/java/io/github/classgraph/AnnotationParameterValue.java index e36c22684..1d7172415 100644 --- a/src/main/java/io/github/classgraph/AnnotationParameterValue.java +++ b/src/main/java/io/github/classgraph/AnnotationParameterValue.java @@ -250,10 +250,16 @@ private String toStringParamValueOnly() { */ @Override public int compareTo(final AnnotationParameterValue other) { + if (other == this) { + return 0; + } final int diff = name.compareTo(other.getName()); if (diff != 0) { return diff; } + if (value.equals(other.value)) { + return 0; + } // Use toString() order (which can be slow) as a last-ditch effort -- only happens // if the annotation has multiple parameters of the same name but different value. final Object p0 = getValue(); From 749f2816f3613dd622c9786bca0110c877897d12 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 5 Mar 2019 02:17:57 -0700 Subject: [PATCH 0015/1778] Provide another fallback method for getting stacktrace --- .../classgraph/classpath/CallStackReader.java | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 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 204fbf695..4931a9c90 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/CallStackReader.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/CallStackReader.java @@ -166,24 +166,28 @@ public Class[] run() { // As a fallback, use getStackTrace() to try to get the call stack if (stack == null) { - try { - throw new Exception(); - } catch (final Exception e) { - final List> classes = new ArrayList<>(); - for (final StackTraceElement elt : e.getStackTrace()) { - try { - classes.add(Class.forName(elt.getClassName())); - } catch (final ClassNotFoundException | LinkageError ignored) { - // Ignored - } + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + if (stackTrace == null || stackTrace.length == 0) { + try { + throw new Exception(); + } catch (final Exception e) { + stackTrace = e.getStackTrace(); } - if (!classes.isEmpty()) { - stack = classes.toArray(new Class[0]); - } else { - // Last-ditch effort -- include just this class in the call stack - stack = new Class[] { CallStackReader.class }; + } + final List> classes = new ArrayList<>(); + for (final StackTraceElement elt : stackTrace) { + try { + classes.add(Class.forName(elt.getClassName())); + } catch (final ClassNotFoundException | LinkageError ignored) { + // Ignored } } + if (!classes.isEmpty()) { + stack = classes.toArray(new Class[0]); + } else { + // Last-ditch effort -- include just this class in the call stack + stack = new Class[] { CallStackReader.class }; + } } return stack; } From 05bc13cc3f7644f03a37ed3c549f23d99949ceee Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 5 Mar 2019 02:19:08 -0700 Subject: [PATCH 0016/1778] Increase robustness of fallback --- .../io/github/classgraph/classpath/CallStackReader.java | 4 ++-- 1 file changed, 2 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 4931a9c90..91898281b 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/CallStackReader.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/CallStackReader.java @@ -155,7 +155,7 @@ public Class[] run() { } // For JRE 7 and 8, use SecurityManager to get call stack - if (stack == null) { + if (stack == null || stack.length == 0) { stack = AccessController.doPrivileged(new PrivilegedAction[]>() { @Override public Class[] run() { @@ -165,7 +165,7 @@ public Class[] run() { } // As a fallback, use getStackTrace() to try to get the call stack - if (stack == null) { + if (stack == null || stack.length == 0) { StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); if (stackTrace == null || stackTrace.length == 0) { try { From 9d088fcc8a4e72163c01b9b727ea0d033121a78c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 5 Mar 2019 02:52:09 -0700 Subject: [PATCH 0017/1778] Change param name --- .../classgraph/classpath/CallStackReader.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 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 91898281b..0eb2f0cf1 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/CallStackReader.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/CallStackReader.java @@ -142,11 +142,11 @@ private static Class[] getCallStackViaSecurityManager(final LogNode log) { */ static Class[] getClassContext(final LogNode log) { // For JRE 9+, use StackWalker to get call stack - Class[] stack = null; + Class[] stackClasses = null; if (VersionFinder.JAVA_MAJOR_VERSION >= 9) { // Invoke with doPrivileged -- see: // http://mail.openjdk.java.net/pipermail/jigsaw-dev/2018-October/013974.html - stack = AccessController.doPrivileged(new PrivilegedAction[]>() { + stackClasses = AccessController.doPrivileged(new PrivilegedAction[]>() { @Override public Class[] run() { return getCallStackViaStackWalker(); @@ -155,8 +155,8 @@ public Class[] run() { } // For JRE 7 and 8, use SecurityManager to get call stack - if (stack == null || stack.length == 0) { - stack = AccessController.doPrivileged(new PrivilegedAction[]>() { + if (stackClasses == null || stackClasses.length == 0) { + stackClasses = AccessController.doPrivileged(new PrivilegedAction[]>() { @Override public Class[] run() { return getCallStackViaSecurityManager(log); @@ -165,7 +165,7 @@ public Class[] run() { } // As a fallback, use getStackTrace() to try to get the call stack - if (stack == null || stack.length == 0) { + if (stackClasses == null || stackClasses.length == 0) { StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); if (stackTrace == null || stackTrace.length == 0) { try { @@ -174,21 +174,21 @@ public Class[] run() { stackTrace = e.getStackTrace(); } } - final List> classes = new ArrayList<>(); + final List> stackClassesList = new ArrayList<>(); for (final StackTraceElement elt : stackTrace) { try { - classes.add(Class.forName(elt.getClassName())); + stackClassesList.add(Class.forName(elt.getClassName())); } catch (final ClassNotFoundException | LinkageError ignored) { // Ignored } } - if (!classes.isEmpty()) { - stack = classes.toArray(new Class[0]); + if (!stackClassesList.isEmpty()) { + stackClasses = stackClassesList.toArray(new Class[0]); } else { // Last-ditch effort -- include just this class in the call stack - stack = new Class[] { CallStackReader.class }; + stackClasses = new Class[] { CallStackReader.class }; } } - return stack; + return stackClasses; } } From 724a2a4eaaf3ecbbf0029b97ec9b37142a9dc6d3 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 5 Mar 2019 02:52:28 -0700 Subject: [PATCH 0018/1778] Remove unused param --- .../io/github/classgraph/ClasspathElementDir.java | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementDir.java b/src/main/java/io/github/classgraph/ClasspathElementDir.java index eba1ae6f0..70f724bcb 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementDir.java @@ -144,16 +144,13 @@ void open(final WorkQueue workQueue, final LogNode log) /** * Create a new {@link Resource} object for a resource or classfile discovered while scanning paths. * - * @param classpathEltDir - * the classpath element directory * @param relativePath * the relative path * @param classpathResourceFile * the classpath resource file * @return the resource */ - private Resource newResource(final File classpathEltDir, final String relativePath, - final File classpathResourceFile) { + private Resource newResource(final String relativePath, final File classpathResourceFile) { return new Resource(this, classpathResourceFile.length()) { /** The random access file. */ private RandomAccessFile randomAccessFile; @@ -287,9 +284,7 @@ public synchronized void close() { @Override Resource getResource(final String relativePath) { final File resourceFile = new File(classpathEltDir, relativePath); - return resourceFile.canRead() && resourceFile.isFile() - ? newResource(classpathEltDir, relativePath, resourceFile) - : null; + return resourceFile.canRead() && resourceFile.isFile() ? newResource(relativePath, resourceFile) : null; } /** @@ -388,7 +383,7 @@ private void scanDirRecursively(final File dir, final LogNode log) { || (parentMatchStatus == ScanSpecPathMatch.AT_WHITELISTED_CLASS_PACKAGE && scanSpec.classfileIsSpecificallyWhitelisted(fileInDirRelativePath))) { // Resource is whitelisted - final Resource resource = newResource(classpathEltDir, fileInDirRelativePath, fileInDir); + final Resource resource = newResource(fileInDirRelativePath, fileInDir); addWhitelistedResource(resource, parentMatchStatus, subLog); // Save last modified time @@ -404,7 +399,7 @@ private void scanDirRecursively(final File dir, final LogNode log) { // Always check for module descriptor in package root, even if package root isn't in whitelist for (final File fileInDir : filesInDir) { if (fileInDir.getName().equals("module-info.class") && fileInDir.isFile()) { - final Resource resource = newResource(classpathEltDir, "module-info.class", fileInDir); + final Resource resource = newResource("module-info.class", fileInDir); addWhitelistedResource(resource, parentMatchStatus, subLog); fileToLastModified.put(fileInDir, fileInDir.lastModified()); } From 58f375b4b946c2b001be1ee18913675dbe097009 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 5 Mar 2019 15:46:18 -0700 Subject: [PATCH 0019/1778] Improve Javadoc --- src/main/java/io/github/classgraph/ClassInfo.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index eb1e78bfc..5c433cb48 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -901,7 +901,9 @@ public String getName() { } /** - * Get simple name from fully-qualified class name. + * Get simple name from fully-qualified class name. Returns everything after the last '.' in the class name, or + * the whole string if the class is in the root package. (Note that this is not the same as the result of + * {@link Class#getSimpleName()}, which returns "" for anonymous classes.) * * @return The simple name of the class. */ @@ -910,7 +912,9 @@ static String getSimpleName(final String className) { } /** - * Get the simple name of the class. + * Get the simple name of the class. Returns everything after the last '.' in the class name, or the whole + * string if the class is in the root package. (Note that this is not the same as the result of + * {@link Class#getSimpleName()}, which returns "" for anonymous classes.) * * @return The simple name of the class. */ From a73a7576fd174d0c3316004442941237117a74ff Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 8 Mar 2019 15:17:34 -0700 Subject: [PATCH 0020/1778] Fix method name and comments --- .../classgraph/issues/issue318/Issue318.java | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) 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 324cc4584..3fdd76af2 100644 --- a/src/test/java/io/github/classgraph/issues/issue318/Issue318.java +++ b/src/test/java/io/github/classgraph/issues/issue318/Issue318.java @@ -14,7 +14,7 @@ import io.github.classgraph.ScanResult; /** - * The Class Issue314. + * Unit test. */ public class Issue318 { @@ -41,20 +41,6 @@ public class Issue318 { MyAnn[] value(); } - // /** - // * The Interface MyAnnRepeating. - // */ - // @Retention(RetentionPolicy.RUNTIME) - // @Target({ ElementType.TYPE }) - // @interface MyAnnRepeating2 { - // /** - // * Value. - // * - // * @return the my ann[] - // */ - // MyAnn[] value(); - // } - /** * The Class With0MyAnn. */ From c8cfdc5997a87e08070967afd0b6a3049b773034 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 8 Mar 2019 15:20:27 -0700 Subject: [PATCH 0021/1778] Add missing inter class dependencies for intermediate types (#329) --- .../java/io/github/classgraph/ClassInfo.java | 3 ++ .../classgraph/issues/issue329/Issue329.java | 37 +++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 src/test/java/io/github/classgraph/issues/issue329/Issue329.java diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index 5c433cb48..fff3b966e 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -2600,6 +2600,9 @@ void addReferencedClassNames(final Set refdClassNames) { */ @Override protected void findReferencedClassNames(final Set referencedClassNames) { + if (this.referencedClassNames != null) { + referencedClassNames.addAll(this.referencedClassNames); + } getMethodInfo().findReferencedClassNames(referencedClassNames); getFieldInfo().findReferencedClassNames(referencedClassNames); getAnnotationInfo().findReferencedClassNames(referencedClassNames); diff --git a/src/test/java/io/github/classgraph/issues/issue329/Issue329.java b/src/test/java/io/github/classgraph/issues/issue329/Issue329.java new file mode 100644 index 000000000..2f74b58e5 --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue329/Issue329.java @@ -0,0 +1,37 @@ +package io.github.classgraph.issues.issue329; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Test; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ClassInfo; +import io.github.classgraph.ScanResult; + +/** + * Unit test. + */ +public class Issue329 { + /** The Class Foo. */ + public class Foo { + /** Constructor. */ + public Foo() { + new Bar(); + } + } + + /** The Class Bar. */ + public class Bar { + } + + /** Test. */ + @Test + public void test() { + try (ScanResult scanResult = new ClassGraph().enableAllInfo().enableInterClassDependencies().verbose() + .enableExternalClasses().whitelistClasses(Foo.class.getName()).scan()) { + final ClassInfo classInfo = scanResult.getClassInfo(Foo.class.getName()); + assertThat(classInfo.getClassDependencies().getNames()) + .containsExactlyInAnyOrder(Issue329.class.getName(), Bar.class.getName()); + } + } +} From 031b037fb7fea9ba8a8fea8a3825a7737e8141fa Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 8 Mar 2019 15:22:53 -0700 Subject: [PATCH 0022/1778] [maven-release-plugin] prepare release classgraph-4.8.13 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 2b7e42bac..0775e72b9 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.13-SNAPSHOT + 4.8.13 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.13 From 1317cf2a8768f79cb0d205f2d865f8f1c108e087 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 8 Mar 2019 15:23:00 -0700 Subject: [PATCH 0023/1778] [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 0775e72b9..93bcff311 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.13 + 4.8.14-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.13 + HEAD From 2415c8eb9374745a7886696eeceb6e52cad22836 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 8 Mar 2019 18:29:10 -0700 Subject: [PATCH 0024/1778] Remove unnecessary check --- src/main/java/io/github/classgraph/Classfile.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index 24ca349b4..7290b5ad5 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -917,7 +917,7 @@ private void readConstantPoolEntries() throws IOException { case 7: // Class reference (format is e.g. "java/lang/String") // Forward or backward indirect reference to a modified UTF8 entry indirectStringRefs[i] = inputStreamOrByteBuffer.readUnsignedShort(); - if (scanSpec.enableInterClassDependencies && entryTag[i] == 7) { + if (scanSpec.enableInterClassDependencies) { // If this is a class ref, and inter-class dependencies are enabled, record the dependency classNameCpIdxs.add(indirectStringRefs[i]); } From 4c2221e49822f6307e7e082be1a82f32b61d14f6 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 8 Mar 2019 18:53:11 -0700 Subject: [PATCH 0025/1778] Remove .verbose() --- .../java/io/github/classgraph/issues/issue329/Issue329.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/io/github/classgraph/issues/issue329/Issue329.java b/src/test/java/io/github/classgraph/issues/issue329/Issue329.java index 2f74b58e5..26e179244 100644 --- a/src/test/java/io/github/classgraph/issues/issue329/Issue329.java +++ b/src/test/java/io/github/classgraph/issues/issue329/Issue329.java @@ -27,7 +27,7 @@ public class Bar { /** Test. */ @Test public void test() { - try (ScanResult scanResult = new ClassGraph().enableAllInfo().enableInterClassDependencies().verbose() + try (ScanResult scanResult = new ClassGraph().enableAllInfo().enableInterClassDependencies() .enableExternalClasses().whitelistClasses(Foo.class.getName()).scan()) { final ClassInfo classInfo = scanResult.getClassInfo(Foo.class.getName()); assertThat(classInfo.getClassDependencies().getNames()) From 40da4e894d20ba0203b140306d18df6c43deca72 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 10 Mar 2019 04:14:04 -0600 Subject: [PATCH 0026/1778] Add `getContentAsString()` convenience method. --- .../java/io/github/classgraph/Resource.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/main/java/io/github/classgraph/Resource.java b/src/main/java/io/github/classgraph/Resource.java index 66b31dc8d..cb735ca83 100644 --- a/src/main/java/io/github/classgraph/Resource.java +++ b/src/main/java/io/github/classgraph/Resource.java @@ -37,6 +37,7 @@ import java.net.URISyntaxException; import java.net.URL; import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; import java.util.zip.ZipEntry; import nonapi.io.github.classgraph.utils.FileUtils; @@ -409,6 +410,22 @@ public ModuleRef getModuleRef() { : null; } + /** + * Convenience method to get the content of this {@link Resource} as a String. Assumes UTF8 encoding. (Calls + * {@link #close()} after completion.) + * + * @return the content of this {@link Resource} as a String. + * @throws IOException + * If an I/O exception occurred. + */ + public String getContentAsString() throws IOException { + try { + return new String(load(), StandardCharsets.UTF_8); + } finally { + close(); + } + } + // ------------------------------------------------------------------------------------------------------------- /** From 035c7b0a9b18dee3656e5f128cda50ce141b234c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 10 Mar 2019 18:02:24 -0600 Subject: [PATCH 0027/1778] [maven-release-plugin] prepare release classgraph-4.8.14 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 93bcff311..20fce028b 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.14-SNAPSHOT + 4.8.14 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.14 From 8baa703be02890ee70ec04086d8ca29eaa6c5fad Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 10 Mar 2019 18:02:30 -0600 Subject: [PATCH 0028/1778] [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 20fce028b..0ee51300e 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.14 + 4.8.15-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.14 + HEAD From d3fe41f00ec4083ccacb07235e60565da723ca95 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 15 Mar 2019 14:34:17 -0600 Subject: [PATCH 0029/1778] Call ScanResult#close() after a ScanResultProcessor finishes --- src/main/java/io/github/classgraph/Scanner.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 69e815763..fa813315e 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -1002,7 +1002,11 @@ public ScanResult call() throws InterruptedException, CancellationException, Exe // Call the ScanResultProcessor, if one was provided if (scanResultProcessor != null) { - scanResultProcessor.processScanResult(scanResult); + try { + scanResultProcessor.processScanResult(scanResult); + } finally { + scanResult.close(); + } } } catch (final InterruptedException e) { From bd4c84bda97cf81c3bef56d833eac01fb69b4a13 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 15 Mar 2019 14:35:50 -0600 Subject: [PATCH 0030/1778] Add instructions to Javadoc about closing the `ScanResult` (#331) --- src/main/java/io/github/classgraph/ClassGraph.java | 14 ++++++++++---- src/main/java/io/github/classgraph/ScanResult.java | 5 ++++- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index 0cba62060..3ac0ee338 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -1107,7 +1107,9 @@ public void run() { } /** - * Asynchronously scans the classpath for matching files, returning a {@code Future}. + * Asynchronously scans the classpath for matching files, returning a {@code Future}. You should + * assign the wrapped {@link ScanResult} in a try-with-resources statement, or manually close it when you are + * finished with it. * * @param executorService * A custom {@link ExecutorService} to use for scheduling worker tasks. @@ -1135,7 +1137,8 @@ public ScanResult call() throws Exception { /** * Scans the classpath using the requested {@link ExecutorService} and the requested degree of parallelism, - * blocking until the scan is complete. + * blocking until the scan is complete. You should assign the returned {@link ScanResult} in a + * try-with-resources statement, or manually close it when you are finished with it. * * @param executorService * A custom {@link ExecutorService} to use for scheduling worker tasks. This {@link ExecutorService} @@ -1182,7 +1185,9 @@ public ScanResult scan(final ExecutorService executorService, final int numParal } /** - * Scans the classpath with the requested number of threads, blocking until the scan is complete. + * Scans the classpath with the requested number of threads, blocking until the scan is complete. You should + * assign the returned {@link ScanResult} in a try-with-resources statement, or manually close it when you are + * finished with it. * * @param numThreads * The number of worker threads to start up. @@ -1197,7 +1202,8 @@ public ScanResult scan(final int numThreads) { } /** - * Scans the classpath, blocking until the scan is complete. + * Scans the classpath, blocking until the scan is complete. You should assign the returned {@link ScanResult} + * in a try-with-resources statement, or manually close it when you are finished with it. * * @return a {@link ScanResult} object representing the result of the scan. * @throws ClassGraphException diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index e027976db..0fcf62363 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -56,7 +56,10 @@ import nonapi.io.github.classgraph.utils.JarUtils; import nonapi.io.github.classgraph.utils.LogNode; -/** The result of a scan. */ +/** + * 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 { /** The order of raw classpath elements. */ private List rawClasspathEltOrderStrs; From a6bd353be414014884f81776340c039c478d52bc Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 18 Mar 2019 14:29:31 -0600 Subject: [PATCH 0031/1778] Fix rt.jar not being detected on Windows (#332) --- .../nonapi/io/github/classgraph/classpath/SystemJarFinder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 c86c5b5a0..4aa89f4bd 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/SystemJarFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/SystemJarFinder.java @@ -68,7 +68,7 @@ private static boolean addJREPath(final File dir) { final String filePath = file.getPath(); if (filePath.endsWith(".jar")) { final String jarPathResolved = FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, filePath); - if (filePath.endsWith("/rt.jar")) { + if (jarPathResolved.endsWith("/rt.jar")) { RT_JARS.add(jarPathResolved); } else { JRE_LIB_OR_EXT_JARS.add(jarPathResolved); From d9bca5f864adff71729855728020cc120630d646 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 18 Mar 2019 14:29:41 -0600 Subject: [PATCH 0032/1778] Improve logging --- .../classgraph/classpath/ClasspathFinder.java | 36 +++++++++---------- 1 file changed, 16 insertions(+), 20 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 f758c5a78..26d1384cf 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java @@ -236,24 +236,7 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { // If system jars are not blacklisted, add JRE rt.jar to the beginning of the classpath final String jreRtJar = SystemJarFinder.getJreRtJarPath(); final boolean scanAllLibOrExtJars = !scanSpec.libOrExtJarWhiteBlackList.whitelistAndBlacklistAreEmpty(); - final Set libOrExtJars = SystemJarFinder.getJreLibOrExtJars(); - if (classpathFinderLog != null && (jreRtJar != null || !libOrExtJars.isEmpty())) { - final LogNode systemJarsLog = classpathFinderLog.log("System jars:"); - if (jreRtJar != null) { - systemJarsLog.log( - (scanSpec.enableSystemJarsAndModules ? "" : "Scanning disabled for rt.jar: ") + jreRtJar); - } - // If the lib/ext jar whitelist is non-empty, then zero or more lib/ext jars were whitelisted - // (calling ClassGraph#whitelistLibOrExtJars() with no parameters manually whitelists all - // jars found in lib/ext dirs, by iterating through all jarfiles in lib/ext dirs and adding - // them to the whitelist). - for (final String libOrExtJarPath : libOrExtJars) { - systemJarsLog.log((scanAllLibOrExtJars || scanSpec.libOrExtJarWhiteBlackList - .isSpecificallyWhitelistedAndNotBlacklisted(libOrExtJarPath) ? "" - : "Scanning disabled for lib or ext jar: ") - + libOrExtJarPath); - } - } + final LogNode systemJarsLog = classpathFinderLog == null ? null : classpathFinderLog.log("System jars:"); classLoaderAndModuleFinder = new ClassLoaderAndModuleFinder(scanSpec, classpathFinderLog); @@ -280,13 +263,26 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { } } else { // Add rt.jar and/or lib/ext jars to beginning of classpath, if enabled - if (jreRtJar != null && scanSpec.enableSystemJarsAndModules) { - classpathOrder.addSystemClasspathEntry(jreRtJar, defaultClassLoader); + if (jreRtJar != null) { + if (scanSpec.enableSystemJarsAndModules) { + classpathOrder.addSystemClasspathEntry(jreRtJar, defaultClassLoader); + if (systemJarsLog != null) { + systemJarsLog.log("Found rt.jar: " + jreRtJar); + } + } else if (systemJarsLog != null) { + systemJarsLog.log((scanSpec.enableSystemJarsAndModules ? "" : "Scanning disabled for rt.jar: ") + + jreRtJar); + } } for (final String libOrExtJarPath : SystemJarFinder.getJreLibOrExtJars()) { if (scanAllLibOrExtJars || scanSpec.libOrExtJarWhiteBlackList .isSpecificallyWhitelistedAndNotBlacklisted(libOrExtJarPath)) { classpathOrder.addSystemClasspathEntry(libOrExtJarPath, defaultClassLoader); + if (systemJarsLog != null) { + systemJarsLog.log("Found lib or ext jar: " + libOrExtJarPath); + } + } else if (systemJarsLog != null) { + systemJarsLog.log("Scanning disabled for lib or ext jar: " + libOrExtJarPath); } } From c58301b0b9866234049904e23cce8350b83e5f9a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 18 Mar 2019 14:30:39 -0600 Subject: [PATCH 0033/1778] [maven-release-plugin] prepare release classgraph-4.8.15 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 0ee51300e..0f269b4cd 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.15-SNAPSHOT + 4.8.15 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.15 From 3fde771809042c81a3c5a6ac3245a562575ca075 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 18 Mar 2019 14:31:40 -0600 Subject: [PATCH 0034/1778] [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 0f269b4cd..22fbadf7f 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.15 + 4.8.16-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.15 + HEAD From d45f98e6f676948bcba1338811cd752b3a5d86fe Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 19 Mar 2019 05:58:19 -0600 Subject: [PATCH 0035/1778] Fix Class-Path path resolution (#333) --- .../io/github/classgraph/ClasspathElementZip.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index 78b0734be..8a3eb9eb4 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -201,13 +201,19 @@ void open(final WorkQueue workQueue, final LogNode log) // Class-Path entries in the manifest file are resolved relative to the dir that // the manifest's jarfile is contained in -- get parent dir of logical zipfile final String path = logicalZipFile.getPath(); - final int lastSlashIdx = path.lastIndexOf('/'); - final String parentPathPrefix = lastSlashIdx < 0 ? "" : path.substring(0, lastSlashIdx + 1); + final int lastPlingIdx = path.lastIndexOf('!'); + final int lastSlashIdx = Math.max(path.lastIndexOf('/'), lastPlingIdx); + // Get path of parent jarfile, if this is a nested jar. If there is a Class-Path entry in a nested + // jar, probably the intent is that the Class-Path path is relative to the root of the parent jar + // (although this is an unlikely scenario). + final String parentZipPrefix = lastPlingIdx < 0 ? "" : path.substring(0, lastPlingIdx + 1); + final String parentPathPrefix = lastSlashIdx < 0 ? "" + : path.substring(lastPlingIdx + 1, lastSlashIdx + 1); for (final String childClassPathEltPath : logicalZipFile.classPathManifestEntryValue.split(" ")) { if (!childClassPathEltPath.isEmpty()) { // Resolve Class-Path entry relative to containing dir - final String childClassPathEltPathResolved = FastPathResolver - .resolve(parentPathPrefix + "/" + childClassPathEltPath); + final String childClassPathEltPathResolved = parentZipPrefix + + FastPathResolver.resolve(parentPathPrefix, childClassPathEltPath); // Only add child classpath elements once if (!childClassPathEltPathResolved.equals(rawPath)) { // Schedule child classpath element for scanning From 549c36a189b3c58bf16cf5c5d217b4a6a9946b96 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 19 Mar 2019 05:59:13 -0600 Subject: [PATCH 0036/1778] [maven-release-plugin] prepare release classgraph-4.8.16 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 22fbadf7f..161f21c15 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.16-SNAPSHOT + 4.8.16 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.16 From 8ff7055be630d4efeff19f1a0d48496633fc75f9 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 19 Mar 2019 05:59:20 -0600 Subject: [PATCH 0037/1778] [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 161f21c15..695840bb6 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.16 + 4.8.17-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.16 + HEAD From 48890219be4227639d6ef159fe64d5b9fc197416 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 19 Mar 2019 06:08:41 -0600 Subject: [PATCH 0038/1778] Robustness fixes for Class-Path path resolution --- .../io/github/classgraph/ClasspathElementZip.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index 8a3eb9eb4..9c015fbba 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -207,13 +207,20 @@ void open(final WorkQueue workQueue, final LogNode log) // jar, probably the intent is that the Class-Path path is relative to the root of the parent jar // (although this is an unlikely scenario). final String parentZipPrefix = lastPlingIdx < 0 ? "" : path.substring(0, lastPlingIdx + 1); - final String parentPathPrefix = lastSlashIdx < 0 ? "" - : path.substring(lastPlingIdx + 1, lastSlashIdx + 1); + String parentPathPrefix = lastSlashIdx < 0 ? "" : path.substring(lastPlingIdx + 1, lastSlashIdx + 1); + if (parentZipPrefix.isEmpty() && parentPathPrefix.isEmpty()) { + parentPathPrefix = FileUtils.CURR_DIR_PATH; + } for (final String childClassPathEltPath : logicalZipFile.classPathManifestEntryValue.split(" ")) { if (!childClassPathEltPath.isEmpty()) { // Resolve Class-Path entry relative to containing dir - final String childClassPathEltPathResolved = parentZipPrefix - + FastPathResolver.resolve(parentPathPrefix, childClassPathEltPath); + String childClassPathEltPathResolved = FastPathResolver.resolve(parentPathPrefix, + childClassPathEltPath); + if (!parentZipPrefix.isEmpty()) { + childClassPathEltPathResolved = parentZipPrefix + + (childClassPathEltPathResolved.startsWith("/") ? childClassPathEltPathResolved + : "/" + childClassPathEltPathResolved); + } // Only add child classpath elements once if (!childClassPathEltPathResolved.equals(rawPath)) { // Schedule child classpath element for scanning From 613d9d50f91cfbbd3d07a0b9b65c2ee12c3f94c7 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 20 Mar 2019 04:21:37 -0600 Subject: [PATCH 0039/1778] Source > Format --- LICENSE | 2 +- src/main/java/io/github/classgraph/AnnotationClassRef.java | 1 - src/main/java/io/github/classgraph/AnnotationEnumValue.java | 1 - src/main/java/io/github/classgraph/AnnotationInfo.java | 1 - .../java/io/github/classgraph/AnnotationParameterValue.java | 1 - .../io/github/classgraph/AnnotationParameterValueList.java | 1 - src/main/java/io/github/classgraph/ClassGraph.java | 1 - src/main/java/io/github/classgraph/ClassInfoList.java | 1 - src/main/java/io/github/classgraph/FieldInfo.java | 1 - src/main/java/io/github/classgraph/FieldInfoList.java | 1 - src/main/java/io/github/classgraph/MethodInfoList.java | 1 - src/main/java/io/github/classgraph/MethodParameterInfo.java | 1 - src/main/java/io/github/classgraph/ModuleInfoList.java | 1 - src/main/java/io/github/classgraph/ModuleReaderProxy.java | 1 - src/main/java/io/github/classgraph/PackageInfoList.java | 1 - .../classgraph/classpath/ClassLoaderAndModuleFinder.java | 1 - .../io/github/classgraph/classpath/ClasspathFinder.java | 1 - .../io/github/classgraph/classpath/ClasspathOrder.java | 1 - .../nonapi/io/github/classgraph/json/JSONDeserializer.java | 1 - .../nonapi/io/github/classgraph/recycler/RecycleOnClose.java | 1 - src/test/java/ClassGraphGraphVizGenerator.java | 3 +-- src/test/java/ClassInDefaultPackage.java | 2 +- src/test/java/DefaultPackageTest.java | 3 +-- src/test/java/DisableRecursiveScanningTest.java | 3 +-- src/test/java/com/xyz/GenerateClassGraphFigDotFile.java | 3 +-- src/test/java/com/xyz/MetaAnnotationTest.java | 3 +-- src/test/java/com/xyz/ScanEverything.java | 3 +-- src/test/java/com/xyz/fig/Figure.java | 3 +-- src/test/java/com/xyz/fig/SceneGraph.java | 3 +-- src/test/java/com/xyz/fig/shape/Circle.java | 3 +-- src/test/java/com/xyz/fig/shape/Diamond.java | 3 +-- src/test/java/com/xyz/fig/shape/ShapeImpl.java | 2 +- src/test/java/com/xyz/fig/shape/Square.java | 3 +-- src/test/java/com/xyz/fig/shape/Triangle.java | 3 +-- src/test/java/com/xyz/meta/A.java | 2 +- src/test/java/com/xyz/meta/B.java | 2 +- src/test/java/com/xyz/meta/C.java | 2 +- .../io/github/classgraph/features/AnnotationEquality.java | 2 +- .../features/AnnotationParamWithPrimitiveTypedArray.java | 3 +-- .../io/github/classgraph/features/DeclaredVsNonDeclared.java | 3 +-- .../classgraph/features/MethodParameterAnnotations.java | 2 +- .../java/io/github/classgraph/features/MultiReleaseJar.java | 3 +-- .../classgraph/issues/GenericInnerClassTypedField.java | 3 +-- src/test/java/io/github/classgraph/issues/IssuesTest.java | 3 +-- .../io/github/classgraph/issues/ResolveTypeVariable.java | 3 +-- .../classgraph/issues/TestGetUniqueClasspathElements.java | 3 +-- .../io/github/classgraph/issues/issue100/Issue100Test.java | 3 +-- .../io/github/classgraph/issues/issue101/AnnotatedClass.java | 2 +- .../issues/issue101/ImplementsAnnotatedInterface.java | 2 +- .../issues/issue101/ImplementsNonAnnotatedSubinterface.java | 2 +- .../io/github/classgraph/issues/issue101/Issue101Test.java | 3 +-- .../classgraph/issues/issue101/NonAnnotatedSubclass.java | 2 +- .../io/github/classgraph/issues/issue107/Issue107Test.java | 3 +-- .../io/github/classgraph/issues/issue128/Issue128Test.java | 3 +-- .../io/github/classgraph/issues/issue140/Issue140Test.java | 3 +-- .../io/github/classgraph/issues/issue141/Issue141Test.java | 2 +- .../io/github/classgraph/issues/issue146/Issue146Test.java | 3 +-- .../io/github/classgraph/issues/issue148/Issue148Test.java | 3 +-- src/test/java/io/github/classgraph/issues/issue148/O1.java | 5 +---- src/test/java/io/github/classgraph/issues/issue148/O2.java | 3 +-- .../io/github/classgraph/issues/issue151/Issue151Test.java | 3 +-- .../io/github/classgraph/issues/issue152/Issue152Test.java | 3 +-- .../io/github/classgraph/issues/issue153/Issue153Test.java | 3 +-- .../io/github/classgraph/issues/issue166/Issue166Test.java | 3 +-- .../io/github/classgraph/issues/issue167/Issue167Test.java | 3 +-- .../java/io/github/classgraph/issues/issue167/a/TestA.java | 2 +- .../io/github/classgraph/issues/issue167/a/b/TestAB.java | 2 +- .../io/github/classgraph/issues/issue171/Issue171Test.java | 3 +-- .../io/github/classgraph/issues/issue175/Issue175Test.java | 3 +-- .../io/github/classgraph/issues/issue193/Issue193Test.java | 3 +-- .../io/github/classgraph/issues/issue209/Issue209Test.java | 3 +-- .../io/github/classgraph/issues/issue216/Issue216Test.java | 3 +-- .../io/github/classgraph/issues/issue223/Issue223Test.java | 3 +-- .../io/github/classgraph/issues/issue238/Issue238Test.java | 3 +-- .../io/github/classgraph/issues/issue245/Issue245Test.java | 3 +-- .../io/github/classgraph/issues/issue246/Issue246Test.java | 3 +-- .../io/github/classgraph/issues/issue255/Issue255Test.java | 3 +-- .../io/github/classgraph/issues/issue260/Issue260Test.java | 3 +-- .../java/io/github/classgraph/issues/issue260/Outer.java | 3 +-- src/test/java/io/github/classgraph/issues/issue260/P.java | 2 +- .../io/github/classgraph/issues/issue261/Issue261Test.java | 3 +-- .../issue267/ClassLoadingWorksWithParentLastLoaders.java | 3 +-- .../issue267/ClassLoadingWorksWithParentLastLoadersStub.java | 3 +-- .../io/github/classgraph/issues/issue277/Issue227Test.java | 1 - .../io/github/classgraph/issues/issue286/Issue286Test.java | 3 +-- .../java/io/github/classgraph/issues/issue289/Issue289.java | 3 +-- .../io/github/classgraph/issues/issue303/Issue303Test.java | 3 +-- .../java/io/github/classgraph/issues/issue305/Issue305.java | 2 +- .../java/io/github/classgraph/issues/issue310/Issue310.java | 3 +-- .../java/io/github/classgraph/issues/issue314/Issue314.java | 3 +-- .../java/io/github/classgraph/issues/issue318/Issue318.java | 1 - .../io/github/classgraph/issues/issue37/Issue37Test.java | 2 +- .../issues/issue38/ImplementsSuppressWarnings.java | 3 +-- .../io/github/classgraph/issues/issue38/Issue38Test.java | 3 +-- .../classgraph/issues/issue38/SomeAnnotationLiteral.java | 3 +-- .../io/github/classgraph/issues/issue46/Issue46Test.java | 3 +-- .../io/github/classgraph/issues/issue74/Issue74Test.java | 3 +-- .../io/github/classgraph/issues/issue78/Issue78Test.java | 3 +-- .../io/github/classgraph/issues/issue80/Issue80Test.java | 3 +-- .../io/github/classgraph/issues/issue83/Issue83Test.java | 3 +-- .../java/io/github/classgraph/issues/issue93/Issue93.java | 3 +-- .../io/github/classgraph/issues/issue99/Issue99Test.java | 3 +-- .../io/github/classgraph/json/JSONSerializationTest.java | 3 +-- src/test/java/io/github/classgraph/test/ClassGraphTest.java | 3 +-- src/test/java/io/github/classgraph/test/ClassInfoTest.java | 3 +-- .../classgraph/test/blacklisted/BlacklistedSubclass.java | 2 +- .../classgraph/test/blacklisted/BlacklistedSuperclass.java | 2 +- .../test/classrefannotation/AnnotationClassRefTest.java | 3 +-- .../github/classgraph/test/external/ExternalSuperclass.java | 2 +- .../ClassWithoutFieldOrMethodAnnotations.java | 3 +-- .../test/fieldannotation/FieldAndMethodAnnotationTest.java | 3 +-- .../io/github/classgraph/test/fieldinfo/FieldInfoTest.java | 3 +-- .../test/internal/InternalAnnotatedByExternal.java | 2 +- .../classgraph/test/internal/InternalExtendsExternal.java | 2 +- .../classgraph/test/internal/InternalExternalTest.java | 3 +-- .../classgraph/test/internal/InternalImplementsExternal.java | 2 +- .../test/methodannotation/ClassWithoutMethodAnnotations.java | 3 +-- .../test/methodannotation/MethodAnnotationTest.java | 3 +-- .../test/methodannotation2/TestMethodMetaAnnotation.java | 3 +-- .../io/github/classgraph/test/methodinfo/MethodInfoTest.java | 3 +-- .../java/io/github/classgraph/test/utils/LogNodeTest.java | 3 +-- src/test/java/io/github/classgraph/test/whitelisted/Cls.java | 2 +- .../java/io/github/classgraph/test/whitelisted/ClsSub.java | 2 +- .../io/github/classgraph/test/whitelisted/ClsSubSub.java | 2 +- .../classgraph/test/whitelisted/HasFieldWithTypeCls.java | 3 +-- .../java/io/github/classgraph/test/whitelisted/Impl1.java | 2 +- .../java/io/github/classgraph/test/whitelisted/Impl1Sub.java | 2 +- .../io/github/classgraph/test/whitelisted/Impl1SubSub.java | 2 +- .../java/io/github/classgraph/test/whitelisted/Impl2.java | 2 +- .../java/io/github/classgraph/test/whitelisted/Impl2Sub.java | 2 +- .../io/github/classgraph/test/whitelisted/Impl2SubSub.java | 2 +- .../io/github/classgraph/test/whitelisted/StaticField.java | 3 +-- .../io/github/classgraph/test/whitelisted/Whitelisted.java | 2 +- .../test/whitelisted/blacklistedsub/BlacklistedSub.java | 2 +- src/test/perf/io/github/classgraph/InputStreamBenchmark.java | 3 +-- .../github/classgraph/issues/issue146/CompiledWithJDK8.java | 1 - 136 files changed, 114 insertions(+), 217 deletions(-) diff --git a/LICENSE b/LICENSE index fcdf34dec..eddec3610 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2015 Luke Hutchison +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 diff --git a/src/main/java/io/github/classgraph/AnnotationClassRef.java b/src/main/java/io/github/classgraph/AnnotationClassRef.java index 98a8e9cd2..15a683d12 100644 --- a/src/main/java/io/github/classgraph/AnnotationClassRef.java +++ b/src/main/java/io/github/classgraph/AnnotationClassRef.java @@ -36,7 +36,6 @@ * Stores the type descriptor of a {@code Class}, as found in an annotation parameter value. */ public class AnnotationClassRef extends ScanResultObject { - /** The type descriptor str. */ private String typeDescriptorStr; diff --git a/src/main/java/io/github/classgraph/AnnotationEnumValue.java b/src/main/java/io/github/classgraph/AnnotationEnumValue.java index 6f2ca0f48..f9f42521e 100644 --- a/src/main/java/io/github/classgraph/AnnotationEnumValue.java +++ b/src/main/java/io/github/classgraph/AnnotationEnumValue.java @@ -36,7 +36,6 @@ * parameter value. */ public class AnnotationEnumValue extends ScanResultObject implements Comparable { - /** The class name. */ private String className; diff --git a/src/main/java/io/github/classgraph/AnnotationInfo.java b/src/main/java/io/github/classgraph/AnnotationInfo.java index d4c8f4b07..9e0a8cc7e 100644 --- a/src/main/java/io/github/classgraph/AnnotationInfo.java +++ b/src/main/java/io/github/classgraph/AnnotationInfo.java @@ -44,7 +44,6 @@ /** Holds metadata about a specific annotation instance on a class, method, method parameter or field. */ public class AnnotationInfo extends ScanResultObject implements Comparable, HasName { - /** The name. */ private String name; diff --git a/src/main/java/io/github/classgraph/AnnotationParameterValue.java b/src/main/java/io/github/classgraph/AnnotationParameterValue.java index 1d7172415..b0ea73b7b 100644 --- a/src/main/java/io/github/classgraph/AnnotationParameterValue.java +++ b/src/main/java/io/github/classgraph/AnnotationParameterValue.java @@ -35,7 +35,6 @@ /** A wrapper used to pair annotation parameter names with annotation parameter values. */ public class AnnotationParameterValue extends ScanResultObject implements HasName, Comparable { - /** The the parameter name. */ private String name; diff --git a/src/main/java/io/github/classgraph/AnnotationParameterValueList.java b/src/main/java/io/github/classgraph/AnnotationParameterValueList.java index 3fd9d3d5d..135201fbe 100644 --- a/src/main/java/io/github/classgraph/AnnotationParameterValueList.java +++ b/src/main/java/io/github/classgraph/AnnotationParameterValueList.java @@ -33,7 +33,6 @@ /** A list of {@link AnnotationParameterValue} objects. */ public class AnnotationParameterValueList extends MappableInfoList { - /** * Constructor. */ diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index 3ac0ee338..b356fc92b 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -59,7 +59,6 @@ * https://github.com/classgraph/classgraph/wiki */ public class ClassGraph { - /** The scanning specification. */ ScanSpec scanSpec = new ScanSpec(); diff --git a/src/main/java/io/github/classgraph/ClassInfoList.java b/src/main/java/io/github/classgraph/ClassInfoList.java index 84a3b4bd3..4c7c9eb62 100644 --- a/src/main/java/io/github/classgraph/ClassInfoList.java +++ b/src/main/java/io/github/classgraph/ClassInfoList.java @@ -53,7 +53,6 @@ * related classes. */ public class ClassInfoList extends MappableInfoList { - /** Directly related classes. */ // N.B. this is marked transient to keep Scrutinizer happy, since thi class extends ArrayList, which is // Serializable, so all fields must be serializable (and Set is an interface, so is not Serializable). diff --git a/src/main/java/io/github/classgraph/FieldInfo.java b/src/main/java/io/github/classgraph/FieldInfo.java index eb0fef3d4..85b33f068 100644 --- a/src/main/java/io/github/classgraph/FieldInfo.java +++ b/src/main/java/io/github/classgraph/FieldInfo.java @@ -43,7 +43,6 @@ * classfile for the class. */ public class FieldInfo extends ScanResultObject implements Comparable, HasName { - /** The declaring class name. */ private String declaringClassName; diff --git a/src/main/java/io/github/classgraph/FieldInfoList.java b/src/main/java/io/github/classgraph/FieldInfoList.java index fe82fca05..e6a6feb7d 100644 --- a/src/main/java/io/github/classgraph/FieldInfoList.java +++ b/src/main/java/io/github/classgraph/FieldInfoList.java @@ -33,7 +33,6 @@ /** A list of {@link FieldInfo} objects. */ public class FieldInfoList extends MappableInfoList { - /** * Constructor. */ diff --git a/src/main/java/io/github/classgraph/MethodInfoList.java b/src/main/java/io/github/classgraph/MethodInfoList.java index 3fa938887..288833a3f 100644 --- a/src/main/java/io/github/classgraph/MethodInfoList.java +++ b/src/main/java/io/github/classgraph/MethodInfoList.java @@ -35,7 +35,6 @@ /** A list of {@link MethodInfo} objects. */ public class MethodInfoList extends InfoList { - /** Construct a list of {@link MethodInfo} objects. */ MethodInfoList() { super(); diff --git a/src/main/java/io/github/classgraph/MethodParameterInfo.java b/src/main/java/io/github/classgraph/MethodParameterInfo.java index 53306c5ff..3b2bb6744 100644 --- a/src/main/java/io/github/classgraph/MethodParameterInfo.java +++ b/src/main/java/io/github/classgraph/MethodParameterInfo.java @@ -40,7 +40,6 @@ * @author lukehutch */ public class MethodParameterInfo { - /** The containing method. */ private final MethodInfo methodInfo; diff --git a/src/main/java/io/github/classgraph/ModuleInfoList.java b/src/main/java/io/github/classgraph/ModuleInfoList.java index 16bd01e81..234aff4ae 100644 --- a/src/main/java/io/github/classgraph/ModuleInfoList.java +++ b/src/main/java/io/github/classgraph/ModuleInfoList.java @@ -32,7 +32,6 @@ /** A list of {@link ModuleInfo} objects. */ public class ModuleInfoList extends MappableInfoList { - /** * Constructor. */ diff --git a/src/main/java/io/github/classgraph/ModuleReaderProxy.java b/src/main/java/io/github/classgraph/ModuleReaderProxy.java index d4ab1ec06..ba4e2379b 100644 --- a/src/main/java/io/github/classgraph/ModuleReaderProxy.java +++ b/src/main/java/io/github/classgraph/ModuleReaderProxy.java @@ -38,7 +38,6 @@ /** A ModuleReader proxy, written using reflection to preserve backwards compatibility with JDK 7 and 8. */ public class ModuleReaderProxy implements Closeable { - /** The module reader. */ private final AutoCloseable moduleReader; diff --git a/src/main/java/io/github/classgraph/PackageInfoList.java b/src/main/java/io/github/classgraph/PackageInfoList.java index 1fdb6ff4e..afde28467 100644 --- a/src/main/java/io/github/classgraph/PackageInfoList.java +++ b/src/main/java/io/github/classgraph/PackageInfoList.java @@ -32,7 +32,6 @@ /** A list of {@link PackageInfo} objects. */ public class PackageInfoList extends MappableInfoList { - /** * Constructor. */ diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderAndModuleFinder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderAndModuleFinder.java index e5b8116f8..e018eba35 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderAndModuleFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderAndModuleFinder.java @@ -44,7 +44,6 @@ /** A class to find the unique ordered classpath elements. */ public class ClassLoaderAndModuleFinder { - /** The context class loaders. */ private final ClassLoader[] contextClassLoaders; 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 26d1384cf..ff9524263 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java @@ -48,7 +48,6 @@ /** A class to find the unique ordered classpath elements. */ public class ClasspathFinder { - /** The classpath order. */ private final ClasspathOrder classpathOrder; 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 e5a131b46..b5ec4351c 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java @@ -46,7 +46,6 @@ /** A class to find the unique ordered classpath elements. */ public class ClasspathOrder { - /** The scan spec. */ private final ScanSpec scanSpec; 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 0f32f224b..7332bd1e1 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/JSONDeserializer.java +++ b/src/main/java/nonapi/io/github/classgraph/json/JSONDeserializer.java @@ -48,7 +48,6 @@ * object graph by inserting reference ids. */ public class JSONDeserializer { - /** * Constructor. */ diff --git a/src/main/java/nonapi/io/github/classgraph/recycler/RecycleOnClose.java b/src/main/java/nonapi/io/github/classgraph/recycler/RecycleOnClose.java index b3fa598dd..5bd265b39 100644 --- a/src/main/java/nonapi/io/github/classgraph/recycler/RecycleOnClose.java +++ b/src/main/java/nonapi/io/github/classgraph/recycler/RecycleOnClose.java @@ -39,7 +39,6 @@ * the exception type that may be thrown when a recyclable item is acquired. */ public class RecycleOnClose implements AutoCloseable { - /** The recycler. */ private final Recycler recycler; diff --git a/src/test/java/ClassGraphGraphVizGenerator.java b/src/test/java/ClassGraphGraphVizGenerator.java index 25668a16f..b0585e4a1 100644 --- a/src/test/java/ClassGraphGraphVizGenerator.java +++ b/src/test/java/ClassGraphGraphVizGenerator.java @@ -5,10 +5,9 @@ import io.github.classgraph.ScanResult; /** - * The Class ClassGraphGraphVizGenerator. + * ClassGraphGraphVizGenerator. */ public class ClassGraphGraphVizGenerator { - /** * The main method. * diff --git a/src/test/java/ClassInDefaultPackage.java b/src/test/java/ClassInDefaultPackage.java index 478de30b7..7a67e1f40 100644 --- a/src/test/java/ClassInDefaultPackage.java +++ b/src/test/java/ClassInDefaultPackage.java @@ -1,5 +1,5 @@ /** - * The Class ClassInDefaultPackage. + * ClassInDefaultPackage. */ public class ClassInDefaultPackage { } diff --git a/src/test/java/DefaultPackageTest.java b/src/test/java/DefaultPackageTest.java index acf2e3601..0ce87ecf2 100644 --- a/src/test/java/DefaultPackageTest.java +++ b/src/test/java/DefaultPackageTest.java @@ -40,10 +40,9 @@ import io.github.classgraph.test.whitelisted.blacklistedsub.BlacklistedSub; /** - * The Class DefaultPackageTest. + * DefaultPackageTest. */ public class DefaultPackageTest { - /** The Constant WHITELIST_PACKAGE. */ private static final String WHITELIST_PACKAGE = Cls.class.getPackage().getName(); diff --git a/src/test/java/DisableRecursiveScanningTest.java b/src/test/java/DisableRecursiveScanningTest.java index bd5b4da46..e5c2d99b5 100644 --- a/src/test/java/DisableRecursiveScanningTest.java +++ b/src/test/java/DisableRecursiveScanningTest.java @@ -40,10 +40,9 @@ import io.github.classgraph.test.whitelisted.blacklistedsub.BlacklistedSub; /** - * The Class DisableRecursiveScanningTest. + * DisableRecursiveScanningTest. */ public class DisableRecursiveScanningTest { - /** The Constant PKG. */ private static final String PKG = Cls.class.getPackage().getName(); diff --git a/src/test/java/com/xyz/GenerateClassGraphFigDotFile.java b/src/test/java/com/xyz/GenerateClassGraphFigDotFile.java index 176212755..083229f27 100644 --- a/src/test/java/com/xyz/GenerateClassGraphFigDotFile.java +++ b/src/test/java/com/xyz/GenerateClassGraphFigDotFile.java @@ -4,10 +4,9 @@ import io.github.classgraph.ScanResult; /** - * The Class GenerateClassGraphFigDotFile. + * GenerateClassGraphFigDotFile. */ public class GenerateClassGraphFigDotFile { - /** * The main method. * diff --git a/src/test/java/com/xyz/MetaAnnotationTest.java b/src/test/java/com/xyz/MetaAnnotationTest.java index 866eb8915..87b6c3ea6 100644 --- a/src/test/java/com/xyz/MetaAnnotationTest.java +++ b/src/test/java/com/xyz/MetaAnnotationTest.java @@ -38,10 +38,9 @@ import io.github.classgraph.ScanResult; /** - * The Class MetaAnnotationTest. + * MetaAnnotationTest. */ public class MetaAnnotationTest { - /** The scan result. */ static ScanResult scanResult; diff --git a/src/test/java/com/xyz/ScanEverything.java b/src/test/java/com/xyz/ScanEverything.java index e34eb86fc..9f74d1043 100644 --- a/src/test/java/com/xyz/ScanEverything.java +++ b/src/test/java/com/xyz/ScanEverything.java @@ -1,10 +1,9 @@ package com.xyz; /** - * The Class ScanEverything. + * ScanEverything. */ public class ScanEverything { - // @Test // public void scanEverything() throws IOException { // final long t0 = System.nanoTime(); diff --git a/src/test/java/com/xyz/fig/Figure.java b/src/test/java/com/xyz/fig/Figure.java index 196e7cdff..5edfc623d 100644 --- a/src/test/java/com/xyz/fig/Figure.java +++ b/src/test/java/com/xyz/fig/Figure.java @@ -5,11 +5,10 @@ import com.xyz.fig.shape.Shape; /** - * The Class Figure. + * Figure. */ @UIWidget public class Figure implements Drawable { - /** The scene graph. */ SceneGraph sceneGraph = new SceneGraph(); diff --git a/src/test/java/com/xyz/fig/SceneGraph.java b/src/test/java/com/xyz/fig/SceneGraph.java index 6ea89d10a..f19136dbe 100644 --- a/src/test/java/com/xyz/fig/SceneGraph.java +++ b/src/test/java/com/xyz/fig/SceneGraph.java @@ -6,10 +6,9 @@ import com.xyz.fig.shape.Shape; /** - * The Class SceneGraph. + * SceneGraph. */ public class SceneGraph implements Drawable { - /** The shapes. */ ArrayList shapes = new ArrayList<>(); diff --git a/src/test/java/com/xyz/fig/shape/Circle.java b/src/test/java/com/xyz/fig/shape/Circle.java index 31c3f4035..9169d57b8 100644 --- a/src/test/java/com/xyz/fig/shape/Circle.java +++ b/src/test/java/com/xyz/fig/shape/Circle.java @@ -3,10 +3,9 @@ import java.awt.Graphics2D; /** - * The Class Circle. + * Circle. */ public class Circle extends ShapeImpl { - /** The r. */ private final float r; diff --git a/src/test/java/com/xyz/fig/shape/Diamond.java b/src/test/java/com/xyz/fig/shape/Diamond.java index f47faa178..26cb90422 100644 --- a/src/test/java/com/xyz/fig/shape/Diamond.java +++ b/src/test/java/com/xyz/fig/shape/Diamond.java @@ -3,10 +3,9 @@ import java.awt.Graphics2D; /** - * The Class Diamond. + * Diamond. */ public class Diamond extends ShapeImpl { - /** The w. */ private final float w; diff --git a/src/test/java/com/xyz/fig/shape/ShapeImpl.java b/src/test/java/com/xyz/fig/shape/ShapeImpl.java index 7cef26fbf..0f3730653 100644 --- a/src/test/java/com/xyz/fig/shape/ShapeImpl.java +++ b/src/test/java/com/xyz/fig/shape/ShapeImpl.java @@ -3,7 +3,7 @@ import java.awt.Graphics2D; /** - * The Class ShapeImpl. + * ShapeImpl. */ public abstract class ShapeImpl implements Shape { diff --git a/src/test/java/com/xyz/fig/shape/Square.java b/src/test/java/com/xyz/fig/shape/Square.java index 54c48be5b..42d0543ba 100644 --- a/src/test/java/com/xyz/fig/shape/Square.java +++ b/src/test/java/com/xyz/fig/shape/Square.java @@ -3,10 +3,9 @@ import java.awt.Graphics2D; /** - * The Class Square. + * Square. */ public class Square extends ShapeImpl { - /** The size. */ private final float size; diff --git a/src/test/java/com/xyz/fig/shape/Triangle.java b/src/test/java/com/xyz/fig/shape/Triangle.java index 5b5a0b83f..7f473c2a3 100644 --- a/src/test/java/com/xyz/fig/shape/Triangle.java +++ b/src/test/java/com/xyz/fig/shape/Triangle.java @@ -3,10 +3,9 @@ import java.awt.Graphics2D; /** - * The Class Triangle. + * Triangle. */ public class Triangle extends ShapeImpl { - /** The edge len. */ private final float edgeLen; diff --git a/src/test/java/com/xyz/meta/A.java b/src/test/java/com/xyz/meta/A.java index 031470537..a1f529a3d 100644 --- a/src/test/java/com/xyz/meta/A.java +++ b/src/test/java/com/xyz/meta/A.java @@ -1,7 +1,7 @@ package com.xyz.meta; /** - * The Class A. + * A. */ @F public class A { diff --git a/src/test/java/com/xyz/meta/B.java b/src/test/java/com/xyz/meta/B.java index a71d91a6a..e47650a14 100644 --- a/src/test/java/com/xyz/meta/B.java +++ b/src/test/java/com/xyz/meta/B.java @@ -1,7 +1,7 @@ package com.xyz.meta; /** - * The Class B. + * B. */ @E @F diff --git a/src/test/java/com/xyz/meta/C.java b/src/test/java/com/xyz/meta/C.java index 78cf92ca0..3a2dcceca 100644 --- a/src/test/java/com/xyz/meta/C.java +++ b/src/test/java/com/xyz/meta/C.java @@ -1,7 +1,7 @@ package com.xyz.meta; /** - * The Class C. + * C. */ @G public class C { diff --git a/src/test/java/io/github/classgraph/features/AnnotationEquality.java b/src/test/java/io/github/classgraph/features/AnnotationEquality.java index 9b45c6380..45c96fe0a 100644 --- a/src/test/java/io/github/classgraph/features/AnnotationEquality.java +++ b/src/test/java/io/github/classgraph/features/AnnotationEquality.java @@ -14,7 +14,7 @@ import io.github.classgraph.ScanResult; /** - * The Class AnnotationEquality. + * AnnotationEquality. */ public class AnnotationEquality { /** diff --git a/src/test/java/io/github/classgraph/features/AnnotationParamWithPrimitiveTypedArray.java b/src/test/java/io/github/classgraph/features/AnnotationParamWithPrimitiveTypedArray.java index 1ad8dfbf5..a9ddf1c25 100644 --- a/src/test/java/io/github/classgraph/features/AnnotationParamWithPrimitiveTypedArray.java +++ b/src/test/java/io/github/classgraph/features/AnnotationParamWithPrimitiveTypedArray.java @@ -14,10 +14,9 @@ import io.github.classgraph.ScanResult; /** - * The Class AnnotationParamWithPrimitiveTypedArray. + * AnnotationParamWithPrimitiveTypedArray. */ public class AnnotationParamWithPrimitiveTypedArray { - /** * The Interface NestedAnnotation. */ diff --git a/src/test/java/io/github/classgraph/features/DeclaredVsNonDeclared.java b/src/test/java/io/github/classgraph/features/DeclaredVsNonDeclared.java index cdf8630b7..d33fcc0de 100644 --- a/src/test/java/io/github/classgraph/features/DeclaredVsNonDeclared.java +++ b/src/test/java/io/github/classgraph/features/DeclaredVsNonDeclared.java @@ -37,10 +37,9 @@ } /** - * The Class DeclaredVsNonDeclared. + * DeclaredVsNonDeclared. */ public class DeclaredVsNonDeclared { - /** * The Class A. */ diff --git a/src/test/java/io/github/classgraph/features/MethodParameterAnnotations.java b/src/test/java/io/github/classgraph/features/MethodParameterAnnotations.java index 2c528eb97..1ec84cc61 100644 --- a/src/test/java/io/github/classgraph/features/MethodParameterAnnotations.java +++ b/src/test/java/io/github/classgraph/features/MethodParameterAnnotations.java @@ -11,7 +11,7 @@ import io.github.classgraph.ScanResult; /** - * The Class AnnotationEquality. + * AnnotationEquality. */ public class MethodParameterAnnotations { /** diff --git a/src/test/java/io/github/classgraph/features/MultiReleaseJar.java b/src/test/java/io/github/classgraph/features/MultiReleaseJar.java index 0f43f87c5..fec222eb8 100644 --- a/src/test/java/io/github/classgraph/features/MultiReleaseJar.java +++ b/src/test/java/io/github/classgraph/features/MultiReleaseJar.java @@ -18,10 +18,9 @@ import nonapi.io.github.classgraph.utils.VersionFinder; /** - * The Class MultiReleaseJar. + * MultiReleaseJar. */ public class MultiReleaseJar { - /** The Constant jarURL. */ private static final URL jarURL = MultiReleaseJar.class.getClassLoader().getResource("multi-release-jar.jar"); diff --git a/src/test/java/io/github/classgraph/issues/GenericInnerClassTypedField.java b/src/test/java/io/github/classgraph/issues/GenericInnerClassTypedField.java index 0feecfe70..904364705 100644 --- a/src/test/java/io/github/classgraph/issues/GenericInnerClassTypedField.java +++ b/src/test/java/io/github/classgraph/issues/GenericInnerClassTypedField.java @@ -10,10 +10,9 @@ import io.github.classgraph.ScanResult; /** - * The Class GenericInnerClassTypedField. + * GenericInnerClassTypedField. */ public class GenericInnerClassTypedField { - /** * The Class A. * diff --git a/src/test/java/io/github/classgraph/issues/IssuesTest.java b/src/test/java/io/github/classgraph/issues/IssuesTest.java index 771d1ffc7..089e097cd 100644 --- a/src/test/java/io/github/classgraph/issues/IssuesTest.java +++ b/src/test/java/io/github/classgraph/issues/IssuesTest.java @@ -12,10 +12,9 @@ import io.github.classgraph.test.whitelisted.Impl1Sub; /** - * The Class IssuesTest. + * IssuesTest. */ public class IssuesTest { - /** * Issue 70. */ diff --git a/src/test/java/io/github/classgraph/issues/ResolveTypeVariable.java b/src/test/java/io/github/classgraph/issues/ResolveTypeVariable.java index b4718dc6a..81b40a1df 100644 --- a/src/test/java/io/github/classgraph/issues/ResolveTypeVariable.java +++ b/src/test/java/io/github/classgraph/issues/ResolveTypeVariable.java @@ -12,13 +12,12 @@ import io.github.classgraph.TypeVariableSignature; /** - * The Class ResolveTypeVariable. + * ResolveTypeVariable. * * @param * the generic type */ public class ResolveTypeVariable> { - /** The list. */ T list; diff --git a/src/test/java/io/github/classgraph/issues/TestGetUniqueClasspathElements.java b/src/test/java/io/github/classgraph/issues/TestGetUniqueClasspathElements.java index a53c7e5de..7fc40ddf9 100644 --- a/src/test/java/io/github/classgraph/issues/TestGetUniqueClasspathElements.java +++ b/src/test/java/io/github/classgraph/issues/TestGetUniqueClasspathElements.java @@ -10,10 +10,9 @@ import io.github.classgraph.ClassGraph; /** - * The Class TestGetUniqueClasspathElements. + * TestGetUniqueClasspathElements. */ public class TestGetUniqueClasspathElements { - /** * Test get unique classpath elements. */ diff --git a/src/test/java/io/github/classgraph/issues/issue100/Issue100Test.java b/src/test/java/io/github/classgraph/issues/issue100/Issue100Test.java index 481e4aaa6..3ee6d0e1f 100644 --- a/src/test/java/io/github/classgraph/issues/issue100/Issue100Test.java +++ b/src/test/java/io/github/classgraph/issues/issue100/Issue100Test.java @@ -42,10 +42,9 @@ import io.github.classgraph.ScanResult; /** - * The Class Issue100Test. + * Issue100Test. */ public class Issue100Test { - /** * Issue 100 test. */ diff --git a/src/test/java/io/github/classgraph/issues/issue101/AnnotatedClass.java b/src/test/java/io/github/classgraph/issues/issue101/AnnotatedClass.java index 9cd8a4273..3d240b43e 100644 --- a/src/test/java/io/github/classgraph/issues/issue101/AnnotatedClass.java +++ b/src/test/java/io/github/classgraph/issues/issue101/AnnotatedClass.java @@ -1,7 +1,7 @@ package io.github.classgraph.issues.issue101; /** - * The Class AnnotatedClass. + * AnnotatedClass. */ @NonInheritedAnnotation @InheritedAnnotation diff --git a/src/test/java/io/github/classgraph/issues/issue101/ImplementsAnnotatedInterface.java b/src/test/java/io/github/classgraph/issues/issue101/ImplementsAnnotatedInterface.java index e8f83f628..b1300408d 100644 --- a/src/test/java/io/github/classgraph/issues/issue101/ImplementsAnnotatedInterface.java +++ b/src/test/java/io/github/classgraph/issues/issue101/ImplementsAnnotatedInterface.java @@ -1,7 +1,7 @@ package io.github.classgraph.issues.issue101; /** - * The Class ImplementsAnnotatedInterface. + * ImplementsAnnotatedInterface. */ public class ImplementsAnnotatedInterface implements AnnotatedInterface { } diff --git a/src/test/java/io/github/classgraph/issues/issue101/ImplementsNonAnnotatedSubinterface.java b/src/test/java/io/github/classgraph/issues/issue101/ImplementsNonAnnotatedSubinterface.java index b39dadbc4..4467eb93f 100644 --- a/src/test/java/io/github/classgraph/issues/issue101/ImplementsNonAnnotatedSubinterface.java +++ b/src/test/java/io/github/classgraph/issues/issue101/ImplementsNonAnnotatedSubinterface.java @@ -1,7 +1,7 @@ package io.github.classgraph.issues.issue101; /** - * The Class ImplementsNonAnnotatedSubinterface. + * ImplementsNonAnnotatedSubinterface. */ public class ImplementsNonAnnotatedSubinterface implements NonAnnotatedSubinterface { } 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 8ac846717..f3bf0f557 100644 --- a/src/test/java/io/github/classgraph/issues/issue101/Issue101Test.java +++ b/src/test/java/io/github/classgraph/issues/issue101/Issue101Test.java @@ -36,10 +36,9 @@ import io.github.classgraph.ScanResult; /** - * The Class Issue101Test. + * Issue101Test. */ public class Issue101Test { - /** * Non inherited annotation. */ diff --git a/src/test/java/io/github/classgraph/issues/issue101/NonAnnotatedSubclass.java b/src/test/java/io/github/classgraph/issues/issue101/NonAnnotatedSubclass.java index d6bb77855..72177f5b3 100644 --- a/src/test/java/io/github/classgraph/issues/issue101/NonAnnotatedSubclass.java +++ b/src/test/java/io/github/classgraph/issues/issue101/NonAnnotatedSubclass.java @@ -1,7 +1,7 @@ package io.github.classgraph.issues.issue101; /** - * The Class NonAnnotatedSubclass. + * NonAnnotatedSubclass. */ public class NonAnnotatedSubclass extends AnnotatedClass { } 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 d80162ef4..ce4120f50 100644 --- a/src/test/java/io/github/classgraph/issues/issue107/Issue107Test.java +++ b/src/test/java/io/github/classgraph/issues/issue107/Issue107Test.java @@ -38,10 +38,9 @@ import io.github.classgraph.ScanResult; /** - * The Class Issue107Test. + * Issue107Test. */ public class Issue107Test { - /** * Issue 107 test. */ diff --git a/src/test/java/io/github/classgraph/issues/issue128/Issue128Test.java b/src/test/java/io/github/classgraph/issues/issue128/Issue128Test.java index 41aac37fd..a88761da6 100644 --- a/src/test/java/io/github/classgraph/issues/issue128/Issue128Test.java +++ b/src/test/java/io/github/classgraph/issues/issue128/Issue128Test.java @@ -42,10 +42,9 @@ import io.github.classgraph.ScanResult; /** - * The Class Issue128Test. + * Issue128Test. */ public class Issue128Test { - /** The Constant SITE. */ private static final String SITE = "https://github.com/classgraph"; diff --git a/src/test/java/io/github/classgraph/issues/issue140/Issue140Test.java b/src/test/java/io/github/classgraph/issues/issue140/Issue140Test.java index 50437963f..2cb83c9db 100644 --- a/src/test/java/io/github/classgraph/issues/issue140/Issue140Test.java +++ b/src/test/java/io/github/classgraph/issues/issue140/Issue140Test.java @@ -42,10 +42,9 @@ import io.github.classgraph.TypeSignature; /** - * The Class Issue140Test. + * Issue140Test. */ public class Issue140Test { - /** The int field. */ // Order of fields is significant public int intField; diff --git a/src/test/java/io/github/classgraph/issues/issue141/Issue141Test.java b/src/test/java/io/github/classgraph/issues/issue141/Issue141Test.java index 10cfe4e07..72fe1192d 100644 --- a/src/test/java/io/github/classgraph/issues/issue141/Issue141Test.java +++ b/src/test/java/io/github/classgraph/issues/issue141/Issue141Test.java @@ -29,7 +29,7 @@ package io.github.classgraph.issues.issue141; /** - * The Class Issue141Test. + * Issue141Test. * * @author wuetherich */ diff --git a/src/test/java/io/github/classgraph/issues/issue146/Issue146Test.java b/src/test/java/io/github/classgraph/issues/issue146/Issue146Test.java index 012479963..766423d19 100644 --- a/src/test/java/io/github/classgraph/issues/issue146/Issue146Test.java +++ b/src/test/java/io/github/classgraph/issues/issue146/Issue146Test.java @@ -38,10 +38,9 @@ import io.github.classgraph.ScanResult; /** - * The Class Issue146Test. + * Issue146Test. */ public class Issue146Test { - /** * Issue 146 test. */ diff --git a/src/test/java/io/github/classgraph/issues/issue148/Issue148Test.java b/src/test/java/io/github/classgraph/issues/issue148/Issue148Test.java index 6d64451e7..205f77c07 100644 --- a/src/test/java/io/github/classgraph/issues/issue148/Issue148Test.java +++ b/src/test/java/io/github/classgraph/issues/issue148/Issue148Test.java @@ -37,10 +37,9 @@ import io.github.classgraph.ScanResult; /** - * The Class Issue148Test. + * Issue148Test. */ public class Issue148Test { - /** The anonymous inner class 1. */ final Runnable anonymousInnerClass1 = new Runnable() { @Override diff --git a/src/test/java/io/github/classgraph/issues/issue148/O1.java b/src/test/java/io/github/classgraph/issues/issue148/O1.java index b16d4b406..0f1ffa7f6 100644 --- a/src/test/java/io/github/classgraph/issues/issue148/O1.java +++ b/src/test/java/io/github/classgraph/issues/issue148/O1.java @@ -1,10 +1,9 @@ package io.github.classgraph.issues.issue148; /** - * The Class O1. + * O1. */ public class O1 { - /** * The Class SI. */ @@ -15,12 +14,10 @@ static class SI { * The Class I. */ public class I { - /** * The Class II. */ public class II { - /** * Constructor. */ diff --git a/src/test/java/io/github/classgraph/issues/issue148/O2.java b/src/test/java/io/github/classgraph/issues/issue148/O2.java index 789c2264a..a514e2791 100644 --- a/src/test/java/io/github/classgraph/issues/issue148/O2.java +++ b/src/test/java/io/github/classgraph/issues/issue148/O2.java @@ -3,10 +3,9 @@ import io.github.classgraph.issues.issue148.O1.SI; /** - * The Class O2. + * O2. */ public class O2 { - /** The x. */ SI x = new SI() { }; 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 55199927e..cd557cafd 100644 --- a/src/test/java/io/github/classgraph/issues/issue151/Issue151Test.java +++ b/src/test/java/io/github/classgraph/issues/issue151/Issue151Test.java @@ -42,10 +42,9 @@ import io.github.classgraph.ScanResult; /** - * The Class Issue151Test. + * Issue151Test. */ public class Issue151Test { - /** * Issue 151 test. */ 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 caae746b3..a883d86c4 100644 --- a/src/test/java/io/github/classgraph/issues/issue152/Issue152Test.java +++ b/src/test/java/io/github/classgraph/issues/issue152/Issue152Test.java @@ -41,10 +41,9 @@ import io.github.classgraph.ScanResult; /** - * The Class Issue152Test. + * Issue152Test. */ public class Issue152Test { - /** The test field. */ public Map> testField; diff --git a/src/test/java/io/github/classgraph/issues/issue153/Issue153Test.java b/src/test/java/io/github/classgraph/issues/issue153/Issue153Test.java index 294da40d3..fc1f18255 100644 --- a/src/test/java/io/github/classgraph/issues/issue153/Issue153Test.java +++ b/src/test/java/io/github/classgraph/issues/issue153/Issue153Test.java @@ -49,7 +49,7 @@ import io.github.classgraph.issues.issue153.Issue153Test.TwoParamAnnotation; /** - * The Class Issue153Test. + * Issue153Test. */ @StringAnnotation("classlabel") @TwoParamAnnotation(value1 = 'x', value2 = { 1, 2, 3 }) @@ -57,7 +57,6 @@ @NestedAnnotation({ @StringAnnotation("one"), @StringAnnotation("two") }) @ClassRefAnnotation(Issue153Test.class) public class Issue153Test { - /** * The Interface StringAnnotation. */ diff --git a/src/test/java/io/github/classgraph/issues/issue166/Issue166Test.java b/src/test/java/io/github/classgraph/issues/issue166/Issue166Test.java index 6f3ee78b6..952d92299 100644 --- a/src/test/java/io/github/classgraph/issues/issue166/Issue166Test.java +++ b/src/test/java/io/github/classgraph/issues/issue166/Issue166Test.java @@ -38,10 +38,9 @@ import io.github.classgraph.ScanResult; /** - * The Class Issue166Test. + * Issue166Test. */ public class Issue166Test { - /** * Issue 166 test. */ diff --git a/src/test/java/io/github/classgraph/issues/issue167/Issue167Test.java b/src/test/java/io/github/classgraph/issues/issue167/Issue167Test.java index 766aa774f..92099cf8a 100644 --- a/src/test/java/io/github/classgraph/issues/issue167/Issue167Test.java +++ b/src/test/java/io/github/classgraph/issues/issue167/Issue167Test.java @@ -43,10 +43,9 @@ import io.github.classgraph.issues.issue167.a.b.TestAB; /** - * The Class Issue167Test. + * Issue167Test. */ public class Issue167Test { - /** The classes. */ public static List> classes = Arrays.asList(TestA.class, TestAB.class); diff --git a/src/test/java/io/github/classgraph/issues/issue167/a/TestA.java b/src/test/java/io/github/classgraph/issues/issue167/a/TestA.java index f903e02f7..9f45e648a 100644 --- a/src/test/java/io/github/classgraph/issues/issue167/a/TestA.java +++ b/src/test/java/io/github/classgraph/issues/issue167/a/TestA.java @@ -1,7 +1,7 @@ package io.github.classgraph.issues.issue167.a; /** - * The Class TestA. + * TestA. */ public class TestA { } diff --git a/src/test/java/io/github/classgraph/issues/issue167/a/b/TestAB.java b/src/test/java/io/github/classgraph/issues/issue167/a/b/TestAB.java index 71ab74389..830322bca 100644 --- a/src/test/java/io/github/classgraph/issues/issue167/a/b/TestAB.java +++ b/src/test/java/io/github/classgraph/issues/issue167/a/b/TestAB.java @@ -1,7 +1,7 @@ package io.github.classgraph.issues.issue167.a.b; /** - * The Class TestAB. + * TestAB. */ public class TestAB { } 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 23ac60cfa..dff45ea48 100644 --- a/src/test/java/io/github/classgraph/issues/issue171/Issue171Test.java +++ b/src/test/java/io/github/classgraph/issues/issue171/Issue171Test.java @@ -11,10 +11,9 @@ import io.github.classgraph.ScanResult; /** - * The Class Issue171Test. + * Issue171Test. */ public class Issue171Test { - /** * Spring boot fully executable jar. */ 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 fb9e980e9..ee847575a 100644 --- a/src/test/java/io/github/classgraph/issues/issue175/Issue175Test.java +++ b/src/test/java/io/github/classgraph/issues/issue175/Issue175Test.java @@ -43,10 +43,9 @@ import io.github.classgraph.ScanResult; /** - * The Class Issue175Test. + * Issue175Test. */ public class Issue175Test { - /** * Test synthetic. */ diff --git a/src/test/java/io/github/classgraph/issues/issue193/Issue193Test.java b/src/test/java/io/github/classgraph/issues/issue193/Issue193Test.java index 5ea9a9149..cc51db52f 100644 --- a/src/test/java/io/github/classgraph/issues/issue193/Issue193Test.java +++ b/src/test/java/io/github/classgraph/issues/issue193/Issue193Test.java @@ -45,10 +45,9 @@ import io.github.classgraph.ScanResult; /** - * The Class Issue193Test. + * Issue193Test. */ public class Issue193Test { - /** * Issue 193 test. * diff --git a/src/test/java/io/github/classgraph/issues/issue209/Issue209Test.java b/src/test/java/io/github/classgraph/issues/issue209/Issue209Test.java index b588d7daf..afa681fd5 100644 --- a/src/test/java/io/github/classgraph/issues/issue209/Issue209Test.java +++ b/src/test/java/io/github/classgraph/issues/issue209/Issue209Test.java @@ -39,10 +39,9 @@ import io.github.classgraph.ScanResult; /** - * The Class Issue209Test. + * Issue209Test. */ public class Issue209Test { - /** * Test spring boot jar with lib jars. */ 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 ad3db2326..2016afcfd 100644 --- a/src/test/java/io/github/classgraph/issues/issue216/Issue216Test.java +++ b/src/test/java/io/github/classgraph/issues/issue216/Issue216Test.java @@ -40,11 +40,10 @@ import io.github.classgraph.ScanResult; /** - * The Class Issue216Test. + * Issue216Test. */ @Entity public class Issue216Test { - /** * Test spring boot jar with lib jars. */ diff --git a/src/test/java/io/github/classgraph/issues/issue223/Issue223Test.java b/src/test/java/io/github/classgraph/issues/issue223/Issue223Test.java index f16aeaa5e..4ad569200 100644 --- a/src/test/java/io/github/classgraph/issues/issue223/Issue223Test.java +++ b/src/test/java/io/github/classgraph/issues/issue223/Issue223Test.java @@ -41,11 +41,10 @@ import io.github.classgraph.ScanResult; /** - * The Class Issue223Test. + * Issue223Test. */ @Entity public class Issue223Test { - /** * The Interface InnerInterface. */ diff --git a/src/test/java/io/github/classgraph/issues/issue238/Issue238Test.java b/src/test/java/io/github/classgraph/issues/issue238/Issue238Test.java index a745e872b..5ee269250 100644 --- a/src/test/java/io/github/classgraph/issues/issue238/Issue238Test.java +++ b/src/test/java/io/github/classgraph/issues/issue238/Issue238Test.java @@ -40,11 +40,10 @@ import io.github.classgraph.ScanResult; /** - * The Class Issue238Test. + * Issue238Test. */ @Entity public class Issue238Test { - /** * The Class B. */ diff --git a/src/test/java/io/github/classgraph/issues/issue245/Issue245Test.java b/src/test/java/io/github/classgraph/issues/issue245/Issue245Test.java index ea7054c0b..cd7460a22 100644 --- a/src/test/java/io/github/classgraph/issues/issue245/Issue245Test.java +++ b/src/test/java/io/github/classgraph/issues/issue245/Issue245Test.java @@ -38,10 +38,9 @@ import io.github.classgraph.ScanResult; /** - * The Class Issue245Test. + * Issue245Test. */ public class Issue245Test { - /** * Test custom package root. */ diff --git a/src/test/java/io/github/classgraph/issues/issue246/Issue246Test.java b/src/test/java/io/github/classgraph/issues/issue246/Issue246Test.java index 9dfcc4279..532853bab 100644 --- a/src/test/java/io/github/classgraph/issues/issue246/Issue246Test.java +++ b/src/test/java/io/github/classgraph/issues/issue246/Issue246Test.java @@ -36,10 +36,9 @@ import io.github.classgraph.ScanResult; /** - * The Class Issue246Test. + * Issue246Test. */ public class Issue246Test { - /** * Test method parameter annotations. */ diff --git a/src/test/java/io/github/classgraph/issues/issue255/Issue255Test.java b/src/test/java/io/github/classgraph/issues/issue255/Issue255Test.java index cf5869afe..0cd9d3476 100644 --- a/src/test/java/io/github/classgraph/issues/issue255/Issue255Test.java +++ b/src/test/java/io/github/classgraph/issues/issue255/Issue255Test.java @@ -39,10 +39,9 @@ import io.github.classgraph.ScanResult; /** - * The Class Issue255Test. + * Issue255Test. */ public class Issue255Test { - /** * Issue 255 test. */ diff --git a/src/test/java/io/github/classgraph/issues/issue260/Issue260Test.java b/src/test/java/io/github/classgraph/issues/issue260/Issue260Test.java index c2c3f3817..e6ed82778 100644 --- a/src/test/java/io/github/classgraph/issues/issue260/Issue260Test.java +++ b/src/test/java/io/github/classgraph/issues/issue260/Issue260Test.java @@ -36,10 +36,9 @@ import io.github.classgraph.ScanResult; /** - * The Class Issue260Test. + * Issue260Test. */ public class Issue260Test { - /** * Issue 260 test. */ diff --git a/src/test/java/io/github/classgraph/issues/issue260/Outer.java b/src/test/java/io/github/classgraph/issues/issue260/Outer.java index 8e1807f16..57c666d52 100644 --- a/src/test/java/io/github/classgraph/issues/issue260/Outer.java +++ b/src/test/java/io/github/classgraph/issues/issue260/Outer.java @@ -1,10 +1,9 @@ package io.github.classgraph.issues.issue260; /** - * The Class Outer. + * Outer. */ public class Outer { - /** * Creates the anonymous. * diff --git a/src/test/java/io/github/classgraph/issues/issue260/P.java b/src/test/java/io/github/classgraph/issues/issue260/P.java index dbcb3fff5..d02db6449 100644 --- a/src/test/java/io/github/classgraph/issues/issue260/P.java +++ b/src/test/java/io/github/classgraph/issues/issue260/P.java @@ -1,7 +1,7 @@ package io.github.classgraph.issues.issue260; /** - * The Class P. + * P. */ public abstract class P { 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 1a20c963b..6df236b87 100644 --- a/src/test/java/io/github/classgraph/issues/issue261/Issue261Test.java +++ b/src/test/java/io/github/classgraph/issues/issue261/Issue261Test.java @@ -36,10 +36,9 @@ import io.github.classgraph.ScanResult; /** - * The Class Issue261Test. + * Issue261Test. */ public class Issue261Test { - /** * The Class SuperSuperCls. */ diff --git a/src/test/java/io/github/classgraph/issues/issue267/ClassLoadingWorksWithParentLastLoaders.java b/src/test/java/io/github/classgraph/issues/issue267/ClassLoadingWorksWithParentLastLoaders.java index 1c0f7df2a..2e0d349ba 100644 --- a/src/test/java/io/github/classgraph/issues/issue267/ClassLoadingWorksWithParentLastLoaders.java +++ b/src/test/java/io/github/classgraph/issues/issue267/ClassLoadingWorksWithParentLastLoaders.java @@ -40,10 +40,9 @@ import io.github.classgraph.ScanResult; /** - * The Class ClassLoadingWorksWithParentLastLoaders. + * ClassLoadingWorksWithParentLastLoaders. */ public class ClassLoadingWorksWithParentLastLoaders { - /** * Assert correct class loaders. * diff --git a/src/test/java/io/github/classgraph/issues/issue267/ClassLoadingWorksWithParentLastLoadersStub.java b/src/test/java/io/github/classgraph/issues/issue267/ClassLoadingWorksWithParentLastLoadersStub.java index c303f099f..947480578 100644 --- a/src/test/java/io/github/classgraph/issues/issue267/ClassLoadingWorksWithParentLastLoadersStub.java +++ b/src/test/java/io/github/classgraph/issues/issue267/ClassLoadingWorksWithParentLastLoadersStub.java @@ -39,10 +39,9 @@ import com.xyz.meta.A; /** - * The Class ClassLoadingWorksWithParentLastLoadersStub. + * ClassLoadingWorksWithParentLastLoadersStub. */ public class ClassLoadingWorksWithParentLastLoadersStub { - /** * Same class loader that found A class should load it. * diff --git a/src/test/java/io/github/classgraph/issues/issue277/Issue227Test.java b/src/test/java/io/github/classgraph/issues/issue277/Issue227Test.java index f0de7f22c..722c3f543 100644 --- a/src/test/java/io/github/classgraph/issues/issue277/Issue227Test.java +++ b/src/test/java/io/github/classgraph/issues/issue277/Issue227Test.java @@ -8,7 +8,6 @@ * https://github.com/classgraph/classgraph/issues/277 */ public class Issue227Test { - /** * Test no args blacklist lib or ext jars. */ diff --git a/src/test/java/io/github/classgraph/issues/issue286/Issue286Test.java b/src/test/java/io/github/classgraph/issues/issue286/Issue286Test.java index 2d9ef4b2c..7b315ba89 100644 --- a/src/test/java/io/github/classgraph/issues/issue286/Issue286Test.java +++ b/src/test/java/io/github/classgraph/issues/issue286/Issue286Test.java @@ -38,10 +38,9 @@ import io.github.classgraph.ScanResult; /** - * The Class Issue286Test. + * Issue286Test. */ public class Issue286Test { - /** * Issue 286 test. */ diff --git a/src/test/java/io/github/classgraph/issues/issue289/Issue289.java b/src/test/java/io/github/classgraph/issues/issue289/Issue289.java index e7cd883d8..b4ce163ec 100644 --- a/src/test/java/io/github/classgraph/issues/issue289/Issue289.java +++ b/src/test/java/io/github/classgraph/issues/issue289/Issue289.java @@ -12,10 +12,9 @@ import io.github.classgraph.ScanResult; /** - * The Class Issue289. + * Issue289. */ public class Issue289 { - /** * Issue 289. */ diff --git a/src/test/java/io/github/classgraph/issues/issue303/Issue303Test.java b/src/test/java/io/github/classgraph/issues/issue303/Issue303Test.java index c06a90e37..3dcdcfd7e 100644 --- a/src/test/java/io/github/classgraph/issues/issue303/Issue303Test.java +++ b/src/test/java/io/github/classgraph/issues/issue303/Issue303Test.java @@ -38,10 +38,9 @@ import io.github.classgraph.ScanResult; /** - * The Class Issue303Test. + * Issue303Test. */ public class Issue303Test { - /** The Constant PACKAGE_NAME. */ private static final String PACKAGE_NAME = "io.github.classgraph"; 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 ba348be52..8e0444b73 100644 --- a/src/test/java/io/github/classgraph/issues/issue305/Issue305.java +++ b/src/test/java/io/github/classgraph/issues/issue305/Issue305.java @@ -16,7 +16,7 @@ import io.github.classgraph.ScanResult; /** - * The Class Issue305. + * Issue305. */ public class Issue305 { /** Test that multi-line continuations in manifest file values are correctly assembled into a string. */ diff --git a/src/test/java/io/github/classgraph/issues/issue310/Issue310.java b/src/test/java/io/github/classgraph/issues/issue310/Issue310.java index 6d52ae365..c9dcc46b3 100644 --- a/src/test/java/io/github/classgraph/issues/issue310/Issue310.java +++ b/src/test/java/io/github/classgraph/issues/issue310/Issue310.java @@ -8,10 +8,9 @@ import io.github.classgraph.ScanResult; /** - * The Class Issue310. + * Issue310. */ public class Issue310 { - /** The Constant A. */ static final double A = 3.0; 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 e8b2537df..7edd325df 100644 --- a/src/test/java/io/github/classgraph/issues/issue314/Issue314.java +++ b/src/test/java/io/github/classgraph/issues/issue314/Issue314.java @@ -8,10 +8,9 @@ import io.github.classgraph.ScanResult; /** - * The Class Issue314. + * Issue314. */ public class Issue314 { - /** * The Class A. */ 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 3fdd76af2..f7b3017b3 100644 --- a/src/test/java/io/github/classgraph/issues/issue318/Issue318.java +++ b/src/test/java/io/github/classgraph/issues/issue318/Issue318.java @@ -17,7 +17,6 @@ * Unit test. */ public class Issue318 { - /** * The Interface MyAnn. */ diff --git a/src/test/java/io/github/classgraph/issues/issue37/Issue37Test.java b/src/test/java/io/github/classgraph/issues/issue37/Issue37Test.java index 884e0c3b0..44aa694e1 100644 --- a/src/test/java/io/github/classgraph/issues/issue37/Issue37Test.java +++ b/src/test/java/io/github/classgraph/issues/issue37/Issue37Test.java @@ -42,7 +42,7 @@ import io.github.classgraph.ScanResult; /** - * The Class Issue37Test. + * Issue37Test. */ public class Issue37Test { /** diff --git a/src/test/java/io/github/classgraph/issues/issue38/ImplementsSuppressWarnings.java b/src/test/java/io/github/classgraph/issues/issue38/ImplementsSuppressWarnings.java index 8ab56e910..224e40f06 100644 --- a/src/test/java/io/github/classgraph/issues/issue38/ImplementsSuppressWarnings.java +++ b/src/test/java/io/github/classgraph/issues/issue38/ImplementsSuppressWarnings.java @@ -3,11 +3,10 @@ import java.lang.annotation.Annotation; /** - * The Class ImplementsSuppressWarnings. + * ImplementsSuppressWarnings. */ @SuppressWarnings("all") public class ImplementsSuppressWarnings implements SuppressWarnings { - /* (non-Javadoc) * @see java.lang.annotation.Annotation#annotationType() */ 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 e3a6bab0d..e9890016e 100644 --- a/src/test/java/io/github/classgraph/issues/issue38/Issue38Test.java +++ b/src/test/java/io/github/classgraph/issues/issue38/Issue38Test.java @@ -10,10 +10,9 @@ import io.github.classgraph.ScanResult; /** - * The Class Issue38Test. + * Issue38Test. */ public class Issue38Test { - /** * The Class AnnotationLiteral. * diff --git a/src/test/java/io/github/classgraph/issues/issue38/SomeAnnotationLiteral.java b/src/test/java/io/github/classgraph/issues/issue38/SomeAnnotationLiteral.java index 62229b544..17659d2d0 100644 --- a/src/test/java/io/github/classgraph/issues/issue38/SomeAnnotationLiteral.java +++ b/src/test/java/io/github/classgraph/issues/issue38/SomeAnnotationLiteral.java @@ -5,11 +5,10 @@ import io.github.classgraph.issues.issue38.Issue38Test.AnnotationLiteral; /** - * The Class SomeAnnotationLiteral. + * SomeAnnotationLiteral. */ @SuppressWarnings("all") public class SomeAnnotationLiteral extends AnnotationLiteral implements SomeAnnotation { - /* (non-Javadoc) * @see io.github.classgraph.issues.issue38.SomeAnnotation#value() */ 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 25317d0f8..6cd4e3031 100644 --- a/src/test/java/io/github/classgraph/issues/issue46/Issue46Test.java +++ b/src/test/java/io/github/classgraph/issues/issue46/Issue46Test.java @@ -36,10 +36,9 @@ import io.github.classgraph.ScanResult; /** - * The Class Issue46Test. + * Issue46Test. */ public class Issue46Test { - /** * Issue 46 test. */ 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 74a43438d..f5c578f0d 100644 --- a/src/test/java/io/github/classgraph/issues/issue74/Issue74Test.java +++ b/src/test/java/io/github/classgraph/issues/issue74/Issue74Test.java @@ -8,10 +8,9 @@ import io.github.classgraph.ScanResult; /** - * The Class Issue74Test. + * Issue74Test. */ public class Issue74Test { - /** * The Interface Function. */ diff --git a/src/test/java/io/github/classgraph/issues/issue78/Issue78Test.java b/src/test/java/io/github/classgraph/issues/issue78/Issue78Test.java index 7fc451210..e1a2d2f23 100644 --- a/src/test/java/io/github/classgraph/issues/issue78/Issue78Test.java +++ b/src/test/java/io/github/classgraph/issues/issue78/Issue78Test.java @@ -8,10 +8,9 @@ import io.github.classgraph.ScanResult; /** - * The Class Issue78Test. + * Issue78Test. */ public class Issue78Test { - /** * Issue 78. */ diff --git a/src/test/java/io/github/classgraph/issues/issue80/Issue80Test.java b/src/test/java/io/github/classgraph/issues/issue80/Issue80Test.java index 7b4dcd686..f4d913b80 100644 --- a/src/test/java/io/github/classgraph/issues/issue80/Issue80Test.java +++ b/src/test/java/io/github/classgraph/issues/issue80/Issue80Test.java @@ -8,10 +8,9 @@ import io.github.classgraph.ScanResult; /** - * The Class Issue80Test. + * Issue80Test. */ public class Issue80Test { - /** * Issue 80. */ diff --git a/src/test/java/io/github/classgraph/issues/issue83/Issue83Test.java b/src/test/java/io/github/classgraph/issues/issue83/Issue83Test.java index bf4a1a556..8494de34b 100644 --- a/src/test/java/io/github/classgraph/issues/issue83/Issue83Test.java +++ b/src/test/java/io/github/classgraph/issues/issue83/Issue83Test.java @@ -14,10 +14,9 @@ import io.github.classgraph.ScanResult; /** - * The Class Issue83Test. + * Issue83Test. */ public class Issue83Test { - /** The Constant jarPathURL. */ private static final URL jarPathURL = Issue83Test.class.getClassLoader().getResource("nested-jars-level1.zip"); 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 8c11940a3..c2479fe45 100644 --- a/src/test/java/io/github/classgraph/issues/issue93/Issue93.java +++ b/src/test/java/io/github/classgraph/issues/issue93/Issue93.java @@ -11,10 +11,9 @@ import io.github.classgraph.ScanResult; /** - * The Class Issue93. + * Issue93. */ public class Issue93 { - /** The Constant PKG. */ private static final String PKG = Issue93.class.getPackage().getName(); diff --git a/src/test/java/io/github/classgraph/issues/issue99/Issue99Test.java b/src/test/java/io/github/classgraph/issues/issue99/Issue99Test.java index 8df525d45..187b49444 100644 --- a/src/test/java/io/github/classgraph/issues/issue99/Issue99Test.java +++ b/src/test/java/io/github/classgraph/issues/issue99/Issue99Test.java @@ -36,10 +36,9 @@ import io.github.classgraph.ScanResult; /** - * The Class Issue99Test. + * Issue99Test. */ public class Issue99Test { - /** The Constant jarPath. */ private static final String jarPath = Issue99Test.class.getClassLoader().getResource("nested-jars-level1.zip") .getPath() + "!level2.jar!level3.jar!classpath1/classpath2"; diff --git a/src/test/java/io/github/classgraph/json/JSONSerializationTest.java b/src/test/java/io/github/classgraph/json/JSONSerializationTest.java index 068aa96cb..2245fbd8c 100644 --- a/src/test/java/io/github/classgraph/json/JSONSerializationTest.java +++ b/src/test/java/io/github/classgraph/json/JSONSerializationTest.java @@ -15,11 +15,10 @@ import nonapi.io.github.classgraph.json.JSONSerializer; /** - * The Class JSONSerializationTest. + * JSONSerializationTest. */ @SuppressWarnings("unused") public class JSONSerializationTest { - /** * The Class A. * diff --git a/src/test/java/io/github/classgraph/test/ClassGraphTest.java b/src/test/java/io/github/classgraph/test/ClassGraphTest.java index c9ed595ea..59b043b28 100644 --- a/src/test/java/io/github/classgraph/test/ClassGraphTest.java +++ b/src/test/java/io/github/classgraph/test/ClassGraphTest.java @@ -63,10 +63,9 @@ import io.github.classgraph.test.whitelisted.blacklistedsub.BlacklistedSub; /** - * The Class ClassGraphTest. + * ClassGraphTest. */ public class ClassGraphTest { - /** The Constant ROOT_PACKAGE. */ private static final String ROOT_PACKAGE = ClassGraphTest.class.getPackage().getName(); diff --git a/src/test/java/io/github/classgraph/test/ClassInfoTest.java b/src/test/java/io/github/classgraph/test/ClassInfoTest.java index fe1721a3c..46394a3af 100644 --- a/src/test/java/io/github/classgraph/test/ClassInfoTest.java +++ b/src/test/java/io/github/classgraph/test/ClassInfoTest.java @@ -25,10 +25,9 @@ import io.github.classgraph.test.whitelisted.Impl2SubSub; /** - * The Class ClassInfoTest. + * ClassInfoTest. */ public class ClassInfoTest { - /** The scan result. */ private static ScanResult scanResult; diff --git a/src/test/java/io/github/classgraph/test/blacklisted/BlacklistedSubclass.java b/src/test/java/io/github/classgraph/test/blacklisted/BlacklistedSubclass.java index ed7d2175a..b7ec4b3ae 100644 --- a/src/test/java/io/github/classgraph/test/blacklisted/BlacklistedSubclass.java +++ b/src/test/java/io/github/classgraph/test/blacklisted/BlacklistedSubclass.java @@ -3,7 +3,7 @@ import io.github.classgraph.test.whitelisted.Whitelisted; /** - * The Class BlacklistedSubclass. + * BlacklistedSubclass. */ public class BlacklistedSubclass extends Whitelisted { } diff --git a/src/test/java/io/github/classgraph/test/blacklisted/BlacklistedSuperclass.java b/src/test/java/io/github/classgraph/test/blacklisted/BlacklistedSuperclass.java index a5b2a2b0d..afa7556cf 100644 --- a/src/test/java/io/github/classgraph/test/blacklisted/BlacklistedSuperclass.java +++ b/src/test/java/io/github/classgraph/test/blacklisted/BlacklistedSuperclass.java @@ -1,7 +1,7 @@ package io.github.classgraph.test.blacklisted; /** - * The Class BlacklistedSuperclass. + * BlacklistedSuperclass. */ public class BlacklistedSuperclass { } 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 6ce3ca66b..79b13b439 100644 --- a/src/test/java/io/github/classgraph/test/classrefannotation/AnnotationClassRefTest.java +++ b/src/test/java/io/github/classgraph/test/classrefannotation/AnnotationClassRefTest.java @@ -47,10 +47,9 @@ import io.github.classgraph.ScanResult; /** - * The Class AnnotationClassRefTest. + * AnnotationClassRefTest. */ public class AnnotationClassRefTest { - /** * The Interface ClassRefAnnotation. */ diff --git a/src/test/java/io/github/classgraph/test/external/ExternalSuperclass.java b/src/test/java/io/github/classgraph/test/external/ExternalSuperclass.java index 26a298469..ba5fccc49 100644 --- a/src/test/java/io/github/classgraph/test/external/ExternalSuperclass.java +++ b/src/test/java/io/github/classgraph/test/external/ExternalSuperclass.java @@ -1,7 +1,7 @@ package io.github.classgraph.test.external; /** - * The Class ExternalSuperclass. + * ExternalSuperclass. */ public class ExternalSuperclass { } diff --git a/src/test/java/io/github/classgraph/test/fieldannotation/ClassWithoutFieldOrMethodAnnotations.java b/src/test/java/io/github/classgraph/test/fieldannotation/ClassWithoutFieldOrMethodAnnotations.java index 102ab5d25..8553621a3 100644 --- a/src/test/java/io/github/classgraph/test/fieldannotation/ClassWithoutFieldOrMethodAnnotations.java +++ b/src/test/java/io/github/classgraph/test/fieldannotation/ClassWithoutFieldOrMethodAnnotations.java @@ -1,10 +1,9 @@ package io.github.classgraph.test.fieldannotation; /** - * The Class ClassWithoutFieldOrMethodAnnotations. + * ClassWithoutFieldOrMethodAnnotations. */ public class ClassWithoutFieldOrMethodAnnotations { - /** The field without annotations. */ public int fieldWithoutAnnotations; 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 7df42e8d7..b0ebeebc3 100644 --- a/src/test/java/io/github/classgraph/test/fieldannotation/FieldAndMethodAnnotationTest.java +++ b/src/test/java/io/github/classgraph/test/fieldannotation/FieldAndMethodAnnotationTest.java @@ -39,10 +39,9 @@ import io.github.classgraph.test.external.ExternalAnnotation; /** - * The Class FieldAndMethodAnnotationTest. + * FieldAndMethodAnnotationTest. */ public class FieldAndMethodAnnotationTest { - /** The public field with annotation. */ public int publicFieldWithAnnotation; diff --git a/src/test/java/io/github/classgraph/test/fieldinfo/FieldInfoTest.java b/src/test/java/io/github/classgraph/test/fieldinfo/FieldInfoTest.java index 929913dda..bff7a4daf 100644 --- a/src/test/java/io/github/classgraph/test/fieldinfo/FieldInfoTest.java +++ b/src/test/java/io/github/classgraph/test/fieldinfo/FieldInfoTest.java @@ -39,10 +39,9 @@ import io.github.classgraph.test.external.ExternalAnnotation; /** - * The Class FieldInfoTest. + * FieldInfoTest. */ public class FieldInfoTest { - /** The Constant publicFieldWithAnnotation. */ @ExternalAnnotation public static final int publicFieldWithAnnotation = 3; diff --git a/src/test/java/io/github/classgraph/test/internal/InternalAnnotatedByExternal.java b/src/test/java/io/github/classgraph/test/internal/InternalAnnotatedByExternal.java index 7414d63fb..867154fc1 100644 --- a/src/test/java/io/github/classgraph/test/internal/InternalAnnotatedByExternal.java +++ b/src/test/java/io/github/classgraph/test/internal/InternalAnnotatedByExternal.java @@ -3,7 +3,7 @@ import io.github.classgraph.test.external.ExternalAnnotation; /** - * The Class InternalAnnotatedByExternal. + * InternalAnnotatedByExternal. */ @ExternalAnnotation public abstract class InternalAnnotatedByExternal { diff --git a/src/test/java/io/github/classgraph/test/internal/InternalExtendsExternal.java b/src/test/java/io/github/classgraph/test/internal/InternalExtendsExternal.java index 63729d72c..492ee4205 100644 --- a/src/test/java/io/github/classgraph/test/internal/InternalExtendsExternal.java +++ b/src/test/java/io/github/classgraph/test/internal/InternalExtendsExternal.java @@ -3,7 +3,7 @@ import io.github.classgraph.test.external.ExternalSuperclass; /** - * The Class InternalExtendsExternal. + * InternalExtendsExternal. */ public abstract class InternalExtendsExternal extends ExternalSuperclass { } 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 6368dc5a8..cdffd96ba 100644 --- a/src/test/java/io/github/classgraph/test/internal/InternalExternalTest.java +++ b/src/test/java/io/github/classgraph/test/internal/InternalExternalTest.java @@ -11,10 +11,9 @@ import io.github.classgraph.test.external.ExternalSuperclass; /** - * The Class InternalExternalTest. + * InternalExternalTest. */ public class InternalExternalTest { - /** * Test whitelisting external classes. */ diff --git a/src/test/java/io/github/classgraph/test/internal/InternalImplementsExternal.java b/src/test/java/io/github/classgraph/test/internal/InternalImplementsExternal.java index 207fc8901..9e5b7f000 100644 --- a/src/test/java/io/github/classgraph/test/internal/InternalImplementsExternal.java +++ b/src/test/java/io/github/classgraph/test/internal/InternalImplementsExternal.java @@ -3,7 +3,7 @@ import io.github.classgraph.test.external.ExternalInterface; /** - * The Class InternalImplementsExternal. + * InternalImplementsExternal. */ public abstract class InternalImplementsExternal implements ExternalInterface { } diff --git a/src/test/java/io/github/classgraph/test/methodannotation/ClassWithoutMethodAnnotations.java b/src/test/java/io/github/classgraph/test/methodannotation/ClassWithoutMethodAnnotations.java index 137d7a424..eeabd91cf 100644 --- a/src/test/java/io/github/classgraph/test/methodannotation/ClassWithoutMethodAnnotations.java +++ b/src/test/java/io/github/classgraph/test/methodannotation/ClassWithoutMethodAnnotations.java @@ -1,10 +1,9 @@ package io.github.classgraph.test.methodannotation; /** - * The Class ClassWithoutMethodAnnotations. + * ClassWithoutMethodAnnotations. */ public class ClassWithoutMethodAnnotations { - /** The field without annotations. */ public int fieldWithoutAnnotations; 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 b0c0848dd..ea2e5c10d 100644 --- a/src/test/java/io/github/classgraph/test/methodannotation/MethodAnnotationTest.java +++ b/src/test/java/io/github/classgraph/test/methodannotation/MethodAnnotationTest.java @@ -42,10 +42,9 @@ import io.github.classgraph.test.external.ExternalAnnotation; /** - * The Class MethodAnnotationTest. + * MethodAnnotationTest. */ public class MethodAnnotationTest { - /** * Get the names of classes with method annotation. */ 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 d3012835f..bb3610579 100644 --- a/src/test/java/io/github/classgraph/test/methodannotation2/TestMethodMetaAnnotation.java +++ b/src/test/java/io/github/classgraph/test/methodannotation2/TestMethodMetaAnnotation.java @@ -43,10 +43,9 @@ import io.github.classgraph.test.external.ExternalAnnotation; /** - * The Class TestMethodMetaAnnotation. + * TestMethodMetaAnnotation. */ public class TestMethodMetaAnnotation { - /** * The Interface MetaAnnotation. */ 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 e04c9f031..0656e3c16 100644 --- a/src/test/java/io/github/classgraph/test/methodinfo/MethodInfoTest.java +++ b/src/test/java/io/github/classgraph/test/methodinfo/MethodInfoTest.java @@ -41,10 +41,9 @@ import io.github.classgraph.test.external.ExternalAnnotation; /** - * The Class MethodInfoTest. + * MethodInfoTest. */ public class MethodInfoTest { - /** * Public method with args. * 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 d9e6d8d0b..313897f69 100644 --- a/src/test/java/io/github/classgraph/test/utils/LogNodeTest.java +++ b/src/test/java/io/github/classgraph/test/utils/LogNodeTest.java @@ -14,10 +14,9 @@ import nonapi.io.github.classgraph.utils.LogNode; /** - * The Class LogNodeTest. + * LogNodeTest. */ public class LogNodeTest { - /** * Test log node logging to system err. */ diff --git a/src/test/java/io/github/classgraph/test/whitelisted/Cls.java b/src/test/java/io/github/classgraph/test/whitelisted/Cls.java index f2a424f78..4095ccbbb 100644 --- a/src/test/java/io/github/classgraph/test/whitelisted/Cls.java +++ b/src/test/java/io/github/classgraph/test/whitelisted/Cls.java @@ -1,7 +1,7 @@ package io.github.classgraph.test.whitelisted; /** - * The Class Cls. + * Cls. */ public class Cls { } diff --git a/src/test/java/io/github/classgraph/test/whitelisted/ClsSub.java b/src/test/java/io/github/classgraph/test/whitelisted/ClsSub.java index a38227b8e..2029358a0 100644 --- a/src/test/java/io/github/classgraph/test/whitelisted/ClsSub.java +++ b/src/test/java/io/github/classgraph/test/whitelisted/ClsSub.java @@ -1,7 +1,7 @@ package io.github.classgraph.test.whitelisted; /** - * The Class ClsSub. + * ClsSub. */ public class ClsSub extends Cls { } diff --git a/src/test/java/io/github/classgraph/test/whitelisted/ClsSubSub.java b/src/test/java/io/github/classgraph/test/whitelisted/ClsSubSub.java index d2e45652c..58311355b 100644 --- a/src/test/java/io/github/classgraph/test/whitelisted/ClsSubSub.java +++ b/src/test/java/io/github/classgraph/test/whitelisted/ClsSubSub.java @@ -1,7 +1,7 @@ package io.github.classgraph.test.whitelisted; /** - * The Class ClsSubSub. + * ClsSubSub. */ public class ClsSubSub extends ClsSub { } diff --git a/src/test/java/io/github/classgraph/test/whitelisted/HasFieldWithTypeCls.java b/src/test/java/io/github/classgraph/test/whitelisted/HasFieldWithTypeCls.java index fd17c8588..5c2ccc565 100644 --- a/src/test/java/io/github/classgraph/test/whitelisted/HasFieldWithTypeCls.java +++ b/src/test/java/io/github/classgraph/test/whitelisted/HasFieldWithTypeCls.java @@ -4,10 +4,9 @@ import java.util.HashMap; /** - * The Class HasFieldWithTypeCls. + * HasFieldWithTypeCls. */ public class HasFieldWithTypeCls { - /** * The Class HasFieldWithTypeCls1. */ diff --git a/src/test/java/io/github/classgraph/test/whitelisted/Impl1.java b/src/test/java/io/github/classgraph/test/whitelisted/Impl1.java index 2fb4c4a20..f81a6be11 100644 --- a/src/test/java/io/github/classgraph/test/whitelisted/Impl1.java +++ b/src/test/java/io/github/classgraph/test/whitelisted/Impl1.java @@ -1,7 +1,7 @@ package io.github.classgraph.test.whitelisted; /** - * The Class Impl1. + * Impl1. */ public class Impl1 implements IfaceSubSub { } diff --git a/src/test/java/io/github/classgraph/test/whitelisted/Impl1Sub.java b/src/test/java/io/github/classgraph/test/whitelisted/Impl1Sub.java index 399c20e6f..506623de0 100644 --- a/src/test/java/io/github/classgraph/test/whitelisted/Impl1Sub.java +++ b/src/test/java/io/github/classgraph/test/whitelisted/Impl1Sub.java @@ -1,7 +1,7 @@ package io.github.classgraph.test.whitelisted; /** - * The Class Impl1Sub. + * Impl1Sub. */ public class Impl1Sub extends Impl1 { } diff --git a/src/test/java/io/github/classgraph/test/whitelisted/Impl1SubSub.java b/src/test/java/io/github/classgraph/test/whitelisted/Impl1SubSub.java index 0ad6a16b8..051df6708 100644 --- a/src/test/java/io/github/classgraph/test/whitelisted/Impl1SubSub.java +++ b/src/test/java/io/github/classgraph/test/whitelisted/Impl1SubSub.java @@ -1,7 +1,7 @@ package io.github.classgraph.test.whitelisted; /** - * The Class Impl1SubSub. + * Impl1SubSub. */ public class Impl1SubSub extends Impl1Sub { } diff --git a/src/test/java/io/github/classgraph/test/whitelisted/Impl2.java b/src/test/java/io/github/classgraph/test/whitelisted/Impl2.java index 353196d4b..591f1f4ee 100644 --- a/src/test/java/io/github/classgraph/test/whitelisted/Impl2.java +++ b/src/test/java/io/github/classgraph/test/whitelisted/Impl2.java @@ -1,7 +1,7 @@ package io.github.classgraph.test.whitelisted; /** - * The Class Impl2. + * Impl2. */ public class Impl2 implements Iface { } diff --git a/src/test/java/io/github/classgraph/test/whitelisted/Impl2Sub.java b/src/test/java/io/github/classgraph/test/whitelisted/Impl2Sub.java index ead825ce7..27b8ad6ec 100644 --- a/src/test/java/io/github/classgraph/test/whitelisted/Impl2Sub.java +++ b/src/test/java/io/github/classgraph/test/whitelisted/Impl2Sub.java @@ -1,7 +1,7 @@ package io.github.classgraph.test.whitelisted; /** - * The Class Impl2Sub. + * Impl2Sub. */ public class Impl2Sub extends Impl2 { } diff --git a/src/test/java/io/github/classgraph/test/whitelisted/Impl2SubSub.java b/src/test/java/io/github/classgraph/test/whitelisted/Impl2SubSub.java index c417684e2..01bdb8547 100644 --- a/src/test/java/io/github/classgraph/test/whitelisted/Impl2SubSub.java +++ b/src/test/java/io/github/classgraph/test/whitelisted/Impl2SubSub.java @@ -1,7 +1,7 @@ package io.github.classgraph.test.whitelisted; /** - * The Class Impl2SubSub. + * Impl2SubSub. */ public class Impl2SubSub extends Impl2Sub implements IfaceSubSub { } diff --git a/src/test/java/io/github/classgraph/test/whitelisted/StaticField.java b/src/test/java/io/github/classgraph/test/whitelisted/StaticField.java index 5fce99442..da900caa0 100644 --- a/src/test/java/io/github/classgraph/test/whitelisted/StaticField.java +++ b/src/test/java/io/github/classgraph/test/whitelisted/StaticField.java @@ -1,10 +1,9 @@ package io.github.classgraph.test.whitelisted; /** - * The Class StaticField. + * StaticField. */ public class StaticField { - /** The Constant stringField. */ // Non-public -- need ignoreFieldVisibility() to match these static final String stringField = "Static field contents"; diff --git a/src/test/java/io/github/classgraph/test/whitelisted/Whitelisted.java b/src/test/java/io/github/classgraph/test/whitelisted/Whitelisted.java index 9acd09836..ab1ca04a4 100644 --- a/src/test/java/io/github/classgraph/test/whitelisted/Whitelisted.java +++ b/src/test/java/io/github/classgraph/test/whitelisted/Whitelisted.java @@ -5,7 +5,7 @@ import io.github.classgraph.test.blacklisted.BlacklistedSuperclass; /** - * The Class Whitelisted. + * Whitelisted. */ @BlacklistedAnnotation public class Whitelisted extends BlacklistedSuperclass implements BlacklistedInterface { diff --git a/src/test/java/io/github/classgraph/test/whitelisted/blacklistedsub/BlacklistedSub.java b/src/test/java/io/github/classgraph/test/whitelisted/blacklistedsub/BlacklistedSub.java index 2f7a0cdb2..d544bbde4 100644 --- a/src/test/java/io/github/classgraph/test/whitelisted/blacklistedsub/BlacklistedSub.java +++ b/src/test/java/io/github/classgraph/test/whitelisted/blacklistedsub/BlacklistedSub.java @@ -1,7 +1,7 @@ package io.github.classgraph.test.whitelisted.blacklistedsub; /** - * The Class BlacklistedSub. + * BlacklistedSub. */ public class BlacklistedSub { } diff --git a/src/test/perf/io/github/classgraph/InputStreamBenchmark.java b/src/test/perf/io/github/classgraph/InputStreamBenchmark.java index 7767b747b..d2b971736 100644 --- a/src/test/perf/io/github/classgraph/InputStreamBenchmark.java +++ b/src/test/perf/io/github/classgraph/InputStreamBenchmark.java @@ -42,11 +42,10 @@ import org.openjdk.jmh.infra.Blackhole; /** - * The Class InputStreamBenchmark. + * InputStreamBenchmark. */ @State(Scope.Benchmark) public class InputStreamBenchmark { - /** The nb bytes. */ @Param({ "16", "4096", "32178", "500000", "5000000" }) public int nbBytes; diff --git a/src/test/resources/io/github/classgraph/issues/issue146/CompiledWithJDK8.java b/src/test/resources/io/github/classgraph/issues/issue146/CompiledWithJDK8.java index 3d8c484ab..9d30e3f69 100644 --- a/src/test/resources/io/github/classgraph/issues/issue146/CompiledWithJDK8.java +++ b/src/test/resources/io/github/classgraph/issues/issue146/CompiledWithJDK8.java @@ -2,6 +2,5 @@ // Compile this with JDK8, using the commandline switch: -parameters public class CompiledWithJDK8 { - public void method(int param0, String param1, double[] param2) {} } From fbea81fb7f02b3a9d6313075fdd5822aac0d56fb Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 21 Mar 2019 02:51:46 -0600 Subject: [PATCH 0040/1778] Use all environment classloaders in turn when classloading (#331) --- .../classpath/ClassLoaderAndModuleFinder.java | 41 ++++++++----------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderAndModuleFinder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderAndModuleFinder.java index e018eba35..41f083482 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderAndModuleFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderAndModuleFinder.java @@ -261,13 +261,25 @@ private static List findModuleRefs(final Class[] callStack, final // those cases are ill-defined -- see: // http://www.javaworld.com/article/2077344/core-java/find-a-way-out-of-the-classloader-maze.html?page=2 - // Get system classloader + // Get context classloader (this is the classloader used by Class.forName(className)) classLoadersUnique = new LinkedHashSet<>(); + final ClassLoader currClassClassLoader = getClass().getClassLoader(); + if (currClassClassLoader != null) { + classLoadersUnique.add(currClassClassLoader); + } + + // Get system classloader final ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); if (systemClassLoader != null) { classLoadersUnique.add(systemClassLoader); } + // Get thread classloader + final ClassLoader threadClassLoader = Thread.currentThread().getContextClassLoader(); + if (threadClassLoader != null) { + classLoadersUnique.add(threadClassLoader); + } + // There is one more classloader in JDK9+, the platform classloader (used for handling extensions), // see: http://openjdk.java.net/jeps/261#Class-loaders // The method call to get it is ClassLoader.getPlatformClassLoader() @@ -316,13 +328,7 @@ private static List findModuleRefs(final Class[] callStack, final } } - // Get context classloader - final ClassLoader threadClassLoader = Thread.currentThread().getContextClassLoader(); - if (threadClassLoader != null) { - classLoadersUnique.add(threadClassLoader); - } - - // Add any custom-added classloaders after system/context classloaders + // Add any custom-added classloaders after system/context/module classloaders if (scanSpec.addedClassLoaders != null) { classLoadersUnique.addAll(scanSpec.addedClassLoaders); } @@ -334,24 +340,9 @@ private static List findModuleRefs(final Class[] callStack, final classLoadersFoundLog = log == null ? null : log.log("Override ClassLoaders:"); } - // Remove all ancestral classloaders (they are called automatically during class load) - final Set ancestralClassLoaders = new HashSet<>(classLoadersUnique.size()); - for (final ClassLoader classLoader : classLoadersUnique) { - for (ClassLoader cl = classLoader.getParent(); cl != null; cl = cl.getParent()) { - ancestralClassLoaders.add(cl); - } - } - final List classLoaderFinalOrder = new ArrayList<>(classLoadersUnique.size()); - for (final ClassLoader classLoader : classLoadersUnique) { - // Build final ClassLoader order, with ancestral classloaders removed - if (!ancestralClassLoaders.contains(classLoader)) { - classLoaderFinalOrder.add(classLoader); - } - } - // Log all identified ClassLoaders if (classLoadersFoundLog != null) { - for (final ClassLoader classLoader : classLoaderFinalOrder) { + for (final ClassLoader classLoader : classLoadersUnique) { classLoadersFoundLog.log(classLoader.getClass().getName()); } } @@ -376,6 +367,6 @@ private static List findModuleRefs(final Class[] callStack, final } } - this.contextClassLoaders = classLoaderFinalOrder.toArray(new ClassLoader[0]); + this.contextClassLoaders = classLoadersUnique.toArray(new ClassLoader[0]); } } From de4045723a5b1737deeb672427d5e82686ae286f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 21 Mar 2019 05:10:35 -0600 Subject: [PATCH 0041/1778] Fix topological sort of classloader parent delegation graph (#331) --- .../classgraph/ClassGraphClassLoader.java | 9 +- .../java/io/github/classgraph/ScanResult.java | 27 +- .../java/io/github/classgraph/Scanner.java | 36 +- .../classpath/ClassLoaderAndModuleFinder.java | 2 + .../classgraph/classpath/ClasspathFinder.java | 383 +++++++++++------- ...LoadingWorksWithParentLastLoadersStub.java | 11 +- 6 files changed, 282 insertions(+), 186 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java index c99dff3ac..30c42e631 100644 --- a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java +++ b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java @@ -64,13 +64,12 @@ protected Class findClass(final String className) // Get ClassInfo for named class final ClassInfo classInfo = scanResult.getClassInfo(className); - // Try environment classloaders first, if the classpath was not overridden, or the scan result - // came from deserialization (since in this case, a new URLClassLoader was created for the - // classpath entries that were found in the serialized JSON doc) + // Try environment classloaders first boolean triedClassInfoLoader = false; - if (scanResult.envClassLoaderOrder != null) { + final ClassLoader[] classLoaderOrder = scanResult.getClassLoaderOrderRespectingParentDelegation(); + if (classLoaderOrder != null) { // Try environment classloaders - for (final ClassLoader envClassLoader : scanResult.envClassLoaderOrder) { + for (final ClassLoader envClassLoader : classLoaderOrder) { try { return Class.forName(className, scanResult.scanSpec.initializeLoadedClasses, envClassLoader); } catch (ReflectiveOperationException | LinkageError e) { diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index 0fcf62363..a511f7ceb 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -98,11 +98,10 @@ public final class ScanResult implements Closeable, AutoCloseable { private ClassGraphClassLoader classGraphClassLoader; /** - * The default order in which ClassLoaders are called to load classes. Used when a specific class does not have - * a record of which ClassLoader provided the URL used to locate the class (e.g. if the class is found using - * java.class.path). + * The default order in which ClassLoaders are called to load classes, respecting parent-first/parent-last + * delegation order. */ - ClassLoader[] envClassLoaderOrder; + private ClassLoader[] classLoaderOrderRespectingParentDelegation; /** The nested jar handler instance. */ private NestedJarHandler nestedJarHandler; @@ -221,8 +220,8 @@ public void run() { * the classpath order * @param rawClasspathEltOrderStrs * the raw classpath element order - * @param envClassLoaderOrder - * the environment classloader order + * @param classLoaderOrderRespectingParentDelegation + * the environment classloader order, respecting parent-first or parent-last delegation order * @param classNameToClassInfo * a map from class name to class info * @param packageNameToPackageInfo @@ -237,7 +236,8 @@ public void run() { * the toplevel log */ ScanResult(final ScanSpec scanSpec, final List classpathOrder, - final List rawClasspathEltOrderStrs, final ClassLoader[] envClassLoaderOrder, + final List rawClasspathEltOrderStrs, + final ClassLoader[] classLoaderOrderRespectingParentDelegation, final Map classNameToClassInfo, final Map packageNameToPackageInfo, final Map moduleNameToModuleInfo, final Map fileToLastModified, @@ -245,7 +245,7 @@ public void run() { this.scanSpec = scanSpec; this.rawClasspathEltOrderStrs = rawClasspathEltOrderStrs; this.classpathOrder = classpathOrder; - this.envClassLoaderOrder = envClassLoaderOrder; + this.classLoaderOrderRespectingParentDelegation = classLoaderOrderRespectingParentDelegation; this.fileToLastModified = fileToLastModified; this.classNameToClassInfo = classNameToClassInfo; this.packageNameToPackageInfo = packageNameToPackageInfo; @@ -1113,6 +1113,15 @@ public long classpathContentsLastModifiedTime() { // ------------------------------------------------------------------------------------------------------------- // Classloading + /** + * Get the ClassLoader order, respecting parent-first/parent-last delegation order. + * + * @return the class loader order. + */ + ClassLoader[] getClassLoaderOrderRespectingParentDelegation() { + return classLoaderOrderRespectingParentDelegation; + } + /** * 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 @@ -1381,7 +1390,7 @@ public void close() { nestedJarHandler = null; } classGraphClassLoader = null; - envClassLoaderOrder = null; + classLoaderOrderRespectingParentDelegation = null; // Remove WeakReference to this ScanResult, so shutdown hook does not try to close this nonClosedWeakReferences.remove(weakReference); // Flush log on exit, in case additional log entries were generated after scan() completed diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index fa813315e..67df14319 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -105,8 +105,8 @@ class Scanner implements Callable { /** The module order. */ private final List moduleClasspathEltOrder; - /** The context classloaders. */ - private final ClassLoader[] contextClassLoaders; + /** The environment classloader order, respecting parent-first or parent-last delegation order. */ + private final ClassLoader[] classLoaderOrderRespectingParentDelegation; // ------------------------------------------------------------------------------------------------------------- @@ -155,7 +155,8 @@ class Scanner implements Callable { final LogNode classpathFinderLog = topLevelLog == null ? null : topLevelLog.log("Finding classpath"); this.classpathFinder = new ClasspathFinder(scanSpec, classpathFinderLog); this.classLoaderAndModuleFinder = classpathFinder.getClassLoaderAndModuleFinder(); - this.contextClassLoaders = classLoaderAndModuleFinder.getContextClassLoaders(); + this.classLoaderOrderRespectingParentDelegation = classpathFinder + .getClassLoaderOrderRespectingParentDelegation(); this.moduleClasspathEltOrder = getModuleOrder(classpathFinderLog); } @@ -175,9 +176,10 @@ private List getModuleOrder(final LogNode log) throws In if (scanSpec.overrideClasspath == null && scanSpec.overrideClassLoaders == null && scanSpec.scanModules) { // Add modules to start of classpath order, before traditional classpath final List systemModuleRefs = classLoaderAndModuleFinder.getSystemModuleRefs(); - final ClassLoader defaultClassLoader = contextClassLoaders != null && contextClassLoaders.length != 0 - ? contextClassLoaders[0] - : null; + final ClassLoader defaultClassLoader = classLoaderOrderRespectingParentDelegation != null + && classLoaderOrderRespectingParentDelegation.length != 0 + ? classLoaderOrderRespectingParentDelegation[0] + : null; if (systemModuleRefs != null) { for (final ModuleRef systemModuleRef : systemModuleRefs) { final String moduleName = systemModuleRef.getName(); @@ -773,8 +775,8 @@ private void maskClassfiles(final List classpathElementOrder, * the final classpath elt order * @param finalClasspathEltOrderStrs * the final classpath elt order strs - * @param contextClassLoaders - * the context classloaders + * @param classLoaderOrderRespectingParentDelegation + * the environment classloader order, respecting parent-first or parent-last delegation order * @return the scan result * @throws InterruptedException * if the scan was interrupted @@ -782,7 +784,8 @@ private void maskClassfiles(final List classpathElementOrder, * if the scan threw an uncaught exception */ private ScanResult performScan(final List finalClasspathEltOrder, - final List finalClasspathEltOrderStrs, final ClassLoader[] contextClassLoaders) + final List finalClasspathEltOrderStrs, + final ClassLoader[] classLoaderOrderRespectingParentDelegation) throws InterruptedException, ExecutionException { // Mask classfiles (remove any classfile resources that are shadowed by an earlier definition // of the same class) @@ -862,9 +865,9 @@ private ScanResult performScan(final List finalClasspathEltOrd } // Return a new ScanResult - return new ScanResult(scanSpec, finalClasspathEltOrder, finalClasspathEltOrderStrs, contextClassLoaders, - classNameToClassInfo, packageNameToPackageInfo, moduleNameToModuleInfo, fileToLastModified, - nestedJarHandler, topLevelLog); + return new ScanResult(scanSpec, finalClasspathEltOrder, finalClasspathEltOrderStrs, + classLoaderOrderRespectingParentDelegation, classNameToClassInfo, packageNameToPackageInfo, + moduleNameToModuleInfo, fileToLastModified, nestedJarHandler, topLevelLog); } // ------------------------------------------------------------------------------------------------------------- @@ -957,16 +960,17 @@ public void processWorkUnit(final ClasspathElement classpathElement, if (scanSpec.performScan) { // Scan classpath / modules, producing a ScanResult. - return performScan(finalClasspathEltOrderFiltered, finalClasspathEltOrderStrs, contextClassLoaders); + return performScan(finalClasspathEltOrderFiltered, finalClasspathEltOrderStrs, + classLoaderOrderRespectingParentDelegation); } else { // Only getting classpath -- return a placeholder ScanResult to hold classpath elements if (topLevelLog != null) { topLevelLog.log("Only returning classpath elements (not performing a scan)"); } return new ScanResult(scanSpec, finalClasspathEltOrderFiltered, finalClasspathEltOrderStrs, - contextClassLoaders, /* classNameToClassInfo = */ null, /* packageNameToPackageInfo = */ null, - /* moduleNameToModuleInfo = */ null, /* fileToLastModified = */ null, nestedJarHandler, - topLevelLog); + classLoaderOrderRespectingParentDelegation, /* classNameToClassInfo = */ null, + /* packageNameToPackageInfo = */ null, /* moduleNameToModuleInfo = */ null, + /* fileToLastModified = */ null, nestedJarHandler, topLevelLog); } } diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderAndModuleFinder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderAndModuleFinder.java index 41f083482..c8c2c045b 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderAndModuleFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderAndModuleFinder.java @@ -53,6 +53,8 @@ public class ClassLoaderAndModuleFinder { /** The non system module refs. */ private List nonSystemModuleRefs; + // ------------------------------------------------------------------------------------------------------------- + /** * Get the context class loaders. * 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 ff9524263..e3cd30b48 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java @@ -30,15 +30,18 @@ import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; +import java.util.Deque; +import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; +import java.util.LinkedList; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import java.util.Set; -import io.github.classgraph.ClassGraphException; import nonapi.io.github.classgraph.ScanSpec; import nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler; -import nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler.DelegationOrder; import nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandlerRegistry; import nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandlerRegistry.ClassLoaderHandlerRegistryEntry; import nonapi.io.github.classgraph.utils.FastPathResolver; @@ -54,167 +57,253 @@ public class ClasspathFinder { /** The classloader and module finder. */ private final ClassLoaderAndModuleFinder classLoaderAndModuleFinder; + /** + * The default order in which ClassLoaders are called to load classes, respecting parent-first/parent-last + * delegation order. + */ + private ClassLoader[] classLoaderOrderRespectingParentDelegation; + // ------------------------------------------------------------------------------------------------------------- /** - * Add a ClassLoaderHandler, and recurse to parent classloader. + * Get the classpath order. + * + * @return The order of raw classpath elements obtained from ClassLoaders. + */ + public ClasspathOrder getClasspathOrder() { + return classpathOrder; + } + + /** + * Get the classloader and module finder. + * + * @return The {@link ClassLoaderAndModuleFinder}. + */ + public ClassLoaderAndModuleFinder getClassLoaderAndModuleFinder() { + return classLoaderAndModuleFinder; + } + + /** + * Get the ClassLoader order, respecting parent-first/parent-last delegation order. + * + * @return the class loader order. + */ + public ClassLoader[] getClassLoaderOrderRespectingParentDelegation() { + return classLoaderOrderRespectingParentDelegation; + } + + // ------------------------------------------------------------------------------------------------------------- + + /** + * Find a {@link ClassLoaderHandler} that can handle a given {@link ClassLoader}. * - * @param scanSpec - * the scan spec * @param classLoader * the classloader - * @param classLoaderHandlerRegistryEntry - * the classloader handler registry entry - * @param foundClassLoaders - * the found classloaders + * @param visitedEmbedded + * the visited embedded classloaders * @param allClassLoaderHandlerRegistryEntries - * the all classloader handler registry entries - * @param classLoaderAndHandlerOrderOut - * the classloader and handler order - * @param ignoredClassLoaderAndHandlerOrderOut - * the ignored classloader and handler order - * @param visited - * visited + * the ClassLoaderHandler registry entries * @param log * the log - * @return true, if successful + * @return the {@link ClassLoaderHandler} */ - private boolean addClassLoaderHandler(final ScanSpec scanSpec, final ClassLoader classLoader, - final ClassLoaderHandlerRegistryEntry classLoaderHandlerRegistryEntry, - final Set foundClassLoaders, - final List allClassLoaderHandlerRegistryEntries, - final List> classLoaderAndHandlerOrderOut, - final List> ignoredClassLoaderAndHandlerOrderOut, - final Set visited, final LogNode log) { - // Instantiate a ClassLoaderHandler for each ClassLoader, in case the ClassLoaderHandler has state - final ClassLoaderHandler classLoaderHandler = classLoaderHandlerRegistryEntry.instantiate(log); - if (classLoaderHandler != null) { - if (log != null) { - log.log("ClassLoader " + classLoader.getClass().getName() + " will be handled by " - + classLoaderHandler); - } - final ClassLoader embeddedClassLoader = classLoaderHandler.getEmbeddedClassLoader(classLoader); - if (embeddedClassLoader != null) { - if (visited.add(embeddedClassLoader)) { - if (log != null) { - log.log("Delegating from " + classLoader.getClass().getName() + " to embedded ClassLoader " - + embeddedClassLoader.getClass().getName()); - } - return addClassLoaderHandler(scanSpec, embeddedClassLoader, classLoaderHandlerRegistryEntry, - foundClassLoaders, allClassLoaderHandlerRegistryEntries, classLoaderAndHandlerOrderOut, - ignoredClassLoaderAndHandlerOrderOut, visited, log); - } else { - if (log != null) { - log.log("Hit infinite loop when delegating from " + classLoader.getClass().getName() - + " to embedded ClassLoader " + embeddedClassLoader.getClass().getName()); + private static ClassLoaderHandler findClassLoaderHandler(final ClassLoader classLoader, + final Set visitedEmbedded, + final List allClassLoaderHandlerRegistryEntries, final LogNode log) { + // Iterate through each superclass of the classloader + final Class classLoaderClass = classLoader.getClass(); + for (Class superclass = classLoaderClass; superclass != null; superclass = superclass.getSuperclass()) { + final String superclassName = superclass.getName(); + // Compare against the class names handled by each ClassLoaderHandler + for (final ClassLoaderHandlerRegistryEntry registryEntry : allClassLoaderHandlerRegistryEntries) { + for (final String handledClassLoaderName : registryEntry.handledClassLoaderNames) { + if (handledClassLoaderName.equals(superclassName)) { + // This ClassLoaderHandler can handle this class -- instantiate it + final ClassLoaderHandler classLoaderHandler = registryEntry.instantiate(log); + if (classLoaderHandler != null) { + // If there is an embedded classloader, recursively find the ClassLoaderHandler + // for the embedded classloader + final ClassLoader embeddedClassLoader = classLoaderHandler + .getEmbeddedClassLoader(classLoader); + if (embeddedClassLoader != null && embeddedClassLoader != classLoader + && visitedEmbedded.add(embeddedClassLoader)) { + if (log != null) { + log.log("Delegating from " + classLoader.getClass().getName() + + " to embedded ClassLoader " + + embeddedClassLoader.getClass().getName()); + } + return findClassLoaderHandler(embeddedClassLoader, visitedEmbedded, + allClassLoaderHandlerRegistryEntries, log); + } + // Otherwise, return the found ClassLoaderHandler + if (log != null) { + log.log("ClassLoader " + classLoader.getClass().getName() + " will be handled by " + + classLoaderHandler.getClass().getName()); + } + return classLoaderHandler; + } } - return false; - } - } else { - final DelegationOrder delegationOrder = classLoaderHandler.getDelegationOrder(classLoader); - final ClassLoader parent = classLoader.getParent(); - if (log != null && parent != null) { - log.log(classLoader.getClass().getName() + " delegates to parent " + parent.getClass().getName() - + " with order " + delegationOrder); } - switch (delegationOrder) { - case PARENT_FIRST: - // Recurse to parent first, then add this ClassLoader to order - if (parent != null) { - findClassLoaderHandlerForClassLoaderAndParents(scanSpec, parent, foundClassLoaders, - allClassLoaderHandlerRegistryEntries, - scanSpec.ignoreParentClassLoaders ? ignoredClassLoaderAndHandlerOrderOut - : classLoaderAndHandlerOrderOut, - ignoredClassLoaderAndHandlerOrderOut, log); - } - classLoaderAndHandlerOrderOut.add(new SimpleEntry<>(classLoader, classLoaderHandler)); - return true; - case PARENT_LAST: - // Add this ClassLoader to order, then recurse to parent - classLoaderAndHandlerOrderOut.add(new SimpleEntry<>(classLoader, classLoaderHandler)); - if (parent != null) { - findClassLoaderHandlerForClassLoaderAndParents(scanSpec, parent, foundClassLoaders, - allClassLoaderHandlerRegistryEntries, - scanSpec.ignoreParentClassLoaders ? ignoredClassLoaderAndHandlerOrderOut - : classLoaderAndHandlerOrderOut, - ignoredClassLoaderAndHandlerOrderOut, log); - } - return true; - default: - throw ClassGraphException.newClassGraphException("Unknown delegation order"); + } + } + final ClassLoaderHandler classLoaderHandler = ClassLoaderHandlerRegistry.FALLBACK_CLASS_LOADER_HANDLER + .instantiate(log); + if (classLoaderHandler == null) { + // Should not happen + throw new RuntimeException("Could not instantiate fallback ClassLoaderHandler"); + } + if (log != null) { + log.log("ClassLoader " + classLoader.getClass().getName() + " will be handled by " + + classLoaderHandler.getClass().getName()); + } + return classLoaderHandler; + } + + /** + * Perform a topological sort on classloader delegation ordering constraints. + * + * @param curr + * the current classloader + * @param visited + * the visited classloaders + * @param orderingConstraints + * the ordering constraints + * @param orderOut + * the order out + */ + private static void topoSort(final ClassLoader curr, final Set visited, + final Map> orderingConstraints, final Deque orderOut) { + if (visited.add(curr)) { + final List downstreamNodes = orderingConstraints.get(curr); + if (downstreamNodes != null) { + for (final ClassLoader downstreamNode : downstreamNodes) { + topoSort(downstreamNode, visited, orderingConstraints, orderOut); } } + orderOut.push(curr); } - return false; } /** - * Recursively find the ClassLoaderHandler that can handle each ClassLoader and its parent(s), correctly - * observing parent delegation order (PARENT_FIRST or PARENT_LAST). + * Add an ordering constraint between two classloaders. + * + * @param processFirst + * the first classloader to try loading a class from + * @param processSecond + * the second classloader to try loading a class from + * @param orderingConstraints + * the ordering constraints + * @param allDownstreamClassLoaders + * all downstream class loaders + */ + private static void addOrderingConstraint(final ClassLoader processFirst, final ClassLoader processSecond, + final Map> orderingConstraints, + final Set allDownstreamClassLoaders) { + List immediateDownstreamClassLoaders = orderingConstraints.get(processFirst); + if (immediateDownstreamClassLoaders == null) { + orderingConstraints.put(processFirst, immediateDownstreamClassLoaders = new ArrayList<>()); + } + immediateDownstreamClassLoaders.add(processSecond); + allDownstreamClassLoaders.add(processSecond); + } + + /** + * Recursively find the {@link ClassLoaderHandler} that can handle each ClassLoader and its parent(s), + * respecting parent delegation order (PARENT_FIRST or PARENT_LAST). * * @param scanSpec * the scan spec - * @param classLoader - * the classloader + * @param contextClassLoaders + * the context classloader order * @param foundClassLoaders * the found classloaders * @param allClassLoaderHandlerRegistryEntries - * the all classloader handler registry entries + * the ClassLoaderHandler registry entries * @param classLoaderAndHandlerOrderOut - * the classloader and handler order out + * the classloader and ClassLoaderHandler order * @param ignoredClassLoaderAndHandlerOrderOut * the ignored classloader and handler order out * @param log * the log */ private void findClassLoaderHandlerForClassLoaderAndParents(final ScanSpec scanSpec, - final ClassLoader classLoader, final Set foundClassLoaders, + final ClassLoader[] contextClassLoaders, final Set foundClassLoaders, final List allClassLoaderHandlerRegistryEntries, - final List> classLoaderAndHandlerOrderOut, - final List> ignoredClassLoaderAndHandlerOrderOut, + final List> classLoaderAndHandlerOrderOut, + final List> ignoredClassLoaderAndHandlerOrderOut, final LogNode log) { - // Don't handle ClassLoaders twice (so that any shared parent ClassLoaders get handled only once) - if (foundClassLoaders.add(classLoader)) { - boolean foundMatch = false; - // Iterate through each ClassLoader superclass name - for (Class c = classLoader.getClass(); c != null; c = c.getSuperclass()) { - // Compare against the class names handled by each ClassLoaderHandler - for (final ClassLoaderHandlerRegistryEntry classLoaderHandlerRegistryEntry : // - allClassLoaderHandlerRegistryEntries) { - for (final String handledClassLoaderName : // - classLoaderHandlerRegistryEntry.handledClassLoaderNames) { - if (handledClassLoaderName.equals(c.getName())) { - // This ClassLoaderHandler can handle this class -- instantiate it - if (addClassLoaderHandler(scanSpec, classLoader, classLoaderHandlerRegistryEntry, - foundClassLoaders, allClassLoaderHandlerRegistryEntries, - classLoaderAndHandlerOrderOut, ignoredClassLoaderAndHandlerOrderOut, - new HashSet(), log)) { - foundMatch = true; - } + // Iterate through all classloaders and their ancestors. + // allClassLoaders is a LinkedHashSet so that the order in which nodes are added, when iterating + // depth first starting at contextClassLoaders, is preserved. + final Set allClassLoaders = new LinkedHashSet<>(); + final Map classLoaderToClassLoaderHandler = new HashMap<>(); + final Map> orderingConstraints = new HashMap<>(); + final Set visitedEmbedded = new HashSet<>(); + final Set allParentClassLoaders = new HashSet<>(); + final Set allDownstreamClassLoaders = new HashSet<>(); + for (final ClassLoader classLoader : contextClassLoaders) { + // Iterate through classloader parent hierarchy, trying to find a ClassLoaderHandler that can + // handle the classloader + for (ClassLoader ancestorClassLoader = classLoader; ancestorClassLoader != null; // + ancestorClassLoader = ancestorClassLoader.getParent()) { + // Only process each ancestor classloader once + if (allClassLoaders.add(ancestorClassLoader)) { + // Find ClassLoaderHandler for ancestor classloader (will be fallback handler if none found) + final ClassLoaderHandler classLoaderHandler = findClassLoaderHandler(ancestorClassLoader, + visitedEmbedded, allClassLoaderHandlerRegistryEntries, log); + classLoaderToClassLoaderHandler.put(ancestorClassLoader, classLoaderHandler); + + // Find delegation order of ancestor classloader relative to its parent + final ClassLoader ancestorParent = ancestorClassLoader.getParent(); + if (ancestorParent != null) { + // Record parents + allParentClassLoaders.add(ancestorParent); + + // Build a DAG using the delegation order + switch (classLoaderHandler.getDelegationOrder(ancestorClassLoader)) { + case PARENT_FIRST: + addOrderingConstraint(ancestorParent, ancestorClassLoader, orderingConstraints, + allDownstreamClassLoaders); + break; + case PARENT_LAST: + addOrderingConstraint(ancestorClassLoader, ancestorParent, orderingConstraints, + allDownstreamClassLoaders); break; + default: + // Should not happen + throw new RuntimeException("Invalid delegation order"); } } - if (foundMatch) { - break; - } - } - if (foundMatch) { + } else { + // Already processed this ancestor and all of its ancestors break; } } - if (!foundMatch) { - if (log != null) { - log.log("Could not find a ClassLoaderHandler that can handle " - + classLoader.getClass().getName() + " , trying " - + ClassLoaderHandlerRegistry.FALLBACK_CLASS_LOADER_HANDLER.classLoaderHandlerClass - .getName() - + " instead. Please report this at: " - + "https://github.com/classgraph/classgraph/issues"); - } - addClassLoaderHandler(scanSpec, classLoader, - ClassLoaderHandlerRegistry.FALLBACK_CLASS_LOADER_HANDLER, foundClassLoaders, - allClassLoaderHandlerRegistryEntries, classLoaderAndHandlerOrderOut, - ignoredClassLoaderAndHandlerOrderOut, new HashSet(), log); + } + + // Find all DAG root nodes, which are all nodes that are not downstream of another node + final Set rootClassLoaders = new LinkedHashSet<>(allClassLoaders); + rootClassLoaders.removeAll(allDownstreamClassLoaders); + + // Perform a topological sort on the DAG of parent delegation order constraints, starting at the roots + final Set visited = new HashSet<>(); + final Deque topoSortOrder = new LinkedList<>(); + for (final ClassLoader rootClassLoader : rootClassLoaders) { + topoSort(rootClassLoader, visited, orderingConstraints, topoSortOrder); + } + + // If ignoring parent classloaders, split resulting list into parents and non-parents + for (final ClassLoader classLoader : topoSortOrder) { + final ClassLoaderHandler classLoaderHandler = classLoaderToClassLoaderHandler.get(classLoader); + final Entry ent = new SimpleEntry<>(classLoader, classLoaderHandler); + if (classLoaderHandler == null) { + // Should not happen + throw new RuntimeException("ClassLoaderHandler not found"); + } + if (scanSpec.ignoreParentClassLoaders && allParentClassLoaders.contains(classLoader)) { + ignoredClassLoaderAndHandlerOrderOut.add(ent); + } else { + classLoaderAndHandlerOrderOut.add(ent); } } } @@ -260,6 +349,8 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { + "found by classpath scanning will be the same as the classes loaded by the " + "context classloader"); } + classLoaderOrderRespectingParentDelegation = contextClassLoaders; + } else { // Add rt.jar and/or lib/ext jars to beginning of classpath, if enabled if (jreRtJar != null) { @@ -296,24 +387,23 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { // Find all unique parent ClassLoaders, and put all ClassLoaders into a single order, according to the // delegation order (PARENT_FIRST or PARENT_LAST) - final List> classLoaderAndHandlerOrder = new ArrayList<>(); - final List> ignoredClassLoaderAndHandlerOrder = // + final List> classLoaderAndHandlerOrder = new ArrayList<>(); + final List> ignoredClassLoaderAndHandlerOrder = // new ArrayList<>(); if (contextClassLoaders != null) { - for (final ClassLoader envClassLoader : contextClassLoaders) { - findClassLoaderHandlerForClassLoaderAndParents(scanSpec, envClassLoader, - /* foundClassLoaders = */ new LinkedHashSet(), - ClassLoaderHandlerRegistry.CLASS_LOADER_HANDLERS, classLoaderAndHandlerOrder, - ignoredClassLoaderAndHandlerOrder, classpathFinderLog); - } + findClassLoaderHandlerForClassLoaderAndParents(scanSpec, contextClassLoaders, + /* foundClassLoaders = */ new LinkedHashSet(), + ClassLoaderHandlerRegistry.CLASS_LOADER_HANDLERS, classLoaderAndHandlerOrder, + ignoredClassLoaderAndHandlerOrder, classpathFinderLog); } // Call each ClassLoaderHandler on its corresponding ClassLoader to get the classpath URLs or paths final LogNode classLoaderClasspathLoopLog = classpathFinderLog == null ? null : classpathFinderLog.log("Finding classpath elements in ClassLoaders"); - for (final SimpleEntry classLoaderAndHandler : // - classLoaderAndHandlerOrder) { + final LinkedHashSet classLoaderOrderRespectingParentDelegationSet = new LinkedHashSet<>(); + for (final Entry classLoaderAndHandler : classLoaderAndHandlerOrder) { final ClassLoader classLoader = classLoaderAndHandler.getKey(); + classLoaderOrderRespectingParentDelegationSet.add(classLoader); final ClassLoaderHandler classLoaderHandler = classLoaderAndHandler.getValue(); final LogNode classLoaderClasspathLog = classLoaderClasspathLoopLog == null ? null : classLoaderClasspathLoopLog.log( @@ -326,8 +416,13 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { } } } + // Need to record the proper order in which classloaders should be called, in particular to + // respect parent-last delegation order, since this is not the default (issue #267). + classLoaderOrderRespectingParentDelegation = classLoaderOrderRespectingParentDelegationSet + .toArray(new ClassLoader[0]); + // Repeat the process for ignored parent ClassLoaders - for (final SimpleEntry classLoaderAndHandler : // + for (final Entry classLoaderAndHandler : // ignoredClassLoaderAndHandlerOrder) { final ClassLoader classLoader = classLoaderAndHandler.getKey(); final ClassLoaderHandler classLoaderHandler = classLoaderAndHandler.getValue(); @@ -373,22 +468,4 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { } } } - - /** - * Get the classpath order. - * - * @return The order of raw classpath elements obtained from ClassLoaders. - */ - public ClasspathOrder getClasspathOrder() { - return classpathOrder; - } - - /** - * Get the classloader and module finder. - * - * @return The {@link ClassLoaderAndModuleFinder}. - */ - public ClassLoaderAndModuleFinder getClassLoaderAndModuleFinder() { - return classLoaderAndModuleFinder; - } } diff --git a/src/test/java/io/github/classgraph/issues/issue267/ClassLoadingWorksWithParentLastLoadersStub.java b/src/test/java/io/github/classgraph/issues/issue267/ClassLoadingWorksWithParentLastLoadersStub.java index 947480578..a5f54742c 100644 --- a/src/test/java/io/github/classgraph/issues/issue267/ClassLoadingWorksWithParentLastLoadersStub.java +++ b/src/test/java/io/github/classgraph/issues/issue267/ClassLoadingWorksWithParentLastLoadersStub.java @@ -49,7 +49,7 @@ public class ClassLoadingWorksWithParentLastLoadersStub { * the exception */ @Test - public void sameClassLoaderThatFoundAClassShouldLoadIt() throws Exception { + public void sameClassLoaderThatFoundAClassShouldLoadIt() throws Throwable { final String currentClassLoadersName = Thread.currentThread().getContextClassLoader().getClass() .getSimpleName(); @@ -59,6 +59,9 @@ public void sameClassLoaderThatFoundAClassShouldLoadIt() throws Exception { final TestLauncher launcher = new TestLauncher(currentClassLoadersName); launcher.start(); launcher.join(); + if (launcher.thrown != null) { + throw launcher.thrown; + } } } @@ -66,6 +69,8 @@ class TestLauncher extends Thread { private final String parentClassLoader; + Throwable thrown; + TestLauncher(final String parentClassLoader) { this.parentClassLoader = parentClassLoader; setDaemon(false); @@ -81,8 +86,8 @@ public void run() { String.class); mainMethod.invoke(mainClass.getDeclaredConstructor().newInstance(), parentClassLoader, "FakeRestartClassLoader"); - } catch (final Throwable ex) { - getUncaughtExceptionHandler().uncaughtException(this, ex); + } catch (final Throwable t) { + thrown = t; } } } From 594b73a7bd507b74648f6b7e53f0c80a9769c73d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 21 Mar 2019 05:17:36 -0600 Subject: [PATCH 0042/1778] [maven-release-plugin] prepare release classgraph-4.8.17 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 695840bb6..86ca70dcd 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.17-SNAPSHOT + 4.8.17 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.17 From e7ded798c7aabdae1e8bccdf07c100b6478292f5 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 21 Mar 2019 05:17:42 -0600 Subject: [PATCH 0043/1778] [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 86ca70dcd..0c6f01e41 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.17 + 4.8.18-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.17 + HEAD From 6625b6419725f7b467ec46679d31cea9484c3338 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 21 Mar 2019 05:22:29 -0600 Subject: [PATCH 0044/1778] Try system classloader after thread classloader (#331) --- .../classpath/ClassLoaderAndModuleFinder.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderAndModuleFinder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderAndModuleFinder.java index c8c2c045b..87ae75024 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderAndModuleFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderAndModuleFinder.java @@ -270,18 +270,18 @@ private static List findModuleRefs(final Class[] callStack, final classLoadersUnique.add(currClassClassLoader); } - // Get system classloader - final ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); - if (systemClassLoader != null) { - classLoadersUnique.add(systemClassLoader); - } - // Get thread classloader final ClassLoader threadClassLoader = Thread.currentThread().getContextClassLoader(); if (threadClassLoader != null) { classLoadersUnique.add(threadClassLoader); } + // Get system classloader + final ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); + if (systemClassLoader != null) { + classLoadersUnique.add(systemClassLoader); + } + // There is one more classloader in JDK9+, the platform classloader (used for handling extensions), // see: http://openjdk.java.net/jeps/261#Class-loaders // The method call to get it is ClassLoader.getPlatformClassLoader() From dfdf42b95b94062d66d2079863047be0ce6ce926 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 21 Mar 2019 05:29:32 -0600 Subject: [PATCH 0045/1778] Check thread classloader before class' classloader --- .../classpath/ClassLoaderAndModuleFinder.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderAndModuleFinder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderAndModuleFinder.java index 87ae75024..dba368a9c 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderAndModuleFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderAndModuleFinder.java @@ -263,19 +263,19 @@ private static List findModuleRefs(final Class[] callStack, final // those cases are ill-defined -- see: // http://www.javaworld.com/article/2077344/core-java/find-a-way-out-of-the-classloader-maze.html?page=2 - // Get context classloader (this is the classloader used by Class.forName(className)) + // Get thread context classloader classLoadersUnique = new LinkedHashSet<>(); - final ClassLoader currClassClassLoader = getClass().getClassLoader(); - if (currClassClassLoader != null) { - classLoadersUnique.add(currClassClassLoader); - } - - // Get thread classloader final ClassLoader threadClassLoader = Thread.currentThread().getContextClassLoader(); if (threadClassLoader != null) { classLoadersUnique.add(threadClassLoader); } + // Get classloader for this class (this is the classloader used by Class.forName(className)) + final ClassLoader currClassClassLoader = getClass().getClassLoader(); + if (currClassClassLoader != null) { + classLoadersUnique.add(currClassClassLoader); + } + // Get system classloader final ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); if (systemClassLoader != null) { From 40d6d27686a42b233b519fb6d6402392e95b7a49 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 21 Mar 2019 05:33:55 -0600 Subject: [PATCH 0046/1778] Remove unused param --- .../io/github/classgraph/classpath/ClasspathFinder.java | 5 +---- 1 file changed, 1 insertion(+), 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 e3cd30b48..efe52d3ec 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java @@ -215,8 +215,6 @@ private static void addOrderingConstraint(final ClassLoader processFirst, final * the scan spec * @param contextClassLoaders * the context classloader order - * @param foundClassLoaders - * the found classloaders * @param allClassLoaderHandlerRegistryEntries * the ClassLoaderHandler registry entries * @param classLoaderAndHandlerOrderOut @@ -227,7 +225,7 @@ private static void addOrderingConstraint(final ClassLoader processFirst, final * the log */ private void findClassLoaderHandlerForClassLoaderAndParents(final ScanSpec scanSpec, - final ClassLoader[] contextClassLoaders, final Set foundClassLoaders, + final ClassLoader[] contextClassLoaders, final List allClassLoaderHandlerRegistryEntries, final List> classLoaderAndHandlerOrderOut, final List> ignoredClassLoaderAndHandlerOrderOut, @@ -392,7 +390,6 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { new ArrayList<>(); if (contextClassLoaders != null) { findClassLoaderHandlerForClassLoaderAndParents(scanSpec, contextClassLoaders, - /* foundClassLoaders = */ new LinkedHashSet(), ClassLoaderHandlerRegistry.CLASS_LOADER_HANDLERS, classLoaderAndHandlerOrder, ignoredClassLoaderAndHandlerOrder, classpathFinderLog); } From 5cda1f5d347f17d18df5500d4a3b41c744c3047c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 21 Mar 2019 08:21:29 -0600 Subject: [PATCH 0047/1778] ClassLoader ordering robustness fixes (#311) --- .../classpath/ClassLoaderAndModuleFinder.java | 83 ++++++++++--------- .../classgraph/classpath/ClasspathFinder.java | 4 +- 2 files changed, 47 insertions(+), 40 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderAndModuleFinder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderAndModuleFinder.java index dba368a9c..0df9cf8f7 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderAndModuleFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderAndModuleFinder.java @@ -132,7 +132,7 @@ private static void findLayerOrder(final Object /* ModuleLayer */ layer, * the log * @return the list */ - private static List findModuleRefs(final List layers, final ScanSpec scanSpec, + private static List findModuleRefs(final LinkedHashSet layers, final ScanSpec scanSpec, final LogNode log) { if (layers.isEmpty()) { return Collections.emptyList(); @@ -209,18 +209,20 @@ private static List findModuleRefs(final List layers, final S * the log * @return the list */ - private static List findModuleRefs(final Class[] callStack, final ScanSpec scanSpec, + private static List findModuleRefsFromCallstack(final Class[] callStack, final ScanSpec scanSpec, final LogNode log) { - final List layers = new ArrayList<>(); - for (final Class stackFrameClass : callStack) { - final Object /* Module */ module = ReflectionUtils.invokeMethod(stackFrameClass, "getModule", - /* throwException = */ false); - if (module != null) { - final Object /* ModuleLayer */ layer = ReflectionUtils.invokeMethod(module, "getLayer", - /* throwException = */ true); - // getLayer() returns null for unnamed modules -- we have to get their classes from java.class.path - if (layer != null) { - layers.add(layer); + final LinkedHashSet layers = new LinkedHashSet<>(); + if (callStack != null) { + for (final Class stackFrameClass : callStack) { + final Object /* Module */ module = ReflectionUtils.invokeMethod(stackFrameClass, "getModule", + /* throwException = */ false); + if (module != null) { + final Object /* ModuleLayer */ layer = ReflectionUtils.invokeMethod(module, "getLayer", + /* throwException = */ true); + // getLayer() returns null for unnamed modules -- have to get their classes from java.class.path + if (layer != null) { + layers.add(layer); + } } } } @@ -257,26 +259,27 @@ private static List findModuleRefs(final Class[] callStack, final if (scanSpec.overrideClassLoaders == null) { // ClassLoaders were not overridden - // Add the ClassLoaders in the order system, caller, context; then remove any of them that are - // parents/ancestors of one or more other classloaders (performed below). There will generally only be - // one class left after this. In rare cases, you may have a separate callerLoader and contextLoader, but - // those cases are ill-defined -- see: + // There's some advice here about choosing the best or the right classloader, but it is not complete + // (e.g. it doesn't cover parent delegation modes): // http://www.javaworld.com/article/2077344/core-java/find-a-way-out-of-the-classloader-maze.html?page=2 - // Get thread context classloader + // Get thread context classloader (this is the first classloader to try, since a context classloader + // can be set as an override on a per-thread basis) classLoadersUnique = new LinkedHashSet<>(); final ClassLoader threadClassLoader = Thread.currentThread().getContextClassLoader(); if (threadClassLoader != null) { classLoadersUnique.add(threadClassLoader); } - // Get classloader for this class (this is the classloader used by Class.forName(className)) + // Get classloader for this class, which will generally be the classloader of the class that + // called ClassGraph (the classloader of the caller is used by Class.forName(className), when + // no classloader is provided) final ClassLoader currClassClassLoader = getClass().getClassLoader(); if (currClassClassLoader != null) { classLoadersUnique.add(currClassClassLoader); } - // Get system classloader + // Get system classloader (this is a fallback if one of the above do not work) final ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); if (systemClassLoader != null) { classLoadersUnique.add(systemClassLoader); @@ -287,26 +290,30 @@ private static List findModuleRefs(final Class[] callStack, final // The method call to get it is ClassLoader.getPlatformClassLoader() // However, since it's not possible to get URLs from this classloader, and it is the parent of // the application classloader returned by ClassLoader.getSystemClassLoader() (so is delegated to - // by the application classloader), there is no point adding it here. + // by the application classloader), there is no point adding it here. Modules are scanned + // directly anyway, so we don't need to get module path entries from the platform classloader. - List allModuleRefsList = null; - if (scanSpec.overrideModuleLayers == null) { - try { - // Find classloaders for classes on callstack - final Class[] callStack = CallStackReader.getClassContext(log); - for (int i = callStack.length - 1; i >= 0; --i) { - final ClassLoader callerClassLoader = callStack[i].getClassLoader(); - if (callerClassLoader != null) { - classLoadersUnique.add(callerClassLoader); - } - } - // Find module references for classes on callstack (for JDK9+) - allModuleRefsList = findModuleRefs(callStack, scanSpec, log); - } catch (final IllegalArgumentException e) { - if (log != null) { - log.log("Could not get call stack", e); + // Find classloaders for classes on callstack, in case any were missed + Class[] callStack = null; + try { + callStack = CallStackReader.getClassContext(log); + for (int i = callStack.length - 1; i >= 0; --i) { + final ClassLoader callerClassLoader = callStack[i].getClassLoader(); + if (callerClassLoader != null) { + classLoadersUnique.add(callerClassLoader); } } + } catch (final IllegalArgumentException e) { + if (log != null) { + log.log("Could not get call stack", e); + } + } + + // Get the module resolution order + List allModuleRefsList = null; + if (scanSpec.overrideModuleLayers == null) { + // Find module references for classes on callstack, and from system (for JDK9+) + allModuleRefsList = findModuleRefsFromCallstack(callStack, scanSpec, log); } else { if (log != null) { final LogNode subLog = log.log("Overriding module layers"); @@ -314,9 +321,9 @@ private static List findModuleRefs(final Class[] callStack, final subLog.log(moduleLayer.toString()); } } - allModuleRefsList = findModuleRefs(scanSpec.overrideModuleLayers, scanSpec, log); + allModuleRefsList = findModuleRefs(new LinkedHashSet<>(scanSpec.overrideModuleLayers), scanSpec, + log); } - if (allModuleRefsList != null) { // Split modules into system modules and non-system modules systemModuleRefs = new ArrayList<>(); 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 efe52d3ec..3843836d4 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java @@ -29,12 +29,12 @@ package nonapi.io.github.classgraph.classpath; import java.util.AbstractMap.SimpleEntry; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -285,7 +285,7 @@ private void findClassLoaderHandlerForClassLoaderAndParents(final ScanSpec scanS // Perform a topological sort on the DAG of parent delegation order constraints, starting at the roots final Set visited = new HashSet<>(); - final Deque topoSortOrder = new LinkedList<>(); + final Deque topoSortOrder = new ArrayDeque<>(); for (final ClassLoader rootClassLoader : rootClassLoaders) { topoSort(rootClassLoader, visited, orderingConstraints, topoSortOrder); } From 6d28b96801d87059ff6b0c96f5cc9027cf6a9c19 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 21 Mar 2019 08:22:29 -0600 Subject: [PATCH 0048/1778] [maven-release-plugin] prepare release classgraph-4.8.18 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 0c6f01e41..96b2987db 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.18-SNAPSHOT + 4.8.18 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.18 From 228f877ee29c12ec4d5b3599b4070198d4b19abd Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 21 Mar 2019 08:22:36 -0600 Subject: [PATCH 0049/1778] [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 96b2987db..e8dab5d71 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.18 + 4.8.19-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.18 + HEAD From 39f8bb69b8f79471518663b6b951b67734b3e06d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 21 Mar 2019 21:37:58 -0600 Subject: [PATCH 0050/1778] Call findLoadedClass() at beginning of findClass() --- src/main/java/io/github/classgraph/ClassGraphClassLoader.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java index 30c42e631..1ca798015 100644 --- a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java +++ b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java @@ -60,6 +60,10 @@ class ClassGraphClassLoader extends ClassLoader { @Override protected Class findClass(final String className) throws ClassNotFoundException, LinkageError, SecurityException { + final Class loadedClass = findLoadedClass(className); + if (loadedClass != null) { + return loadedClass; + } // Get ClassInfo for named class final ClassInfo classInfo = scanResult.getClassInfo(className); From 6de2b60643896fefcb6dd3e96d53eb685b013503 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 22 Mar 2019 04:53:05 -0600 Subject: [PATCH 0051/1778] Preload classes needed by shutdown hook (#331) --- .../java/io/github/classgraph/ScanResult.java | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index a511f7ceb..1484ce936 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -30,10 +30,17 @@ import java.io.Closeable; import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.RandomAccessFile; import java.lang.ref.WeakReference; import java.net.MalformedURLException; import java.net.URI; +import java.net.URISyntaxException; import java.net.URL; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.file.Files; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -206,6 +213,64 @@ public void run() { } } }); + + // 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. Otherwise, the classloader may be + // closed by its own shutdown hook before ClassGraph's shutdown hook can run, and classloading can + // fail, which will throw an exception and leave resources open (#331). + // 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. + File tempFile = null; + boolean createdTempFile = false; + try { + tempFile = Files.createTempFile("ClassGraph", "").toFile(); + tempFile.deleteOnExit(); + try (PrintWriter printWriter = new PrintWriter(tempFile.getName())) { + printWriter.print("temp"); + createdTempFile = true; + } + } catch (final IOException e1) { + // Could not create temp file + } + if (!createdTempFile) { + // Could not create temp file (system does not have a writeable filesystem?). + // Instead, try opening items on java.class.path until one is found that can be opened. + final String classpath = System.getProperty("java.class.path"); + if (classpath == null) { + // Should not happen -- one of these options should work + throw new RuntimeException("Could not create temp file, and could not read java.class.path"); + } + boolean foundReadableClasspathEntry = false; + for (final String classpathEntry : JarUtils.smartPathSplit(classpath)) { + try { + tempFile = new File(new URI(classpathEntry)); + } catch (final URISyntaxException e2) { + continue; + } + if (FileUtils.canRead(tempFile)) { + // Found a readable file + foundReadableClasspathEntry = true; + break; + } + } + if (!foundReadableClasspathEntry) { + // Should not happen -- one of these options should work + throw new RuntimeException( + "Could not create temp file, and could not read a file from java.class.path"); + } + } + MappedByteBuffer buffer = null; + try (RandomAccessFile raf = new RandomAccessFile(tempFile, "r"); + FileChannel fileChannel = raf.getChannel()) { + buffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size()); + } catch (final IOException | OutOfMemoryError e) { + throw new RuntimeException("Could not open file", e); + } + // Preload classes needed by shutdown hook + FileUtils.closeDirectByteBuffer(buffer, /* log = */ null); + if (createdTempFile) { + tempFile.delete(); + } } // ------------------------------------------------------------------------------------------------------------- From 0da39b1f6a030225d3ec597ee086131db51a3925 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 22 Mar 2019 05:11:52 -0600 Subject: [PATCH 0052/1778] Be more robust to failure to call cleaner --- .../io/github/classgraph/utils/FileUtils.java | 33 +++++++++++++++++-- 1 file changed, 30 insertions(+), 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 adbdbf755..0c9215d33 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -508,9 +508,36 @@ private static boolean closeDirectByteBufferPrivileged(final ByteBuffer byteBuff } // Invoke ((DirectBuffer) byteBuffer).cleaner().clean() final Method cleaner = byteBuffer.getClass().getMethod("cleaner"); - cleaner.setAccessible(true); - cleanMethod.invoke(cleaner.invoke(byteBuffer)); - return true; + if (cleaner == null) { + if (log != null) { + log.log("Could not unmap ByteBuffer, cleaner == null"); + } + return false; + } + try { + cleaner.setAccessible(true); + } catch (final Exception e) { + if (log != null) { + log.log("Could not unmap ByteBuffer, cleaner.setAccessible(true) failed"); + } + return false; + } + final Object cleanerResult = cleaner.invoke(byteBuffer); + if (cleanerResult == null) { + if (log != null) { + log.log("Could not unmap ByteBuffer, cleanerResult == null"); + } + return false; + } + try { + cleanMethod.invoke(cleaner.invoke(byteBuffer)); + return true; + } catch (final Exception e) { + if (log != null) { + log.log("Could not unmap ByteBuffer, cleanMethod.invoke(cleanerResult) failed: " + e); + } + return false; + } } else { if (theUnsafe == null) { if (log != null) { From fcd0f47ede4bce96656bd31cc47b6a929d585c32 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 22 Mar 2019 05:40:57 -0600 Subject: [PATCH 0053/1778] Fix previous commit --- src/main/java/io/github/classgraph/ScanResult.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index 1484ce936..740f34b0a 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -223,9 +223,10 @@ public void run() { File tempFile = null; boolean createdTempFile = false; try { - tempFile = Files.createTempFile("ClassGraph", "").toFile(); + tempFile = File.createTempFile("ClassGraph-", "-temp"); tempFile.deleteOnExit(); - try (PrintWriter printWriter = new PrintWriter(tempFile.getName())) { + System.err.println("Created temp file " + tempFile); + try (PrintWriter printWriter = new PrintWriter(tempFile)) { printWriter.print("temp"); createdTempFile = true; } @@ -269,7 +270,11 @@ public void run() { // Preload classes needed by shutdown hook FileUtils.closeDirectByteBuffer(buffer, /* log = */ null); if (createdTempFile) { - tempFile.delete(); + try { + Files.delete(tempFile.toPath()); + } catch (final IOException | SecurityException e) { + // Failed + } } } From e6568f3e7c8eb582303da621680549f9f560428b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 22 Mar 2019 05:52:54 -0600 Subject: [PATCH 0054/1778] Check if classpath is overridden before rejecting rt.jar (@jechlin) --- .../classgraph/ClassGraphException.java | 2 + .../java/io/github/classgraph/ClassInfo.java | 2 + .../java/io/github/classgraph/Resource.java | 2 + .../io/github/classgraph/WhiteBlackList.java | 8 + .../AntClassLoaderHandler.java | 7 +- .../ClassLoaderHandlerRegistry.java | 1 + .../EquinoxClassLoaderHandler.java | 27 ++-- ...quinoxContextFinderClassLoaderHandler.java | 4 +- .../FallbackClassLoaderHandler.java | 84 +++++------ .../FelixClassLoaderHandler.java | 15 +- .../JBossClassLoaderHandler.java | 20 ++- .../JPMSClassLoaderHandler.java | 3 +- .../OSGiDefaultClassLoaderHandler.java | 5 +- ...DelegationOrderTestClassLoaderHandler.java | 5 +- ...assWorldsClassRealmClassLoaderHandler.java | 139 ++++++++++++++++++ .../TomcatWebappClassLoaderBaseHandler.java | 11 +- .../URLClassLoaderHandler.java | 5 +- .../WeblogicClassLoaderHandler.java | 9 +- .../WebsphereLibertyClassLoaderHandler.java | 5 +- ...ebsphereTraditionalClassLoaderHandler.java | 5 +- .../classgraph/classpath/ClasspathFinder.java | 5 +- .../classgraph/classpath/ClasspathOrder.java | 37 +++-- .../classgraph/concurrency/SingletonMap.java | 4 +- .../features/MethodParameterAnnotations.java | 14 ++ .../classgraph/features/MultiReleaseJar.java | 6 + .../issues/issue128/Issue128Test.java | 4 +- ...LoadingWorksWithParentLastLoadersStub.java | 5 +- .../classgraph/issues/issue289/Issue289.java | 4 + .../classgraph/issues/issue305/Issue305.java | 8 +- .../classgraph/test/ClassGraphTest.java | 3 + 30 files changed, 341 insertions(+), 108 deletions(-) create mode 100644 src/main/java/nonapi/io/github/classgraph/classloaderhandler/PlexusClassWorldsClassRealmClassLoaderHandler.java diff --git a/src/main/java/io/github/classgraph/ClassGraphException.java b/src/main/java/io/github/classgraph/ClassGraphException.java index 5b7ef26e8..ef6f76d8e 100644 --- a/src/main/java/io/github/classgraph/ClassGraphException.java +++ b/src/main/java/io/github/classgraph/ClassGraphException.java @@ -81,6 +81,8 @@ public static ClassGraphException newClassGraphException(final String message) { * @param cause * the cause * @return the ClassGraphException + * @throws ClassGraphException + * the class graph exception */ public static ClassGraphException newClassGraphException(final String message, final Throwable cause) throws ClassGraphException { diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index fff3b966e..de7ac0599 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -905,6 +905,8 @@ public String getName() { * the whole string if the class is in the root package. (Note that this is not the same as the result of * {@link Class#getSimpleName()}, which returns "" for anonymous classes.) * + * @param className + * the class name * @return The simple name of the class. */ static String getSimpleName(final String className) { diff --git a/src/main/java/io/github/classgraph/Resource.java b/src/main/java/io/github/classgraph/Resource.java index cb735ca83..861fcb408 100644 --- a/src/main/java/io/github/classgraph/Resource.java +++ b/src/main/java/io/github/classgraph/Resource.java @@ -309,6 +309,8 @@ protected void markAsClosed() { /** * Convert a URI to URL, catching "jrt:" URIs as invalid. * + * @param uri + * the uri * @return the URL. * @throws IllegalArgumentException * if the URI could not be converted to a URL, or the URI had "jrt:" scheme. diff --git a/src/main/java/nonapi/io/github/classgraph/WhiteBlackList.java b/src/main/java/nonapi/io/github/classgraph/WhiteBlackList.java index 6bc24a807..31178071d 100644 --- a/src/main/java/nonapi/io/github/classgraph/WhiteBlackList.java +++ b/src/main/java/nonapi/io/github/classgraph/WhiteBlackList.java @@ -567,6 +567,14 @@ void sortPrefixes() { } } + /** + * Quote list. + * + * @param coll + * the coll + * @param buf + * the buf + */ private static void quoteList(final Collection coll, final StringBuilder buf) { buf.append('['); boolean first = true; 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 578170575..36abf7d57 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/AntClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/AntClassLoaderHandler.java @@ -61,12 +61,15 @@ public DelegationOrder getDelegationOrder(final ClassLoader classLoaderInstance) } /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handle(nonapi.io.github.classgraph.ScanSpec, java.lang.ClassLoader, nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) + * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handle( + * nonapi.io.github.classgraph.ScanSpec, java.lang.ClassLoader, + * nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) */ @Override public void handle(final ScanSpec scanSpec, final ClassLoader classLoader, final ClasspathOrder classpathOrderOut, final LogNode log) { classpathOrderOut.addClasspathEntries( // - (String) ReflectionUtils.invokeMethod(classLoader, "getClasspath", false), classLoader, log); + (String) ReflectionUtils.invokeMethod(classLoader, "getClasspath", false), classLoader, scanSpec, + log); } } 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 ff8912423..c28804106 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(PlexusClassWorldsClassRealmClassLoaderHandler.class), // For unit testing of PARENT_LAST delegation order new ClassLoaderHandlerRegistryEntry(ParentLastDelegationOrderTestClassLoaderHandler.class), 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 cfbb31690..5ea1bb0bd 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxClassLoaderHandler.java @@ -87,11 +87,13 @@ public DelegationOrder getDelegationOrder(final ClassLoader classLoaderInstance) * the classloader * @param classpathOrderOut * the classpath order + * @param scanSpec + * the scan spec * @param log * the log */ private void addBundleFile(final Object bundlefile, final Set path, final ClassLoader classLoader, - final ClasspathOrder classpathOrderOut, final LogNode log) { + final ClasspathOrder classpathOrderOut, ScanSpec scanSpec, final LogNode log) { // Don't get stuck in infinite loop if (bundlefile != null && path.add(bundlefile)) { // type File @@ -104,21 +106,21 @@ private void addBundleFile(final Object bundlefile, final Set path, fina if (foundClassPathElement) { // We found the base file and a classpath element, e.g. "bin/" classpathOrderOut.addClasspathEntry(basefile.toString() + "/" + fieldVal.toString(), - classLoader, log); + classLoader, scanSpec, log); break; } } if (!foundClassPathElement) { // No classpath element found, just use basefile - classpathOrderOut.addClasspathEntry(basefile.toString(), classLoader, log); + classpathOrderOut.addClasspathEntry(basefile.toString(), classLoader, scanSpec, log); } } addBundleFile(ReflectionUtils.getFieldVal(bundlefile, "wrapped", false), path, classLoader, - classpathOrderOut, log); + classpathOrderOut, scanSpec, log); addBundleFile(ReflectionUtils.getFieldVal(bundlefile, "next", false), path, classLoader, - classpathOrderOut, log); + classpathOrderOut, scanSpec, log); } } @@ -131,11 +133,13 @@ private void addBundleFile(final Object bundlefile, final Set path, fina * the class loader * @param classpathOrderOut * the classpath order out + * @param scanSpec + * the scan spec * @param log * the log */ private void addClasspathEntries(final Object owner, final ClassLoader classLoader, - final ClasspathOrder classpathOrderOut, final LogNode log) { + final ClasspathOrder classpathOrderOut, ScanSpec scanSpec, final LogNode log) { // type ClasspathEntry[] final Object entries = ReflectionUtils.getFieldVal(owner, "entries", false); if (entries != null) { @@ -144,21 +148,22 @@ private void addClasspathEntries(final Object owner, final ClassLoader classLoad final Object entry = Array.get(entries, i); // type BundleFile final Object bundlefile = ReflectionUtils.getFieldVal(entry, "bundlefile", false); - addBundleFile(bundlefile, new HashSet<>(), classLoader, classpathOrderOut, log); + addBundleFile(bundlefile, new HashSet<>(), classLoader, classpathOrderOut, scanSpec, log); } } } /* (non-Javadoc) * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handle( - * nonapi.io.github.classgraph.ScanSpec, java.lang.ClassLoader, nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) + * nonapi.io.github.classgraph.ScanSpec, java.lang.ClassLoader, + * nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) */ @Override public void handle(final ScanSpec scanSpec, final ClassLoader classLoader, final ClasspathOrder classpathOrderOut, final LogNode log) { // type ClasspathManager final Object manager = ReflectionUtils.getFieldVal(classLoader, "manager", false); - addClasspathEntries(manager, classLoader, classpathOrderOut, log); + addClasspathEntries(manager, classLoader, classpathOrderOut, scanSpec, log); // type FragmentClasspath[] final Object fragments = ReflectionUtils.getFieldVal(manager, "fragments", false); @@ -166,7 +171,7 @@ public void handle(final ScanSpec scanSpec, final ClassLoader classLoader, for (int f = 0, fragLength = Array.getLength(fragments); f < fragLength; f++) { // type FragmentClasspath final Object fragment = Array.get(fragments, f); - addClasspathEntries(fragment, classLoader, classpathOrderOut, log); + addClasspathEntries(fragment, classLoader, classpathOrderOut, scanSpec, log); } } // Only read system bundles once (all bundles should give the same results for this). We assume there is @@ -204,7 +209,7 @@ public void handle(final ScanSpec scanSpec, final ClassLoader classLoader, final int fileIdx = location.indexOf("file:"); if (fileIdx >= 0) { location = location.substring(fileIdx); - classpathOrderOut.addClasspathEntry(location, classLoader, log); + classpathOrderOut.addClasspathEntry(location, classLoader, scanSpec, log); } } } 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 6ee5c5a82..00c80f6f2 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxContextFinderClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxContextFinderClassLoaderHandler.java @@ -62,7 +62,9 @@ public DelegationOrder getDelegationOrder(final ClassLoader classLoaderInstance) } /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handle(nonapi.io.github.classgraph.ScanSpec, java.lang.ClassLoader, nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) + * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handle( + * nonapi.io.github.classgraph.ScanSpec, java.lang.ClassLoader, + * nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) */ @Override public void handle(final ScanSpec scanSpec, final ClassLoader classLoader, 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 7df59037e..1bcafc024 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FallbackClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FallbackClassLoaderHandler.java @@ -64,92 +64,94 @@ public DelegationOrder getDelegationOrder(final ClassLoader classLoaderInstance) } /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handle(nonapi.io.github.classgraph.ScanSpec, java.lang.ClassLoader, nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) + * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handle( + * nonapi.io.github.classgraph.ScanSpec, java.lang.ClassLoader, + * nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) */ @Override public void handle(final ScanSpec scanSpec, final ClassLoader classLoader, final ClasspathOrder classpathOrderOut, final LogNode log) { boolean valid = false; valid |= classpathOrderOut.addClasspathEntryObject( - ReflectionUtils.invokeMethod(classLoader, "getClassPath", false), classLoader, log); + ReflectionUtils.invokeMethod(classLoader, "getClassPath", false), classLoader, scanSpec, log); valid |= classpathOrderOut.addClasspathEntryObject( - ReflectionUtils.invokeMethod(classLoader, "getClasspath", false), classLoader, log); + ReflectionUtils.invokeMethod(classLoader, "getClasspath", false), classLoader, scanSpec, log); valid |= classpathOrderOut.addClasspathEntryObject( - ReflectionUtils.invokeMethod(classLoader, "classpath", false), classLoader, log); + ReflectionUtils.invokeMethod(classLoader, "classpath", false), classLoader, scanSpec, log); valid |= classpathOrderOut.addClasspathEntryObject( - ReflectionUtils.invokeMethod(classLoader, "classPath", false), classLoader, log); + ReflectionUtils.invokeMethod(classLoader, "classPath", false), classLoader, scanSpec, log); valid |= classpathOrderOut.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "cp", false), - classLoader, log); + classLoader, scanSpec, log); valid |= classpathOrderOut.addClasspathEntryObject( - ReflectionUtils.getFieldVal(classLoader, "classpath", false), classLoader, log); + ReflectionUtils.getFieldVal(classLoader, "classpath", false), classLoader, scanSpec, log); valid |= classpathOrderOut.addClasspathEntryObject( - ReflectionUtils.getFieldVal(classLoader, "classPath", false), classLoader, log); + ReflectionUtils.getFieldVal(classLoader, "classPath", false), classLoader, scanSpec, log); valid |= classpathOrderOut.addClasspathEntryObject(ReflectionUtils.getFieldVal(classLoader, "cp", false), - classLoader, log); + classLoader, scanSpec, log); valid |= classpathOrderOut.addClasspathEntryObject( - ReflectionUtils.invokeMethod(classLoader, "getPath", false), classLoader, log); + ReflectionUtils.invokeMethod(classLoader, "getPath", false), classLoader, scanSpec, log); valid |= classpathOrderOut.addClasspathEntryObject( - ReflectionUtils.invokeMethod(classLoader, "getPaths", false), classLoader, log); + ReflectionUtils.invokeMethod(classLoader, "getPaths", false), classLoader, scanSpec, log); valid |= classpathOrderOut.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "path", false), - classLoader, log); + classLoader, scanSpec, log); valid |= classpathOrderOut.addClasspathEntryObject( - ReflectionUtils.invokeMethod(classLoader, "paths", false), classLoader, log); + ReflectionUtils.invokeMethod(classLoader, "paths", false), classLoader, scanSpec, log); valid |= classpathOrderOut.addClasspathEntryObject(ReflectionUtils.getFieldVal(classLoader, "paths", false), - classLoader, log); + classLoader, scanSpec, log); valid |= classpathOrderOut.addClasspathEntryObject(ReflectionUtils.getFieldVal(classLoader, "paths", false), - classLoader, log); + classLoader, scanSpec, log); valid |= classpathOrderOut.addClasspathEntryObject( - ReflectionUtils.invokeMethod(classLoader, "getDir", false), classLoader, log); + ReflectionUtils.invokeMethod(classLoader, "getDir", false), classLoader, scanSpec, log); valid |= classpathOrderOut.addClasspathEntryObject( - ReflectionUtils.invokeMethod(classLoader, "getDirs", false), classLoader, log); + ReflectionUtils.invokeMethod(classLoader, "getDirs", false), classLoader, scanSpec, log); valid |= classpathOrderOut.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "dir", false), - classLoader, log); + classLoader, scanSpec, log); valid |= classpathOrderOut.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "dirs", false), - classLoader, log); + classLoader, scanSpec, log); valid |= classpathOrderOut.addClasspathEntryObject(ReflectionUtils.getFieldVal(classLoader, "dir", false), - classLoader, log); + classLoader, scanSpec, log); valid |= classpathOrderOut.addClasspathEntryObject(ReflectionUtils.getFieldVal(classLoader, "dirs", false), - classLoader, log); + classLoader, scanSpec, log); valid |= classpathOrderOut.addClasspathEntryObject( - ReflectionUtils.invokeMethod(classLoader, "getFile", false), classLoader, log); + ReflectionUtils.invokeMethod(classLoader, "getFile", false), classLoader, scanSpec, log); valid |= classpathOrderOut.addClasspathEntryObject( - ReflectionUtils.invokeMethod(classLoader, "getFiles", false), classLoader, log); + ReflectionUtils.invokeMethod(classLoader, "getFiles", false), classLoader, scanSpec, log); valid |= classpathOrderOut.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "file", false), - classLoader, log); + classLoader, scanSpec, log); valid |= classpathOrderOut.addClasspathEntryObject( - ReflectionUtils.invokeMethod(classLoader, "files", false), classLoader, log); + ReflectionUtils.invokeMethod(classLoader, "files", false), classLoader, scanSpec, log); valid |= classpathOrderOut.addClasspathEntryObject(ReflectionUtils.getFieldVal(classLoader, "file", false), - classLoader, log); + classLoader, scanSpec, log); valid |= classpathOrderOut.addClasspathEntryObject(ReflectionUtils.getFieldVal(classLoader, "files", false), - classLoader, log); + classLoader, scanSpec, log); valid |= classpathOrderOut.addClasspathEntryObject( - ReflectionUtils.invokeMethod(classLoader, "getJar", false), classLoader, log); + ReflectionUtils.invokeMethod(classLoader, "getJar", false), classLoader, scanSpec, log); valid |= classpathOrderOut.addClasspathEntryObject( - ReflectionUtils.invokeMethod(classLoader, "getJars", false), classLoader, log); + ReflectionUtils.invokeMethod(classLoader, "getJars", false), classLoader, scanSpec, log); valid |= classpathOrderOut.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "jar", false), - classLoader, log); + classLoader, scanSpec, log); valid |= classpathOrderOut.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "jars", false), - classLoader, log); + classLoader, scanSpec, log); valid |= classpathOrderOut.addClasspathEntryObject(ReflectionUtils.getFieldVal(classLoader, "jar", false), - classLoader, log); + classLoader, scanSpec, log); valid |= classpathOrderOut.addClasspathEntryObject(ReflectionUtils.getFieldVal(classLoader, "jars", false), - classLoader, log); + classLoader, scanSpec, log); valid |= classpathOrderOut.addClasspathEntryObject( - ReflectionUtils.invokeMethod(classLoader, "getURL", false), classLoader, log); + ReflectionUtils.invokeMethod(classLoader, "getURL", false), classLoader, scanSpec, log); valid |= classpathOrderOut.addClasspathEntryObject( - ReflectionUtils.invokeMethod(classLoader, "getURLs", false), classLoader, log); + ReflectionUtils.invokeMethod(classLoader, "getURLs", false), classLoader, scanSpec, log); valid |= classpathOrderOut.addClasspathEntryObject( - ReflectionUtils.invokeMethod(classLoader, "getUrl", false), classLoader, log); + ReflectionUtils.invokeMethod(classLoader, "getUrl", false), classLoader, scanSpec, log); valid |= classpathOrderOut.addClasspathEntryObject( - ReflectionUtils.invokeMethod(classLoader, "getUrls", false), classLoader, log); + ReflectionUtils.invokeMethod(classLoader, "getUrls", false), classLoader, scanSpec, log); valid |= classpathOrderOut.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "url", false), - classLoader, log); + classLoader, scanSpec, log); valid |= classpathOrderOut.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "urls", false), - classLoader, log); + classLoader, scanSpec, log); valid |= classpathOrderOut.addClasspathEntryObject(ReflectionUtils.getFieldVal(classLoader, "url", false), - classLoader, log); + classLoader, scanSpec, log); valid |= classpathOrderOut.addClasspathEntryObject(ReflectionUtils.getFieldVal(classLoader, "urls", false), - classLoader, log); + 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 e50161a10..c7887ba2a 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FelixClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FelixClassLoaderHandler.java @@ -98,11 +98,13 @@ private String getContentLocation(final Object content) { * the classloader * @param classpathOrderOut * the classpath order out + * @param scanSpec + * the scan spec * @param log * the log */ private void addBundle(final Object bundleWiring, final ClassLoader classLoader, - final ClasspathOrder classpathOrderOut, final LogNode log) { + final ClasspathOrder classpathOrderOut, ScanSpec scanSpec, final LogNode log) { // Track the bundles we've processed to prevent loops bundles.add(bundleWiring); @@ -113,7 +115,7 @@ private void addBundle(final Object bundleWiring, final ClassLoader classLoader, final String location = content != null ? getContentLocation(content) : null; if (location != null) { // Add the bundle object - classpathOrderOut.addClasspathEntry(location, classLoader, log); + classpathOrderOut.addClasspathEntry(location, classLoader, scanSpec, log); // And any embedded content final List embeddedContent = (List) ReflectionUtils.invokeMethod(revision, "getContentPath", @@ -123,7 +125,7 @@ private void addBundle(final Object bundleWiring, final ClassLoader classLoader, if (embedded != content) { final String embeddedLocation = embedded != null ? getContentLocation(embedded) : null; if (embeddedLocation != null) { - classpathOrderOut.addClasspathEntry(embeddedLocation, classLoader, log); + classpathOrderOut.addClasspathEntry(embeddedLocation, classLoader, scanSpec, log); } } } @@ -132,7 +134,8 @@ private void addBundle(final Object bundleWiring, final ClassLoader classLoader, } /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handle(nonapi.io.github.classgraph.ScanSpec, java.lang.ClassLoader, nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) + * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handle(nonapi.io.github.classgraph.ScanSpec, + * java.lang.ClassLoader, nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) */ @Override public void handle(final ScanSpec scanSpec, final ClassLoader classLoader, @@ -140,7 +143,7 @@ public void handle(final ScanSpec scanSpec, final ClassLoader classLoader, // Get the wiring for the ClassLoader's bundle final Object bundleWiring = ReflectionUtils.getFieldVal(classLoader, "m_wiring", false); - addBundle(bundleWiring, classLoader, classpathOrderOut, log); + addBundle(bundleWiring, classLoader, classpathOrderOut, 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. @@ -151,7 +154,7 @@ public void handle(final ScanSpec scanSpec, final ClassLoader classLoader, for (final Object wire : requiredWires) { final Object provider = ReflectionUtils.invokeMethod(wire, "getProviderWiring", false); if (!bundles.contains(provider)) { - addBundle(provider, classLoader, classpathOrderOut, log); + addBundle(provider, classLoader, classpathOrderOut, 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 7217a511e..332605553 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java @@ -84,11 +84,13 @@ public DelegationOrder getDelegationOrder(final ClassLoader classLoaderInstance) * the classloader * @param classpathOrderOut * the classpath order + * @param scanSpec + * the scan spec * @param log * the log */ private void handleResourceLoader(final Object resourceLoader, final ClassLoader classLoader, - final ClasspathOrder classpathOrderOut, final LogNode log) { + final ClasspathOrder classpathOrderOut, ScanSpec scanSpec, final LogNode log) { if (resourceLoader == null) { return; } @@ -129,7 +131,7 @@ private void handleResourceLoader(final Object resourceLoader, final ClassLoader } } if (path != null) { - classpathOrderOut.addClasspathEntry(path, classLoader, log); + classpathOrderOut.addClasspathEntry(path, classLoader, scanSpec, log); } else { if (log != null) { log.log("Could not determine classpath for ResourceLoader: " + resourceLoader); @@ -148,11 +150,14 @@ private void handleResourceLoader(final Object resourceLoader, final ClassLoader * the classloader * @param classpathOrderOut * the classpath order + * @param scanSpec + * the scan spec * @param log * the log */ private void handleRealModule(final Object module, final Set visitedModules, - final ClassLoader classLoader, final ClasspathOrder classpathOrderOut, final LogNode log) { + final ClassLoader classLoader, final ClasspathOrder classpathOrderOut, ScanSpec scanSpec, + final LogNode log) { if (!visitedModules.add(module)) { // Avoid extracting paths from the same module more than once return; @@ -172,14 +177,15 @@ private void handleRealModule(final Object module, final Set visitedModu // Could skip NativeLibraryResourceLoader instances altogether, but testing for their existence // only seems to add about 3% to the total scan time. // if (!resourceLoader.getClass().getSimpleName().equals("NativeLibraryResourceLoader")) { - handleResourceLoader(resourceLoader, moduleLoader, classpathOrderOut, log); + handleResourceLoader(resourceLoader, moduleLoader, classpathOrderOut, scanSpec, log); //} } } } /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handle(nonapi.io.github.classgraph.ScanSpec, java.lang.ClassLoader, nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) + * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handle(nonapi.io.github.classgraph.ScanSpec, + * java.lang.ClassLoader, nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) */ @Override public void handle(final ScanSpec scanSpec, final ClassLoader classLoader, @@ -195,7 +201,7 @@ public void handle(final ScanSpec scanSpec, final ClassLoader classLoader, final Object val = ent.getValue(); // type Module final Object realModule = ReflectionUtils.invokeMethod(val, "getModule", false); - handleRealModule(realModule, visitedModules, classLoader, classpathOrderOut, log); + handleRealModule(realModule, visitedModules, classLoader, classpathOrderOut, scanSpec, log); } // type Map> @SuppressWarnings("unchecked") @@ -207,7 +213,7 @@ public void handle(final ScanSpec scanSpec, final ClassLoader classLoader, final Object moduleClassLoader = ReflectionUtils.getFieldVal(localLoader, "this$0", false); // type Module final Object realModule = ReflectionUtils.getFieldVal(moduleClassLoader, "module", false); - handleRealModule(realModule, visitedModules, classLoader, classpathOrderOut, log); + handleRealModule(realModule, visitedModules, classLoader, classpathOrderOut, 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 8576943e9..fb805b2bf 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JPMSClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JPMSClassLoaderHandler.java @@ -65,7 +65,8 @@ public DelegationOrder getDelegationOrder(final ClassLoader classLoaderInstance) } /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handle(nonapi.io.github.classgraph.ScanSpec, java.lang.ClassLoader, nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) + * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handle(nonapi.io.github.classgraph.ScanSpec, + * java.lang.ClassLoader, nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) */ @Override public void handle(final ScanSpec scanSpec, final ClassLoader classLoader, 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 d472a79d9..d1f6bfcdf 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/OSGiDefaultClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/OSGiDefaultClassLoaderHandler.java @@ -67,7 +67,8 @@ public DelegationOrder getDelegationOrder(final ClassLoader classLoaderInstance) } /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handle(nonapi.io.github.classgraph.ScanSpec, java.lang.ClassLoader, nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) + * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handle(nonapi.io.github.classgraph.ScanSpec, + * java.lang.ClassLoader, nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) */ @Override public void handle(final ScanSpec scanSpec, final ClassLoader classloader, @@ -79,7 +80,7 @@ public void handle(final ScanSpec scanSpec, final ClassLoader classloader, final Object bundleFile = ReflectionUtils.invokeMethod(entry, "getBundleFile", false); final File baseFile = (File) ReflectionUtils.invokeMethod(bundleFile, "getBaseFile", false); if (baseFile != null) { - classpathOrderOut.addClasspathEntry(baseFile.getPath(), classloader, log); + classpathOrderOut.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 9beea6dca..fd11f16c5 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ParentLastDelegationOrderTestClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ParentLastDelegationOrderTestClassLoaderHandler.java @@ -61,13 +61,14 @@ public DelegationOrder getDelegationOrder(final ClassLoader classLoaderInstance) } /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handle(nonapi.io.github.classgraph.ScanSpec, java.lang.ClassLoader, nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) + * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handle(nonapi.io.github.classgraph.ScanSpec, + * java.lang.ClassLoader, nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) */ @Override public void handle(final ScanSpec scanSpec, final ClassLoader classLoader, final ClasspathOrder classpathOrderOut, final LogNode log) { final String classpath = (String) ReflectionUtils.invokeMethod(classLoader, "getClasspath", /* throwException = */ true); - classpathOrderOut.addClasspathEntry(classpath, classLoader, log); + classpathOrderOut.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 new file mode 100644 index 000000000..1fe82ca6e --- /dev/null +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/PlexusClassWorldsClassRealmClassLoaderHandler.java @@ -0,0 +1,139 @@ +/* + * 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.classloaderhandler; + +import java.io.File; +import java.util.LinkedHashSet; +import java.util.Map.Entry; +import java.util.Set; +import java.util.SortedSet; + +import nonapi.io.github.classgraph.ScanSpec; +import nonapi.io.github.classgraph.classpath.ClasspathOrder; +import nonapi.io.github.classgraph.utils.LogNode; +import nonapi.io.github.classgraph.utils.ReflectionUtils; + +/** + * Handle the Plexus ClassWorlds ClassRealm ClassLoader. + * + * @author lukehutch + */ +class PlexusClassWorldsClassRealmClassLoaderHandler implements ClassLoaderHandler { + + /* (non-Javadoc) + * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handledClassLoaders() + */ + @Override + public String[] handledClassLoaders() { + return new String[] { "org.codehaus.plexus.classworlds.realm.ClassRealm" }; + } + + /* (non-Javadoc) + * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#getEmbeddedClassLoader(java.lang.ClassLoader) + */ + @Override + public ClassLoader getEmbeddedClassLoader(final ClassLoader classRealmInstance) { + // Set classLoaderOrder = new LinkedHashSet<>(); + // + // // From ClassRealm#loadClassFromImport(String) -> getImportClassLoader(String) + // final Object foreignImports = ReflectionUtils.getFieldVal(classRealmInstance, "foreignImports", false); + // if (foreignImports != null) { + // @SuppressWarnings("unchecked") + // SortedSet foreignImportEntries = (SortedSet) foreignImports; + // for (Object entry : foreignImportEntries) { + // final Object classLoader = ReflectionUtils.invokeMethod(entry, "getClassLoader", false); + // if (classLoader instanceof ClassLoader) { + // classLoaderOrder.add((ClassLoader) classLoader); + // } + // } + // } + // + // // Get delegation order -- different strategies have different delegation orders + // DelegationOrder delegationOrder = getDelegationOrder(classRealmInstance); + // + // // From ClassRealm#loadClassFromSelf(String) -> findLoadedClass(String) for self-first strategy + // if (delegationOrder == DelegationOrder.PARENT_LAST) { + // classLoaderOrder.add(classRealmInstance); + // } + // + // // 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 Object parentClassLoader = ReflectionUtils.invokeMethod(classRealmInstance, "getParentClassLoader", + // false); + // if (parentClassLoader instanceof ClassLoader) { + // classLoaderOrder.add((ClassLoader) parentClassLoader); + // } + // + // // From ClassRealm#loadClassFromSelf(String) -> findLoadedClass(String) for parent-first strategy + // if (delegationOrder == DelegationOrder.PARENT_FIRST) { + // classLoaderOrder.add(classRealmInstance); + // } + // + // return classLoaderOrder; + return null; + } + + /* (non-Javadoc) + * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#getDelegationOrder(java.lang.ClassLoader) + */ + @Override + public DelegationOrder getDelegationOrder(final ClassLoader classRealmInstance) { + final Object strategy = ReflectionUtils.getFieldVal(classRealmInstance, "strategy", false); + if (strategy != null) { + String strategyClassName = strategy.getClass().getName(); + if (strategyClassName.equals("org.codehaus.plexus.classworlds.strategy.SelfFirstStrategy") + || strategyClassName.equals("org.codehaus.plexus.classworlds.strategy.OsgiBundleStrategy")) { + return DelegationOrder.PARENT_LAST; + } + } + // org.codehaus.plexus.classworlds.strategy.ParentFirstStrategy (or failed to find strategy) + return DelegationOrder.PARENT_FIRST; + } + + /* (non-Javadoc) + * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handle( + * nonapi.io.github.classgraph.ScanSpec, java.lang.ClassLoader, + * nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) + */ + @Override + public void handle(final ScanSpec scanSpec, final ClassLoader classloader, + final ClasspathOrder classpathOrderOut, final LogNode log) { + // final Object[] entries = (Object[]) ReflectionUtils.getFieldVal(classpathManager, "entries", false); + // 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); + // if (baseFile != null) { + // classpathOrderOut.addClasspathEntry(baseFile.getPath(), classloader, 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 c56ac2dc9..42a70805d 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java @@ -66,7 +66,8 @@ public DelegationOrder getDelegationOrder(final ClassLoader classLoaderInstance) } /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handle(nonapi.io.github.classgraph.ScanSpec, java.lang.ClassLoader, nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) + * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handle(nonapi.io.github.classgraph.ScanSpec, + * java.lang.ClassLoader, nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) */ @Override public void handle(final ScanSpec scanSpec, final ClassLoader classLoader, @@ -75,7 +76,7 @@ public void handle(final ScanSpec scanSpec, final ClassLoader classLoader, final Object resources = ReflectionUtils.invokeMethod(classLoader, "getResources", false); // type List final Object baseURLs = ReflectionUtils.invokeMethod(resources, "getBaseUrls", false); - classpathOrderOut.addClasspathEntryObject(baseURLs, classLoader, log); + classpathOrderOut.addClasspathEntryObject(baseURLs, classLoader, scanSpec, log); // type List> // members: preResources, mainResources, classResources, jarResources, postResources @SuppressWarnings("unchecked") @@ -119,9 +120,9 @@ public void handle(final ScanSpec scanSpec, final ClassLoader classLoader, classpathOrderOut.addClasspathEntryObject( base + (isJar ? "!" : "") + (internalPath.startsWith("/") ? internalPath : "/" + internalPath), - classLoader, log); + classLoader, scanSpec, log); } else { - classpathOrderOut.addClasspathEntryObject(base, classLoader, log); + classpathOrderOut.addClasspathEntryObject(base, classLoader, scanSpec, log); } } } @@ -129,6 +130,6 @@ public void handle(final ScanSpec scanSpec, final ClassLoader classLoader, } // This may or may not duplicate the above final Object urls = ReflectionUtils.invokeMethod(classLoader, "getURLs", false); - classpathOrderOut.addClasspathEntryObject(urls, classLoader, log); + classpathOrderOut.addClasspathEntryObject(urls, classLoader, scanSpec, log); } } 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 25436dd0f..e25c2bb56 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/URLClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/URLClassLoaderHandler.java @@ -63,7 +63,8 @@ public DelegationOrder getDelegationOrder(final ClassLoader classLoaderInstance) } /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handle(nonapi.io.github.classgraph.ScanSpec, java.lang.ClassLoader, nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) + * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handle(nonapi.io.github.classgraph.ScanSpec, + * java.lang.ClassLoader, nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) */ @Override public void handle(final ScanSpec scanSpec, final ClassLoader classLoader, @@ -72,7 +73,7 @@ public void handle(final ScanSpec scanSpec, final ClassLoader classLoader, if (urls != null) { for (final URL url : urls) { if (url != null) { - classpathOrderOut.addClasspathEntry(url.toString(), classLoader, log); + classpathOrderOut.addClasspathEntry(url.toString(), classLoader, scanSpec, log); } } } 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 79ac54dba..686f9f215 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WeblogicClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WeblogicClassLoaderHandler.java @@ -68,14 +68,17 @@ public DelegationOrder getDelegationOrder(final ClassLoader classLoaderInstance) } /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handle(nonapi.io.github.classgraph.ScanSpec, java.lang.ClassLoader, nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) + * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handle(nonapi.io.github.classgraph.ScanSpec, + * java.lang.ClassLoader, nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) */ @Override public void handle(final ScanSpec scanSpec, final ClassLoader classLoader, final ClasspathOrder classpathOrderOut, final LogNode log) { classpathOrderOut.addClasspathEntries( // - (String) ReflectionUtils.invokeMethod(classLoader, "getFinderClassPath", false), classLoader, log); + (String) ReflectionUtils.invokeMethod(classLoader, "getFinderClassPath", false), classLoader, + scanSpec, log); classpathOrderOut.addClasspathEntries( // - (String) ReflectionUtils.invokeMethod(classLoader, "getClassPath", false), classLoader, log); + (String) ReflectionUtils.invokeMethod(classLoader, "getClassPath", false), 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 2bd72920b..f3bf0b8d8 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java @@ -118,7 +118,8 @@ private String getPath(final Object classpath) { } /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handle(nonapi.io.github.classgraph.ScanSpec, java.lang.ClassLoader, nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) + * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handle(nonapi.io.github.classgraph.ScanSpec, + * java.lang.ClassLoader, nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) */ @Override public void handle(final ScanSpec scanSpec, final ClassLoader classLoader, @@ -137,7 +138,7 @@ public void handle(final ScanSpec scanSpec, final ClassLoader classLoader, for (final Object classpath : classPathElements) { final String path = getPath(classpath); if (path != null && path.length() > 0) { - classpathOrderOut.addClasspathEntry(path, classLoader, log); + classpathOrderOut.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 88572aabe..2cddaf680 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereTraditionalClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereTraditionalClassLoaderHandler.java @@ -70,12 +70,13 @@ public DelegationOrder getDelegationOrder(final ClassLoader classLoaderInstance) } /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handle(nonapi.io.github.classgraph.ScanSpec, java.lang.ClassLoader, nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) + * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handle(nonapi.io.github.classgraph.ScanSpec, + * java.lang.ClassLoader, nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) */ @Override public void handle(final ScanSpec scanSpec, final ClassLoader classloader, final ClasspathOrder classpathOrderOut, final LogNode log) { final String classpath = (String) ReflectionUtils.invokeMethod(classloader, "getClassPath", false); - classpathOrderOut.addClasspathEntries(classpath, classloader, log); + classpathOrderOut.addClasspathEntries(classpath, classloader, scanSpec, 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 3843836d4..9edb1a022 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java @@ -341,7 +341,8 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { } final LogNode overrideLog = classpathFinderLog == null ? null : classpathFinderLog.log("Overriding classpath with: " + scanSpec.overrideClasspath); - classpathOrder.addClasspathEntries(scanSpec.overrideClasspath, defaultClassLoader, overrideLog); + classpathOrder.addClasspathEntries(scanSpec.overrideClasspath, defaultClassLoader, scanSpec, + overrideLog); if (overrideLog != null) { overrideLog.log("WARNING: when the classpath is overridden, there is no guarantee that the classes " + "found by classpath scanning will be the same as the classes loaded by the " @@ -451,7 +452,7 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { if (!ignoredClasspathOrder.getClasspathEntryUniqueResolvedPaths() .contains(pathElementResolved)) { // pathElement is not also listed in an ignored parent classloader - classpathOrder.addClasspathEntry(pathElement, defaultClassLoader, sysPropLog); + classpathOrder.addClasspathEntry(pathElement, defaultClassLoader, scanSpec, sysPropLog); } else { // pathElement is also listed in an ignored parent classloader, ignore it (Issue #169) if (sysPropLog != null) { 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 b5ec4351c..5832bdf79 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java @@ -127,11 +127,14 @@ boolean addSystemClasspathEntry(final String pathEntry, final ClassLoader classL * FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, path) * @param classLoader * the classloader + * @param scanSpec + * the scan spec * @return true, if added and unique */ - private boolean addClasspathEntry(final String pathEntry, final ClassLoader classLoader) { - if (SystemJarFinder.getJreLibOrExtJars().contains(pathEntry) - || pathEntry.equals(SystemJarFinder.getJreRtJarPath())) { + private boolean addClasspathEntry(final String pathEntry, final ClassLoader classLoader, ScanSpec scanSpec) { + if (scanSpec.overrideClasspath != null // + && (SystemJarFinder.getJreLibOrExtJars().contains(pathEntry) + || pathEntry.equals(SystemJarFinder.getJreRtJarPath()))) { // JRE lib and ext jars are handled separately, so reject them as duplicates if they are // returned by a system classloader return false; @@ -151,12 +154,15 @@ private boolean addClasspathEntry(final String pathEntry, final ClassLoader clas * the URL or path of the classpath element. * @param classLoader * the ClassLoader that this classpath element was obtained from. + * @param scanSpec + * the scan spec * @param log * the LogNode instance to use if logging in verbose mode. * @return true (and add the classpath element) if pathElement is not null, empty, nonexistent, or filtered out * by user-specified criteria, otherwise return false. */ - public boolean addClasspathEntry(final String pathElement, final ClassLoader classLoader, final LogNode log) { + public boolean addClasspathEntry(final String pathElement, final ClassLoader classLoader, ScanSpec scanSpec, + final LogNode log) { if (pathElement == null || pathElement.isEmpty()) { return false; } @@ -212,7 +218,7 @@ public boolean addClasspathEntry(final String pathElement, final ClassLoader cla final String fileInDirPath = fileInDir.getPath(); final String fileInDirPathResolved = FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, fileInDirPath); - if (addClasspathEntry(fileInDirPathResolved, classLoader)) { + if (addClasspathEntry(fileInDirPathResolved, classLoader, scanSpec)) { if (dirLog != null) { dirLog.log("Found classpath element: " + fileInDirPath + (fileInDirPath.equals(fileInDirPathResolved) ? "" @@ -249,7 +255,7 @@ public boolean addClasspathEntry(final String pathElement, final ClassLoader cla } return false; } - if (addClasspathEntry(pathElementResolved, classLoader)) { + if (addClasspathEntry(pathElementResolved, classLoader, scanSpec)) { if (log != null) { log.log("Found classpath element: " + pathElement + (pathElement.equals(pathElementResolved) ? "" : " -> " + pathElementResolved)); @@ -272,11 +278,14 @@ public boolean addClasspathEntry(final String pathElement, final ClassLoader cla * the delimited string of URLs or paths of the classpath. * @param classLoader * the ClassLoader that this classpath was obtained from. + * @param scanSpec + * the scan spec * @param log * the LogNode instance to use if logging in verbose mode. * @return true (and add the classpath element) if pathElement is not null or empty, otherwise return false. */ - public boolean addClasspathEntries(final String pathStr, final ClassLoader classLoader, final LogNode log) { + public boolean addClasspathEntries(final String pathStr, final ClassLoader classLoader, ScanSpec scanSpec, + final LogNode log) { if (pathStr == null || pathStr.isEmpty()) { return false; } else { @@ -285,7 +294,7 @@ public boolean addClasspathEntries(final String pathStr, final ClassLoader class return false; } else { for (final String pathElement : parts) { - addClasspathEntry(pathElement, classLoader, log); + addClasspathEntry(pathElement, classLoader, scanSpec, log); } return true; } @@ -302,20 +311,22 @@ public boolean addClasspathEntries(final String pathStr, final ClassLoader class * the object containing a classpath string or strings. * @param classLoader * the ClassLoader that this classpath was obtained from. + * @param scanSpec + * the scan spec * @param log * the LogNode instance to use if logging in verbose mode. * @return true (and add the classpath element) if pathEl)ement is not null or empty, otherwise return false. */ public boolean addClasspathEntryObject(final Object pathObject, final ClassLoader classLoader, - final LogNode log) { + ScanSpec scanSpec, final LogNode log) { boolean valid = false; if (pathObject != null) { if (pathObject instanceof String) { - valid |= addClasspathEntries((String) pathObject, classLoader, log); + valid |= addClasspathEntries((String) pathObject, classLoader, scanSpec, log); } else if (pathObject instanceof Iterable) { for (final Object p : (Iterable) pathObject) { if (p != null) { - valid |= addClasspathEntries(p.toString(), classLoader, log); + valid |= addClasspathEntries(p.toString(), classLoader, scanSpec, log); } } } else { @@ -324,12 +335,12 @@ public boolean addClasspathEntryObject(final Object pathObject, final ClassLoade for (int j = 0, n = Array.getLength(pathObject); j < n; j++) { final Object elt = Array.get(pathObject, j); if (elt != null) { - valid |= addClasspathEntryObject(elt, classLoader, log); + valid |= addClasspathEntryObject(elt, classLoader, scanSpec, log); } } } else { // Try simply calling toString() as a final fallback, in case this returns something sensible - valid |= addClasspathEntries(pathObject.toString(), classLoader, log); + valid |= addClasspathEntries(pathObject.toString(), classLoader, scanSpec, 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 85b5e811a..85869d2c6 100644 --- a/src/main/java/nonapi/io/github/classgraph/concurrency/SingletonMap.java +++ b/src/main/java/nonapi/io/github/classgraph/concurrency/SingletonMap.java @@ -41,11 +41,13 @@ * A map from keys to singleton instances. Allows you to create object instance singletons and add them to a * {@link ConcurrentMap} on demand, based on a key value. Works the same as * {@code concurrentMap.computeIfAbsent(key, key -> newInstance(key))}, except that it also works on JDK 7. - * + * * @param * The key type. * @param * The value type. + * @param + * the element type */ public abstract class SingletonMap { /** The map. */ diff --git a/src/test/java/io/github/classgraph/features/MethodParameterAnnotations.java b/src/test/java/io/github/classgraph/features/MethodParameterAnnotations.java index 1ec84cc61..9ae7849ad 100644 --- a/src/test/java/io/github/classgraph/features/MethodParameterAnnotations.java +++ b/src/test/java/io/github/classgraph/features/MethodParameterAnnotations.java @@ -32,6 +32,13 @@ public class MethodParameterAnnotations { * The Class Y. */ private abstract static class Y { + + /** + * W. + * + * @param w + * the w + */ abstract void w(@W int w); } @@ -39,6 +46,13 @@ private abstract static class Y { * The Class Z. */ private abstract static class Z { + + /** + * X. + * + * @param x + * the x + */ abstract void x(@X int x); } diff --git a/src/test/java/io/github/classgraph/features/MultiReleaseJar.java b/src/test/java/io/github/classgraph/features/MultiReleaseJar.java index fec222eb8..186d74c37 100644 --- a/src/test/java/io/github/classgraph/features/MultiReleaseJar.java +++ b/src/test/java/io/github/classgraph/features/MultiReleaseJar.java @@ -26,6 +26,9 @@ public class MultiReleaseJar { /** * Multi release jar. + * + * @throws Exception + * the exception */ @Test public void multiReleaseJar() throws Exception { @@ -64,6 +67,9 @@ public void accept(final Resource resource, final byte[] byteArray) { /** * Multi release versioning of resources. + * + * @throws Exception + * the exception */ @Test public void multiReleaseVersioningOfResources() throws Exception { diff --git a/src/test/java/io/github/classgraph/issues/issue128/Issue128Test.java b/src/test/java/io/github/classgraph/issues/issue128/Issue128Test.java index a88761da6..1455cb75f 100644 --- a/src/test/java/io/github/classgraph/issues/issue128/Issue128Test.java +++ b/src/test/java/io/github/classgraph/issues/issue128/Issue128Test.java @@ -59,8 +59,8 @@ public class Issue128Test { /** * Issue 128 test. * - * @throws IOException - * Signals that an I/O exception has occurred. + * @throws Exception + * the exception */ @Test public void issue128Test() throws Exception { diff --git a/src/test/java/io/github/classgraph/issues/issue267/ClassLoadingWorksWithParentLastLoadersStub.java b/src/test/java/io/github/classgraph/issues/issue267/ClassLoadingWorksWithParentLastLoadersStub.java index a5f54742c..9740139f3 100644 --- a/src/test/java/io/github/classgraph/issues/issue267/ClassLoadingWorksWithParentLastLoadersStub.java +++ b/src/test/java/io/github/classgraph/issues/issue267/ClassLoadingWorksWithParentLastLoadersStub.java @@ -42,11 +42,12 @@ * ClassLoadingWorksWithParentLastLoadersStub. */ public class ClassLoadingWorksWithParentLastLoadersStub { + /** * Same class loader that found A class should load it. * - * @throws Exception - * the exception + * @throws Throwable + * the throwable */ @Test public void sameClassLoaderThatFoundAClassShouldLoadIt() throws Throwable { diff --git a/src/test/java/io/github/classgraph/issues/issue289/Issue289.java b/src/test/java/io/github/classgraph/issues/issue289/Issue289.java index b4ce163ec..14374b15d 100644 --- a/src/test/java/io/github/classgraph/issues/issue289/Issue289.java +++ b/src/test/java/io/github/classgraph/issues/issue289/Issue289.java @@ -15,8 +15,12 @@ * Issue289. */ public class Issue289 { + /** * Issue 289. + * + * @throws Exception + * the exception */ @Test public void issue289() throws Exception { 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 8e0444b73..6475ac124 100644 --- a/src/test/java/io/github/classgraph/issues/issue305/Issue305.java +++ b/src/test/java/io/github/classgraph/issues/issue305/Issue305.java @@ -19,7 +19,13 @@ * Issue305. */ public class Issue305 { - /** Test that multi-line continuations in manifest file values are correctly assembled into a string. */ + + /** + * 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 { ConsoleHandler errPrintStreamHandler = null; diff --git a/src/test/java/io/github/classgraph/test/ClassGraphTest.java b/src/test/java/io/github/classgraph/test/ClassGraphTest.java index 59b043b28..088019671 100644 --- a/src/test/java/io/github/classgraph/test/ClassGraphTest.java +++ b/src/test/java/io/github/classgraph/test/ClassGraphTest.java @@ -391,6 +391,9 @@ public void scanStaticFinalFieldName() { /** * Scan static final field name ignore visibility. + * + * @throws Exception + * the exception */ @Test public void scanStaticFinalFieldNameIgnoreVisibility() throws Exception { From 15086253034434f8ab5d6b1d1fc62eff70b59558 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 22 Mar 2019 09:33:38 -0600 Subject: [PATCH 0055/1778] Major improvements to how ClassLoaderHandlers work --- .../java/io/github/classgraph/ScanResult.java | 1 - .../AntClassLoaderHandler.java | 61 ++-- .../ClassLoaderHandler.java | 67 ---- .../ClassLoaderHandlerRegistry.java | 182 +++++++---- .../EquinoxClassLoaderHandler.java | 82 ++--- ...quinoxContextFinderClassLoaderHandler.java | 63 ++-- .../FallbackClassLoaderHandler.java | 165 +++++----- .../FelixClassLoaderHandler.java | 81 ++--- .../JBossClassLoaderHandler.java | 70 ++-- .../JPMSClassLoaderHandler.java | 60 ++-- .../OSGiDefaultClassLoaderHandler.java | 62 ++-- ...DelegationOrderTestClassLoaderHandler.java | 62 ++-- .../SpringBootRestartClassLoaderHandler.java | 70 ++-- .../TomcatWebappClassLoaderBaseHandler.java | 68 ++-- .../URLClassLoaderHandler.java | 60 ++-- .../WeblogicClassLoaderHandler.java | 75 +++-- .../WebsphereLibertyClassLoaderHandler.java | 64 ++-- ...ebsphereTraditionalClassLoaderHandler.java | 69 ++-- .../classpath/ClassLoaderOrder.java | 160 ++++++++++ .../classgraph/classpath/ClasspathFinder.java | 298 ++---------------- .../classgraph/classpath/ClasspathOrder.java | 11 +- 21 files changed, 929 insertions(+), 902 deletions(-) create mode 100644 src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderOrder.java diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index 740f34b0a..827098388 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -225,7 +225,6 @@ public void run() { try { tempFile = File.createTempFile("ClassGraph-", "-temp"); tempFile.deleteOnExit(); - System.err.println("Created temp file " + tempFile); try (PrintWriter printWriter = new PrintWriter(tempFile)) { printWriter.print("temp"); createdTempFile = true; 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 36abf7d57..5c6905607 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/AntClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/AntClassLoaderHandler.java @@ -29,46 +29,53 @@ package nonapi.io.github.classgraph.classloaderhandler; import nonapi.io.github.classgraph.ScanSpec; +import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; 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 { - - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handledClassLoaders() - */ - @Override - public String[] handledClassLoaders() { - return new String[] { "org.apache.tools.ant.AntClassLoader" }; - } - - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#getEmbeddedClassLoader(java.lang.ClassLoader) + /** + * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. + * + * @param classLoader + * the {@link ClassLoader}. + * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ - @Override - public ClassLoader getEmbeddedClassLoader(final ClassLoader outerClassLoaderInstance) { - return null; + public static boolean canHandle(final ClassLoader classLoader) { + return "org.apache.tools.ant.AntClassLoader".equals(classLoader.getClass().getName()); } - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#getDelegationOrder(java.lang.ClassLoader) + /** + * 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. */ - @Override - public DelegationOrder getDelegationOrder(final ClassLoader classLoaderInstance) { - return DelegationOrder.PARENT_FIRST; + public static void findClassLoaderOrder(final ClassLoader classLoader, + final ClassLoaderOrder classLoaderOrder) { + classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true); + classLoaderOrder.add(classLoader); } - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handle( - * nonapi.io.github.classgraph.ScanSpec, java.lang.ClassLoader, - * nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) + /** + * 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. */ - @Override - public void handle(final ScanSpec scanSpec, final ClassLoader classLoader, - final ClasspathOrder classpathOrderOut, final LogNode log) { - classpathOrderOut.addClasspathEntries( // + public static void findClasspathOrder(final ClassLoader classLoader, final ClasspathOrder classpathOrder, + final ScanSpec scanSpec, final LogNode log) { + classpathOrder.addClasspathEntries( (String) ReflectionUtils.invokeMethod(classLoader, "getClasspath", false), classLoader, scanSpec, log); } diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandler.java index 837dc310d..b6fff3a71 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandler.java @@ -28,10 +28,6 @@ */ package nonapi.io.github.classgraph.classloaderhandler; -import nonapi.io.github.classgraph.ScanSpec; -import nonapi.io.github.classgraph.classpath.ClasspathOrder; -import nonapi.io.github.classgraph.utils.LogNode; - /** * A ClassLoader handler. * @@ -39,67 +35,4 @@ * If you create a custom ClassLoaderHandler, please consider submitting it to the ClassGraph open source project. */ public interface ClassLoaderHandler { - /** - * The fully-qualified names of handled classloader classes. - * - * @return The names of ClassLoaders that this ClassLoaderHandler can handle. - */ - String[] handledClassLoaders(); - - /** - * The delegation order configuration for a given ClassLoader instance (this is usually PARENT_FIRST for most - * ClassLoaders, but this can be overridden by some ClassLoaders, e.g. WebSphere). - */ - enum DelegationOrder { - /** Delegate to parent before handling in child. */ - PARENT_FIRST, - /** Handle classloading in child before delegating to parent. */ - PARENT_LAST - } - - /** - * If this ClassLoader delegates directly to an embedded classloader instance, return it here, otherwise return - * null. - * - * @param outerClassLoaderInstance - * The outer ClassLoader instance to check for an embedded ClassLoader. - * @return The embedded ClassLoader to use instead of the outer ClassLoader, or null to use the outer - * ClassLoader. - */ - ClassLoader getEmbeddedClassLoader(ClassLoader outerClassLoaderInstance); - - /** - * The delegation order configuration for a given ClassLoader instance (this is usually PARENT_FIRST for most - * ClassLoaders, since you don't generally want to be able to override system classes with user classes, but - * this can be overridden by some ClassLoaders, e.g. WebSphere). - * - * @param classLoaderInstance - * The ClassLoader to get the delegation order for. - * @return The delegation order for the given ClassLoader. - */ - DelegationOrder getDelegationOrder(ClassLoader classLoaderInstance); - - /** - * Determine if a given ClassLoader can be handled (meaning that its classpath elements can be extracted from - * it), and if it can, extract the classpath elements from the ClassLoader and register them with the - * ClasspathFinder using classpathFinder.addClasspathElement(pathElement) or - * classpathFinder.addClasspathElements(path). - * - * @param scanSpec - * the scanning specification, in case it is needed, e.g. this could be used to reduce the number of - * classpath elements returned in cases where it is very costly for a given classloader to return the - * entire classpath. (The ScanSpec can be safely ignored, however, and the returned paths will be - * filtered by ClassGraph.) - * @param classLoader - * The ClassLoader class to attempt to handle. If you can't directly use instanceof (because you are - * using introspection so that your ClassLoaderHandler implementation can be added to the upstream - * ClassGraph project), you should iterate through the ClassLoader's superclass lineage to ensure - * subclasses of the target ClassLoader are correctly detected. - * @param classpathOrderOut - * The ClasspathOrder to register any discovered classpath elements with. - * @param log - * A logger instance -- if this is non-null, write debug information using log.log("message"). - */ - void handle(ScanSpec scanSpec, final ClassLoader classLoader, final ClasspathOrder classpathOrderOut, - LogNode log); } 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 c28804106..fcc4d50aa 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java @@ -28,55 +28,54 @@ */ package nonapi.io.github.classgraph.classloaderhandler; -import java.util.ArrayList; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Method; import java.util.Arrays; import java.util.Collections; import java.util.List; -import io.github.classgraph.ClassGraphException; +import nonapi.io.github.classgraph.ScanSpec; +import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; +import nonapi.io.github.classgraph.classpath.ClasspathOrder; import nonapi.io.github.classgraph.utils.LogNode; /** The registry for ClassLoaderHandler classes. */ public class ClassLoaderHandlerRegistry { /** - * Default ClassLoaderHandlers. + * Default ClassLoaderHandlers. If a ClassLoaderHandler is added to ClassGraph, it should be added to this list. */ - public static final List CLASS_LOADER_HANDLERS; - - static { - // If a ClassLoaderHandler is added to ClassGraph, it should be added to this list. - final List builtInHandlers = Arrays.asList( - // ClassLoaderHandlers for other ClassLoaders that are handled by ClassGraph - new ClassLoaderHandlerRegistryEntry(AntClassLoaderHandler.class), - new ClassLoaderHandlerRegistryEntry(EquinoxClassLoaderHandler.class), - new ClassLoaderHandlerRegistryEntry(EquinoxContextFinderClassLoaderHandler.class), - new ClassLoaderHandlerRegistryEntry(FelixClassLoaderHandler.class), - new ClassLoaderHandlerRegistryEntry(JBossClassLoaderHandler.class), - new ClassLoaderHandlerRegistryEntry(WeblogicClassLoaderHandler.class), - new ClassLoaderHandlerRegistryEntry(WebsphereLibertyClassLoaderHandler.class), - new ClassLoaderHandlerRegistryEntry(WebsphereTraditionalClassLoaderHandler.class), - new ClassLoaderHandlerRegistryEntry(OSGiDefaultClassLoaderHandler.class), - new ClassLoaderHandlerRegistryEntry(SpringBootRestartClassLoaderHandler.class), - new ClassLoaderHandlerRegistryEntry(TomcatWebappClassLoaderBaseHandler.class), - // new ClassLoaderHandlerRegistryEntry(PlexusClassWorldsClassRealmClassLoaderHandler.class), - - // For unit testing of PARENT_LAST delegation order - new ClassLoaderHandlerRegistryEntry(ParentLastDelegationOrderTestClassLoaderHandler.class), - - // JPMS support - new ClassLoaderHandlerRegistryEntry(JPMSClassLoaderHandler.class), - - // Java 7/8 support (list last, as fallback) - new ClassLoaderHandlerRegistryEntry(URLClassLoaderHandler.class)); - - final List registeredHandlers = new ArrayList<>(builtInHandlers); - - CLASS_LOADER_HANDLERS = Collections.unmodifiableList(registeredHandlers); - } - - /** The fallback ClassLoaderHandler. Do not need to add FallbackClassLoaderHandler to the above list. */ - public static final ClassLoaderHandlerRegistryEntry FALLBACK_CLASS_LOADER_HANDLER = // - new ClassLoaderHandlerRegistryEntry(FallbackClassLoaderHandler.class); + public static final List CLASS_LOADER_HANDLERS = // + Collections.unmodifiableList(Arrays.asList( + // ClassLoaderHandlers for other ClassLoaders that are handled by ClassGraph + new ClassLoaderHandlerRegistryEntry(AntClassLoaderHandler.class), + new ClassLoaderHandlerRegistryEntry(EquinoxClassLoaderHandler.class), + new ClassLoaderHandlerRegistryEntry(EquinoxContextFinderClassLoaderHandler.class), + new ClassLoaderHandlerRegistryEntry(FelixClassLoaderHandler.class), + new ClassLoaderHandlerRegistryEntry(JBossClassLoaderHandler.class), + new ClassLoaderHandlerRegistryEntry(WeblogicClassLoaderHandler.class), + new ClassLoaderHandlerRegistryEntry(WebsphereLibertyClassLoaderHandler.class), + new ClassLoaderHandlerRegistryEntry(WebsphereTraditionalClassLoaderHandler.class), + new ClassLoaderHandlerRegistryEntry(OSGiDefaultClassLoaderHandler.class), + new ClassLoaderHandlerRegistryEntry(SpringBootRestartClassLoaderHandler.class), + new ClassLoaderHandlerRegistryEntry(TomcatWebappClassLoaderBaseHandler.class), + new ClassLoaderHandlerRegistryEntry(PlexusClassWorldsClassRealmClassLoaderHandler.class), + + // For unit testing of PARENT_LAST delegation order + new ClassLoaderHandlerRegistryEntry(ParentLastDelegationOrderTestClassLoaderHandler.class), + + // JPMS support (this handler does nothing, since modules are handled separately) + new ClassLoaderHandlerRegistryEntry(JPMSClassLoaderHandler.class), + + // Java 7/8 URLClassLoader support (should be second-to-last, so that subclasses of + // URLClassLoader are handled by more specific handlers above) + new ClassLoaderHandlerRegistryEntry(URLClassLoaderHandler.class))); + + /** Fallback ClassLoaderHandler. */ + public static final ClassLoaderHandlerRegistryEntry FALLBACK_HANDLER = new ClassLoaderHandlerRegistryEntry( + FallbackClassLoaderHandler.class); + + // ------------------------------------------------------------------------------------------------------------- /** * Lib dirs whose jars should be added to the classpath automatically (to compensate for some classloaders not @@ -108,6 +107,8 @@ public class ClassLoaderHandlerRegistry { "WEB-INF/classes/" // }; + // ------------------------------------------------------------------------------------------------------------- + /** * Constructor. */ @@ -119,9 +120,16 @@ private ClassLoaderHandlerRegistry() { * A list of fully-qualified ClassLoader class names paired with the ClassLoaderHandler that can handle them. */ public static class ClassLoaderHandlerRegistryEntry { - /** The names of handled ClassLoaders. */ - public final String[] handledClassLoaderNames; - /** The ClassLoader class.. */ + /** canHandle method handle. */ + private final MethodHandle canHandle; + + /** findClassLoaderOrder method handle. */ + private final MethodHandle findClassLoaderOrder; + + /** findClasspathOrder method handle. */ + private final MethodHandle findClasspathOrder; + + /** The ClassLoaderHandler class. */ public final Class classLoaderHandlerClass; /** @@ -133,32 +141,86 @@ public static class ClassLoaderHandlerRegistryEntry { private ClassLoaderHandlerRegistryEntry(final Class classLoaderHandlerClass) { this.classLoaderHandlerClass = classLoaderHandlerClass; try { - // Instantiate each ClassLoaderHandler in order to call the handledClassLoaders() method (this is - // needed because Java doesn't support inherited static interface methods) - this.handledClassLoaderNames = classLoaderHandlerClass.getDeclaredConstructor().newInstance() - .handledClassLoaders(); - } catch (final ReflectiveOperationException | ExceptionInInitializerError e) { - throw ClassGraphException - .newClassGraphException("Could not instantiate " + classLoaderHandlerClass.getName(), e); + final Method canHandleMethod = classLoaderHandlerClass.getDeclaredMethod("canHandle", + ClassLoader.class); + canHandle = MethodHandles.lookup().unreflect(canHandleMethod); + } catch (final Exception e) { + throw new RuntimeException( + "Could not find canHandle method for " + classLoaderHandlerClass.getName(), e); + } + try { + final Method findClassLoaderOrderMethod = classLoaderHandlerClass + .getDeclaredMethod("findClassLoaderOrder", ClassLoader.class, ClassLoaderOrder.class); + findClassLoaderOrder = MethodHandles.lookup().unreflect(findClassLoaderOrderMethod); + } catch (final Exception e) { + throw new RuntimeException( + "Could not find findClassLoaderOrder method for " + classLoaderHandlerClass.getName(), e); + } + try { + final Method findClasspathOrderMethod = classLoaderHandlerClass.getDeclaredMethod( + "findClasspathOrder", ClassLoader.class, ClasspathOrder.class, ScanSpec.class, + LogNode.class); + findClasspathOrder = MethodHandles.lookup().unreflect(findClasspathOrderMethod); + } catch (final Exception e) { + throw new RuntimeException( + "Could not find findClasspathOrder method for " + classLoaderHandlerClass.getName(), e); + } + } + + /** + * Call the static method canHandle(ClassLoader) for the associated {@link ClassLoaderHandler}. + * + * @param classLoader + * the {@link ClassLoader}. + * @return true, if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. + */ + public boolean canHandle(final ClassLoader classLoader) { + try { + return (boolean) canHandle.invokeExact(classLoader); + } catch (final Throwable e) { + throw new RuntimeException( + "Exception while calling canHandle for " + classLoaderHandlerClass.getName(), e); + } + } + + /** + * Call the static method findClassLoaderOrder(ClassLoader, ClassLoaderOrder) for the associated + * {@link ClassLoaderHandler}. + * + * @param classLoader + * the {@link ClassLoader}. + * @param classLoaderOrder + * a {@link ClassLoaderOrder} object. + */ + public void findClassLoaderOrder(final ClassLoader classLoader, final ClassLoaderOrder classLoaderOrder) { + try { + findClassLoaderOrder.invokeExact(classLoader, classLoaderOrder); + } catch (final Throwable e) { + throw new RuntimeException( + "Exception while calling findClassLoaderOrder for " + classLoaderHandlerClass.getName(), e); } } /** - * Instantiate a ClassLoaderHandler, or return null if the class could not be instantiated. + * Call the static method findClasspathOrder(ClassLoader, ClasspathOrder) for the associated + * {@link ClassLoaderHandler}. * + * @param classLoader + * the {@link ClassLoader}. + * @param classpathOrder + * a {@link ClasspathOrder} object. + * @param scanSpec + * the {@link ScanSpec}. * @param log - * The log. - * @return The ClassLoaderHandler instance. + * the log. */ - public ClassLoaderHandler instantiate(final LogNode log) { + public void findClasspathOrder(final ClassLoader classLoader, final ClasspathOrder classpathOrder, + final ScanSpec scanSpec, final LogNode log) { try { - // Instantiate a ClassLoaderHandler - return classLoaderHandlerClass.getDeclaredConstructor().newInstance(); - } catch (final ReflectiveOperationException | ExceptionInInitializerError e) { - if (log != null) { - log.log("Could not instantiate " + classLoaderHandlerClass.getName(), e); - } - return null; + findClasspathOrder.invokeExact(classLoader, classpathOrder, scanSpec, log); + } catch (final Throwable e) { + throw new RuntimeException( + "Exception while calling findClassLoaderOrder for " + classLoaderHandlerClass.getName(), e); } } } 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 5ea1bb0bd..5ca1e114c 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxClassLoaderHandler.java @@ -36,6 +36,7 @@ import java.util.Set; import nonapi.io.github.classgraph.ScanSpec; +import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; import nonapi.io.github.classgraph.utils.LogNode; import nonapi.io.github.classgraph.utils.ReflectionUtils; @@ -44,36 +45,39 @@ * Extract classpath entries from the Eclipse Equinox ClassLoader. */ class EquinoxClassLoaderHandler implements ClassLoaderHandler { - /** Field names. */ private static final List FIELD_NAMES = Collections .unmodifiableList(Arrays.asList("cp", "nestedDirName")); - /** True if system bundles have been read. */ - private boolean alreadyReadSystemBundles; - - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handledClassLoaders() + /** + * True if system bundles have been read. We assume there is only one system bundle on the classpath, so this is + * static. */ - @Override - public String[] handledClassLoaders() { - return new String[] { "org.eclipse.osgi.internal.loader.EquinoxClassLoader" }; - } + private static boolean alreadyReadSystemBundles; - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#getEmbeddedClassLoader(java.lang.ClassLoader) + /** + * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. + * + * @param classLoader + * the {@link ClassLoader}. + * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ - @Override - public ClassLoader getEmbeddedClassLoader(final ClassLoader outerClassLoaderInstance) { - return null; + public static boolean canHandle(final ClassLoader classLoader) { + return "org.eclipse.osgi.internal.loader.EquinoxClassLoader".equals(classLoader.getClass().getName()); } - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#getDelegationOrder(java.lang.ClassLoader) + /** + * 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. */ - @Override - public DelegationOrder getDelegationOrder(final ClassLoader classLoaderInstance) { - return DelegationOrder.PARENT_FIRST; + public static void findClassLoaderOrder(final ClassLoader classLoader, + final ClassLoaderOrder classLoaderOrder) { + classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true); + classLoaderOrder.add(classLoader); } /** @@ -92,8 +96,9 @@ public DelegationOrder getDelegationOrder(final ClassLoader classLoaderInstance) * @param log * the log */ - private void addBundleFile(final Object bundlefile, final Set path, final ClassLoader classLoader, - final ClasspathOrder classpathOrderOut, ScanSpec scanSpec, final LogNode log) { + private static void addBundleFile(final Object bundlefile, final Set path, + final ClassLoader classLoader, final ClasspathOrder classpathOrderOut, final ScanSpec scanSpec, + final LogNode log) { // Don't get stuck in infinite loop if (bundlefile != null && path.add(bundlefile)) { // type File @@ -138,8 +143,8 @@ private void addBundleFile(final Object bundlefile, final Set path, fina * @param log * the log */ - private void addClasspathEntries(final Object owner, final ClassLoader classLoader, - final ClasspathOrder classpathOrderOut, ScanSpec scanSpec, final LogNode log) { + 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); if (entries != null) { @@ -153,17 +158,23 @@ private void addClasspathEntries(final Object owner, final ClassLoader classLoad } } - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handle( - * nonapi.io.github.classgraph.ScanSpec, java.lang.ClassLoader, - * nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) + /** + * 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. */ - @Override - public void handle(final ScanSpec scanSpec, final ClassLoader classLoader, - final ClasspathOrder classpathOrderOut, final LogNode log) { + 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); - addClasspathEntries(manager, classLoader, classpathOrderOut, scanSpec, log); + addClasspathEntries(manager, classLoader, classpathOrder, scanSpec, log); // type FragmentClasspath[] final Object fragments = ReflectionUtils.getFieldVal(manager, "fragments", false); @@ -171,11 +182,10 @@ public void handle(final ScanSpec scanSpec, final ClassLoader classLoader, for (int f = 0, fragLength = Array.getLength(fragments); f < fragLength; f++) { // type FragmentClasspath final Object fragment = Array.get(fragments, f); - addClasspathEntries(fragment, classLoader, classpathOrderOut, scanSpec, log); + addClasspathEntries(fragment, classLoader, classpathOrder, scanSpec, log); } } - // Only read system bundles once (all bundles should give the same results for this). We assume there is - // only one separate Equinox instance on the classpath. + // 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); @@ -209,7 +219,7 @@ public void handle(final ScanSpec scanSpec, final ClassLoader classLoader, final int fileIdx = location.indexOf("file:"); if (fileIdx >= 0) { location = location.substring(fileIdx); - classpathOrderOut.addClasspathEntry(location, classLoader, scanSpec, log); + classpathOrder.addClasspathEntry(location, classLoader, scanSpec, log); } } } 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 00c80f6f2..3b4685a59 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxContextFinderClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxContextFinderClassLoaderHandler.java @@ -29,46 +29,55 @@ package nonapi.io.github.classgraph.classloaderhandler; import nonapi.io.github.classgraph.ScanSpec; +import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; 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 { - - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handledClassLoaders() - */ - @Override - public String[] handledClassLoaders() { - return new String[] { "org.eclipse.osgi.internal.framework.ContextFinder" }; - } - - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#getEmbeddedClassLoader(java.lang.ClassLoader) + /** + * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. + * + * @param classLoader + * the {@link ClassLoader}. + * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ - @Override - public ClassLoader getEmbeddedClassLoader(final ClassLoader outerClassLoaderInstance) { - return (ClassLoader) ReflectionUtils.getFieldVal(outerClassLoaderInstance, "parentContextClassLoader", - false); + public static boolean canHandle(final ClassLoader classLoader) { + return "org.eclipse.osgi.internal.framework.ContextFinder".equals(classLoader.getClass().getName()); } - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#getDelegationOrder(java.lang.ClassLoader) + /** + * 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. */ - @Override - public DelegationOrder getDelegationOrder(final ClassLoader classLoaderInstance) { - return DelegationOrder.PARENT_FIRST; + public static void findClassLoaderOrder(final ClassLoader classLoader, + final ClassLoaderOrder classLoaderOrder) { + classLoaderOrder.delegateTo( + (ClassLoader) ReflectionUtils.getFieldVal(classLoader, "parentContextClassLoader", false), + /* isParent = */ true); + classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true); + classLoaderOrder.add(classLoader); } - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handle( - * nonapi.io.github.classgraph.ScanSpec, java.lang.ClassLoader, - * nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) + /** + * 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. */ - @Override - public void handle(final ScanSpec scanSpec, final ClassLoader classLoader, - final ClasspathOrder classpathOrderOut, final LogNode log) { + public static void findClasspathOrder(final ClassLoader classLoader, final ClasspathOrder classpathOrder, + final ScanSpec scanSpec, final LogNode log) { // Nothing to handle -- embedded parentContextClassLoader will be used instead. } } 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 1bcafc024..1a8e615b1 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FallbackClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FallbackClassLoaderHandler.java @@ -29,6 +29,7 @@ package nonapi.io.github.classgraph.classloaderhandler; import nonapi.io.github.classgraph.ScanSpec; +import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; import nonapi.io.github.classgraph.utils.LogNode; import nonapi.io.github.classgraph.utils.ReflectionUtils; @@ -37,120 +38,126 @@ * Fallback ClassLoaderHandler. Tries to get classpath from a range of possible method and field names. */ class FallbackClassLoaderHandler implements ClassLoaderHandler { - - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handledClassLoaders() - */ - @Override - public String[] handledClassLoaders() { - // The actual string "*" is unimportant here, it is ignored - return new String[] { "*" }; - } - - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#getEmbeddedClassLoader(java.lang.ClassLoader) + /** + * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. + * + * @param classLoader + * the {@link ClassLoader}. + * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ - @Override - public ClassLoader getEmbeddedClassLoader(final ClassLoader outerClassLoaderInstance) { - return null; + public static boolean canHandle(final ClassLoader classLoader) { + // This is the fallback handler, it handles anything + return true; } - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#getDelegationOrder(java.lang.ClassLoader) + /** + * 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. */ - @Override - public DelegationOrder getDelegationOrder(final ClassLoader classLoaderInstance) { - return DelegationOrder.PARENT_FIRST; + public static void findClassLoaderOrder(final ClassLoader classLoader, + final ClassLoaderOrder classLoaderOrder) { + classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true); + classLoaderOrder.add(classLoader); } - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handle( - * nonapi.io.github.classgraph.ScanSpec, java.lang.ClassLoader, - * nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) + /** + * 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. */ - @Override - public void handle(final ScanSpec scanSpec, final ClassLoader classLoader, - final ClasspathOrder classpathOrderOut, final LogNode log) { + public static void findClasspathOrder(final ClassLoader classLoader, final ClasspathOrder classpathOrder, + final ScanSpec scanSpec, final LogNode log) { boolean valid = false; - valid |= classpathOrderOut.addClasspathEntryObject( + valid |= classpathOrder.addClasspathEntryObject( ReflectionUtils.invokeMethod(classLoader, "getClassPath", false), classLoader, scanSpec, log); - valid |= classpathOrderOut.addClasspathEntryObject( + valid |= classpathOrder.addClasspathEntryObject( ReflectionUtils.invokeMethod(classLoader, "getClasspath", false), classLoader, scanSpec, log); - valid |= classpathOrderOut.addClasspathEntryObject( + valid |= classpathOrder.addClasspathEntryObject( ReflectionUtils.invokeMethod(classLoader, "classpath", false), classLoader, scanSpec, log); - valid |= classpathOrderOut.addClasspathEntryObject( + valid |= classpathOrder.addClasspathEntryObject( ReflectionUtils.invokeMethod(classLoader, "classPath", false), classLoader, scanSpec, log); - valid |= classpathOrderOut.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "cp", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "cp", false), classLoader, scanSpec, log); - valid |= classpathOrderOut.addClasspathEntryObject( + valid |= classpathOrder.addClasspathEntryObject( ReflectionUtils.getFieldVal(classLoader, "classpath", false), classLoader, scanSpec, log); - valid |= classpathOrderOut.addClasspathEntryObject( + valid |= classpathOrder.addClasspathEntryObject( ReflectionUtils.getFieldVal(classLoader, "classPath", false), classLoader, scanSpec, log); - valid |= classpathOrderOut.addClasspathEntryObject(ReflectionUtils.getFieldVal(classLoader, "cp", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.getFieldVal(classLoader, "cp", false), classLoader, scanSpec, log); - valid |= classpathOrderOut.addClasspathEntryObject( - ReflectionUtils.invokeMethod(classLoader, "getPath", false), classLoader, scanSpec, log); - valid |= classpathOrderOut.addClasspathEntryObject( + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "getPath", false), + classLoader, scanSpec, log); + valid |= classpathOrder.addClasspathEntryObject( ReflectionUtils.invokeMethod(classLoader, "getPaths", false), classLoader, scanSpec, log); - valid |= classpathOrderOut.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "path", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "path", false), + classLoader, scanSpec, log); + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "paths", false), + classLoader, scanSpec, log); + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.getFieldVal(classLoader, "paths", false), + classLoader, scanSpec, log); + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.getFieldVal(classLoader, "paths", false), classLoader, scanSpec, log); - valid |= classpathOrderOut.addClasspathEntryObject( - ReflectionUtils.invokeMethod(classLoader, "paths", false), classLoader, scanSpec, log); - valid |= classpathOrderOut.addClasspathEntryObject(ReflectionUtils.getFieldVal(classLoader, "paths", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "getDir", false), classLoader, scanSpec, log); - valid |= classpathOrderOut.addClasspathEntryObject(ReflectionUtils.getFieldVal(classLoader, "paths", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "getDirs", false), classLoader, scanSpec, log); - valid |= classpathOrderOut.addClasspathEntryObject( - ReflectionUtils.invokeMethod(classLoader, "getDir", false), classLoader, scanSpec, log); - valid |= classpathOrderOut.addClasspathEntryObject( - ReflectionUtils.invokeMethod(classLoader, "getDirs", false), classLoader, scanSpec, log); - valid |= classpathOrderOut.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "dir", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "dir", false), classLoader, scanSpec, log); - valid |= classpathOrderOut.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "dirs", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "dirs", false), classLoader, scanSpec, log); - valid |= classpathOrderOut.addClasspathEntryObject(ReflectionUtils.getFieldVal(classLoader, "dir", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.getFieldVal(classLoader, "dir", false), classLoader, scanSpec, log); - valid |= classpathOrderOut.addClasspathEntryObject(ReflectionUtils.getFieldVal(classLoader, "dirs", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.getFieldVal(classLoader, "dirs", false), classLoader, scanSpec, log); - valid |= classpathOrderOut.addClasspathEntryObject( - ReflectionUtils.invokeMethod(classLoader, "getFile", false), classLoader, scanSpec, log); - valid |= classpathOrderOut.addClasspathEntryObject( + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "getFile", false), + classLoader, scanSpec, log); + valid |= classpathOrder.addClasspathEntryObject( ReflectionUtils.invokeMethod(classLoader, "getFiles", false), classLoader, scanSpec, log); - valid |= classpathOrderOut.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "file", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "file", false), + classLoader, scanSpec, log); + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "files", false), + classLoader, scanSpec, log); + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.getFieldVal(classLoader, "file", false), + classLoader, scanSpec, log); + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.getFieldVal(classLoader, "files", false), + classLoader, scanSpec, log); + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "getJar", false), + classLoader, scanSpec, log); + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "getJars", false), + classLoader, scanSpec, log); + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "jar", false), + classLoader, scanSpec, log); + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "jars", false), classLoader, scanSpec, log); - valid |= classpathOrderOut.addClasspathEntryObject( - ReflectionUtils.invokeMethod(classLoader, "files", false), classLoader, scanSpec, log); - valid |= classpathOrderOut.addClasspathEntryObject(ReflectionUtils.getFieldVal(classLoader, "file", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.getFieldVal(classLoader, "jar", false), classLoader, scanSpec, log); - valid |= classpathOrderOut.addClasspathEntryObject(ReflectionUtils.getFieldVal(classLoader, "files", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.getFieldVal(classLoader, "jars", false), classLoader, scanSpec, log); - valid |= classpathOrderOut.addClasspathEntryObject( - ReflectionUtils.invokeMethod(classLoader, "getJar", false), classLoader, scanSpec, log); - valid |= classpathOrderOut.addClasspathEntryObject( - ReflectionUtils.invokeMethod(classLoader, "getJars", false), classLoader, scanSpec, log); - valid |= classpathOrderOut.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "jar", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "getURL", false), classLoader, scanSpec, log); - valid |= classpathOrderOut.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "jars", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "getURLs", false), classLoader, scanSpec, log); - valid |= classpathOrderOut.addClasspathEntryObject(ReflectionUtils.getFieldVal(classLoader, "jar", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "getUrl", false), classLoader, scanSpec, log); - valid |= classpathOrderOut.addClasspathEntryObject(ReflectionUtils.getFieldVal(classLoader, "jars", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "getUrls", false), classLoader, scanSpec, log); - valid |= classpathOrderOut.addClasspathEntryObject( - ReflectionUtils.invokeMethod(classLoader, "getURL", false), classLoader, scanSpec, log); - valid |= classpathOrderOut.addClasspathEntryObject( - ReflectionUtils.invokeMethod(classLoader, "getURLs", false), classLoader, scanSpec, log); - valid |= classpathOrderOut.addClasspathEntryObject( - ReflectionUtils.invokeMethod(classLoader, "getUrl", false), classLoader, scanSpec, log); - valid |= classpathOrderOut.addClasspathEntryObject( - ReflectionUtils.invokeMethod(classLoader, "getUrls", false), classLoader, scanSpec, log); - valid |= classpathOrderOut.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "url", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "url", false), classLoader, scanSpec, log); - valid |= classpathOrderOut.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "urls", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.invokeMethod(classLoader, "urls", false), classLoader, scanSpec, log); - valid |= classpathOrderOut.addClasspathEntryObject(ReflectionUtils.getFieldVal(classLoader, "url", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.getFieldVal(classLoader, "url", false), classLoader, scanSpec, log); - valid |= classpathOrderOut.addClasspathEntryObject(ReflectionUtils.getFieldVal(classLoader, "urls", false), + valid |= classpathOrder.addClasspathEntryObject(ReflectionUtils.getFieldVal(classLoader, "urls", false), 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 c7887ba2a..49528a4b5 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FelixClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FelixClassLoaderHandler.java @@ -34,6 +34,7 @@ import java.util.Set; import nonapi.io.github.classgraph.ScanSpec; +import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; import nonapi.io.github.classgraph.utils.LogNode; import nonapi.io.github.classgraph.utils.ReflectionUtils; @@ -47,34 +48,32 @@ * @author elrufaie */ class FelixClassLoaderHandler implements ClassLoaderHandler { - - /** The bundles. */ - private final Set bundles = new HashSet<>(); - - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handledClassLoaders() - */ - @Override - public String[] handledClassLoaders() { - return new String[] { // - "org.apache.felix.framework.BundleWiringImpl$BundleClassLoaderJava5", - "org.apache.felix.framework.BundleWiringImpl$BundleClassLoader" }; - } - - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#getEmbeddedClassLoader(java.lang.ClassLoader) + /** + * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. + * + * @param classLoader + * the {@link ClassLoader}. + * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ - @Override - public ClassLoader getEmbeddedClassLoader(final ClassLoader outerClassLoaderInstance) { - return null; + public static boolean canHandle(final ClassLoader classLoader) { + return "org.apache.felix.framework.BundleWiringImpl$BundleClassLoaderJava5" + .equals(classLoader.getClass().getName()) + || "org.apache.felix.framework.BundleWiringImpl$BundleClassLoader" + .equals(classLoader.getClass().getName()); } - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#getDelegationOrder(java.lang.ClassLoader) + /** + * 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. */ - @Override - public DelegationOrder getDelegationOrder(final ClassLoader classLoaderInstance) { - return DelegationOrder.PARENT_FIRST; + public static void findClassLoaderOrder(final ClassLoader classLoader, + final ClassLoaderOrder classLoaderOrder) { + classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true); + classLoaderOrder.add(classLoader); } /** @@ -84,7 +83,7 @@ public DelegationOrder getDelegationOrder(final ClassLoader classLoaderInstance) * the content object * @return the content location */ - private String getContentLocation(final Object content) { + private static String getContentLocation(final Object content) { final File file = (File) ReflectionUtils.invokeMethod(content, "getFile", false); return file != null ? file.toURI().toString() : null; } @@ -98,13 +97,16 @@ private String getContentLocation(final Object content) { * the classloader * @param classpathOrderOut * the classpath order out + * @param bundles + * the bundles * @param scanSpec * the scan spec * @param log * the log */ - private void addBundle(final Object bundleWiring, final ClassLoader classLoader, - final ClasspathOrder classpathOrderOut, ScanSpec scanSpec, final LogNode log) { + private static void addBundle(final Object bundleWiring, final ClassLoader classLoader, + final ClasspathOrder classpathOrderOut, final Set bundles, final ScanSpec scanSpec, + final LogNode log) { // Track the bundles we've processed to prevent loops bundles.add(bundleWiring); @@ -133,17 +135,24 @@ private void addBundle(final Object bundleWiring, final ClassLoader classLoader, } } - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handle(nonapi.io.github.classgraph.ScanSpec, - * java.lang.ClassLoader, nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) + /** + * 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. */ - @Override - public void handle(final ScanSpec scanSpec, final ClassLoader classLoader, - final ClasspathOrder classpathOrderOut, final LogNode log) { - + public static void findClasspathOrder(final ClassLoader classLoader, final ClasspathOrder classpathOrder, + 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); - addBundle(bundleWiring, classLoader, classpathOrderOut, scanSpec, log); + 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. @@ -154,7 +163,7 @@ public void handle(final ScanSpec scanSpec, final ClassLoader classLoader, for (final Object wire : requiredWires) { final Object provider = ReflectionUtils.invokeMethod(wire, "getProviderWiring", false); if (!bundles.contains(provider)) { - addBundle(provider, classLoader, classpathOrderOut, scanSpec, log); + 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 332605553..c50657a9e 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.Set; import nonapi.io.github.classgraph.ScanSpec; +import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; import nonapi.io.github.classgraph.utils.FileUtils; import nonapi.io.github.classgraph.utils.LogNode; @@ -50,29 +51,29 @@ * https://github.com/jboss-modules/jboss-modules/blob/master/src/main/java/org/jboss/modules/ModuleClassLoader.java */ class JBossClassLoaderHandler implements ClassLoaderHandler { - - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handledClassLoaders() - */ - @Override - public String[] handledClassLoaders() { - return new String[] { "org.jboss.modules.ModuleClassLoader" }; - } - - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#getEmbeddedClassLoader(java.lang.ClassLoader) + /** + * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. + * + * @param classLoader + * the {@link ClassLoader}. + * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ - @Override - public ClassLoader getEmbeddedClassLoader(final ClassLoader outerClassLoaderInstance) { - return null; + public static boolean canHandle(final ClassLoader classLoader) { + return "org.jboss.modules.ModuleClassLoader".equals(classLoader.getClass().getName()); } - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#getDelegationOrder(java.lang.ClassLoader) + /** + * 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. */ - @Override - public DelegationOrder getDelegationOrder(final ClassLoader classLoaderInstance) { - return DelegationOrder.PARENT_FIRST; + public static void findClassLoaderOrder(final ClassLoader classLoader, + final ClassLoaderOrder classLoaderOrder) { + classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true); + classLoaderOrder.add(classLoader); } /** @@ -89,8 +90,8 @@ public DelegationOrder getDelegationOrder(final ClassLoader classLoaderInstance) * @param log * the log */ - private void handleResourceLoader(final Object resourceLoader, final ClassLoader classLoader, - final ClasspathOrder classpathOrderOut, ScanSpec scanSpec, final LogNode log) { + private static void handleResourceLoader(final Object resourceLoader, final ClassLoader classLoader, + final ClasspathOrder classpathOrderOut, final ScanSpec scanSpec, final LogNode log) { if (resourceLoader == null) { return; } @@ -155,8 +156,8 @@ private void handleResourceLoader(final Object resourceLoader, final ClassLoader * @param log * the log */ - private void handleRealModule(final Object module, final Set visitedModules, - final ClassLoader classLoader, final ClasspathOrder classpathOrderOut, ScanSpec scanSpec, + private static void handleRealModule(final Object module, final Set visitedModules, + 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 @@ -183,13 +184,20 @@ private void handleRealModule(final Object module, final Set visitedModu } } - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handle(nonapi.io.github.classgraph.ScanSpec, - * java.lang.ClassLoader, nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) + /** + * 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. */ - @Override - public void handle(final ScanSpec scanSpec, final ClassLoader classLoader, - final ClasspathOrder classpathOrderOut, final LogNode log) { + 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 Set visitedModules = new HashSet<>(); @@ -201,7 +209,7 @@ public void handle(final ScanSpec scanSpec, final ClassLoader classLoader, final Object val = ent.getValue(); // type Module final Object realModule = ReflectionUtils.invokeMethod(val, "getModule", false); - handleRealModule(realModule, visitedModules, classLoader, classpathOrderOut, scanSpec, log); + handleRealModule(realModule, visitedModules, classLoader, classpathOrder, scanSpec, log); } // type Map> @SuppressWarnings("unchecked") @@ -213,7 +221,7 @@ public void handle(final ScanSpec scanSpec, final ClassLoader classLoader, final Object moduleClassLoader = ReflectionUtils.getFieldVal(localLoader, "this$0", false); // type Module final Object realModule = ReflectionUtils.getFieldVal(moduleClassLoader, "module", false); - handleRealModule(realModule, visitedModules, classLoader, classpathOrderOut, scanSpec, log); + 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 fb805b2bf..b6a06e33c 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JPMSClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JPMSClassLoaderHandler.java @@ -29,6 +29,7 @@ package nonapi.io.github.classgraph.classloaderhandler; import nonapi.io.github.classgraph.ScanSpec; +import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; import nonapi.io.github.classgraph.utils.LogNode; @@ -37,40 +38,45 @@ * them (module scanning uses a different mechanism from classpath scanning). */ class JPMSClassLoaderHandler implements ClassLoaderHandler { - - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handledClassLoaders() - */ - @Override - public String[] handledClassLoaders() { - return new String[] { // - "jdk.internal.loader.ClassLoaders$AppClassLoader", // - "jdk.internal.loader.BuiltinClassLoader" }; - } - - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#getEmbeddedClassLoader(java.lang.ClassLoader) + /** + * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. + * + * @param classLoader + * the {@link ClassLoader}. + * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ - @Override - public ClassLoader getEmbeddedClassLoader(final ClassLoader outerClassLoaderInstance) { - return null; + public static boolean canHandle(final ClassLoader classLoader) { + return "jdk.internal.loader.ClassLoaders$AppClassLoader".equals(classLoader.getClass().getName()) + || "jdk.internal.loader.BuiltinClassLoader".equals(classLoader.getClass().getName()); } - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#getDelegationOrder(java.lang.ClassLoader) + /** + * 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. */ - @Override - public DelegationOrder getDelegationOrder(final ClassLoader classLoaderInstance) { - return DelegationOrder.PARENT_FIRST; + public static void findClassLoaderOrder(final ClassLoader classLoader, + final ClassLoaderOrder classLoaderOrder) { + // Don't add this classLoader or its parents to the classloader order -- modules are handled separately } - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handle(nonapi.io.github.classgraph.ScanSpec, - * java.lang.ClassLoader, nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) + /** + * 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. */ - @Override - public void handle(final ScanSpec scanSpec, final ClassLoader classLoader, - final ClasspathOrder classpathOrderOut, final LogNode log) { + public static void findClasspathOrder(final ClassLoader classLoader, final ClasspathOrder classpathOrder, + final ScanSpec scanSpec, final LogNode log) { // The JDK9 classloaders have a field, URLClassPath ucp, containing URLs for unnamed modules, // but it is not visible. } 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 d1f6bfcdf..19ead93a3 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/OSGiDefaultClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/OSGiDefaultClassLoaderHandler.java @@ -31,6 +31,7 @@ import java.io.File; import nonapi.io.github.classgraph.ScanSpec; +import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; import nonapi.io.github.classgraph.utils.LogNode; import nonapi.io.github.classgraph.utils.ReflectionUtils; @@ -41,46 +42,53 @@ * @author lukehutch */ class OSGiDefaultClassLoaderHandler implements ClassLoaderHandler { - - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handledClassLoaders() - */ - @Override - public String[] handledClassLoaders() { - return new String[] { "org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader" }; - } - - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#getEmbeddedClassLoader(java.lang.ClassLoader) + /** + * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. + * + * @param classLoader + * the {@link ClassLoader}. + * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ - @Override - public ClassLoader getEmbeddedClassLoader(final ClassLoader outerClassLoaderInstance) { - return null; + public static boolean canHandle(final ClassLoader classLoader) { + return "org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader".equals(classLoader.getClass().getName()); } - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#getDelegationOrder(java.lang.ClassLoader) + /** + * 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. */ - @Override - public DelegationOrder getDelegationOrder(final ClassLoader classLoaderInstance) { - return DelegationOrder.PARENT_FIRST; + public static void findClassLoaderOrder(final ClassLoader classLoader, + final ClassLoaderOrder classLoaderOrder) { + classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true); + classLoaderOrder.add(classLoader); } - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handle(nonapi.io.github.classgraph.ScanSpec, - * java.lang.ClassLoader, nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) + /** + * 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. */ - @Override - public void handle(final ScanSpec scanSpec, final ClassLoader classloader, - final ClasspathOrder classpathOrderOut, final LogNode log) { - final Object classpathManager = ReflectionUtils.invokeMethod(classloader, "getClasspathManager", false); + 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); 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); if (baseFile != null) { - classpathOrderOut.addClasspathEntry(baseFile.getPath(), classloader, scanSpec, log); + 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 fd11f16c5..50a84a213 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ParentLastDelegationOrderTestClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ParentLastDelegationOrderTestClassLoaderHandler.java @@ -29,46 +29,56 @@ package nonapi.io.github.classgraph.classloaderhandler; import nonapi.io.github.classgraph.ScanSpec; +import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; 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 { - - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handledClassLoaders() - */ - @Override - public String[] handledClassLoaders() { - return new String[] { "io.github.classgraph.issues.issue267.FakeRestartClassLoader" }; - } - - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#getEmbeddedClassLoader(java.lang.ClassLoader) + /** + * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. + * + * @param classLoader + * the {@link ClassLoader}. + * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ - @Override - public ClassLoader getEmbeddedClassLoader(final ClassLoader outerClassLoaderInstance) { - return null; + public static boolean canHandle(final ClassLoader classLoader) { + return "io.github.classgraph.issues.issue267.FakeRestartClassLoader" + .equals(classLoader.getClass().getName()); } - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#getDelegationOrder(java.lang.ClassLoader) + /** + * 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. */ - @Override - public DelegationOrder getDelegationOrder(final ClassLoader classLoaderInstance) { - return DelegationOrder.PARENT_LAST; + public static void findClassLoaderOrder(final ClassLoader classLoader, + final ClassLoaderOrder classLoaderOrder) { + // Add self first, then delegate to parent + classLoaderOrder.add(classLoader); + classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true); } - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handle(nonapi.io.github.classgraph.ScanSpec, - * java.lang.ClassLoader, nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) + /** + * 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. */ - @Override - public void handle(final ScanSpec scanSpec, final ClassLoader classLoader, - final ClasspathOrder classpathOrderOut, final LogNode log) { + 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); - classpathOrderOut.addClasspathEntry(classpath, classLoader, scanSpec, log); + classpathOrder.addClasspathEntry(classpath, classLoader, scanSpec, log); } } 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 27b6b0488..86303a452 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/SpringBootRestartClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/SpringBootRestartClassLoaderHandler.java @@ -28,7 +28,9 @@ */ package nonapi.io.github.classgraph.classloaderhandler; +import io.github.classgraph.ClassGraph; import nonapi.io.github.classgraph.ScanSpec; +import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; import nonapi.io.github.classgraph.utils.LogNode; @@ -40,32 +42,38 @@ * handler for that class loader also has to delegate in PARENT_LAST order. */ class SpringBootRestartClassLoaderHandler implements ClassLoaderHandler { - /** - * The handler delegate. Spring Boot's devtools class loader is an extension of URLClassLoader, so there's no - * need to use reflection to access the supported URLs, and we can delegate the handling to an internal instance - * of URLClassLoaderHandler. - */ - private final URLClassLoaderHandler handlerDelegate = new URLClassLoaderHandler(); - - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handledClassLoaders() + * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. + * + * @param classLoader + * the {@link ClassLoader}. + * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ - @Override - public String[] handledClassLoaders() { - return new String[] { // - "org.springframework.boot.devtools.restart.classloader.RestartClassLoader" }; + public static boolean canHandle(final ClassLoader classLoader) { + return "org.springframework.boot.devtools.restart.classloader.RestartClassLoader" + .equals(classLoader.getClass().getName()); } - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#getEmbeddedClassLoader(java.lang.ClassLoader) + /** + * 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. */ - @Override - public ClassLoader getEmbeddedClassLoader(final ClassLoader outerClassLoaderInstance) { - return null; + public static void findClassLoaderOrder(final ClassLoader classLoader, + final ClassLoaderOrder classLoaderOrder) { + // The Restart classloader is a parent-last classloader, so need to delegate to the context + // classloader(s) before the parent. + classLoaderOrder.delegateTo(Thread.currentThread().getContextClassLoader(), /* isParent = */ false); + classLoaderOrder.delegateTo(ClassGraph.class.getClassLoader(), /* isParent = */ false); + classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true); } /** + * Find the classpath entries for the associated {@link ClassLoader}. + * * Spring Boot's RestartClassLoader sits in front of the parent class loader and watches a given set of * directories for changes. While those classes are reachable from the parent class loader directly, they should * always be loaded through direct access from the RestartClassLoader until it's completely turned of by means @@ -77,21 +85,17 @@ public ClassLoader getEmbeddedClassLoader(final ClassLoader outerClassLoaderInst * See: #267, * #268 * - * @param classLoaderInstance - * the class loader instance - * @return the delegation order - */ - @Override - public ClassLoaderHandler.DelegationOrder getDelegationOrder(final ClassLoader classLoaderInstance) { - return DelegationOrder.PARENT_LAST; - } - - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handle(nonapi.io.github.classgraph.ScanSpec, java.lang.ClassLoader, nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) + * @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. */ - @Override - public void handle(final ScanSpec scanSpec, final ClassLoader classLoader, - final ClasspathOrder classpathOrderOut, final LogNode log) { - handlerDelegate.handle(scanSpec, classLoader, classpathOrderOut, log); + public static void findClasspathOrder(final ClassLoader classLoader, final ClasspathOrder classpathOrder, + final ScanSpec scanSpec, final LogNode log) { + // The Restart classloader doesn't itself store any URLs } } \ No newline at end of file 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 42a70805d..36dd357f8 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java @@ -32,51 +32,57 @@ import java.util.List; import nonapi.io.github.classgraph.ScanSpec; +import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; 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 { - - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handledClassLoaders() - */ - @Override - public String[] handledClassLoaders() { - return new String[] { // - "org.apache.catalina.loader.WebappClassLoaderBase", // - }; - } - - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#getEmbeddedClassLoader(java.lang.ClassLoader) + /** + * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. + * + * @param classLoader + * the {@link ClassLoader}. + * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ - @Override - public ClassLoader getEmbeddedClassLoader(final ClassLoader outerClassLoaderInstance) { - return null; + public static boolean canHandle(final ClassLoader classLoader) { + return "org.apache.catalina.loader.WebappClassLoaderBase".equals(classLoader.getClass().getName()); } - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#getDelegationOrder(java.lang.ClassLoader) + /** + * 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. */ - @Override - public DelegationOrder getDelegationOrder(final ClassLoader classLoaderInstance) { - return DelegationOrder.PARENT_FIRST; + public static void findClassLoaderOrder(final ClassLoader classLoader, + final ClassLoaderOrder classLoaderOrder) { + classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true); + classLoaderOrder.add(classLoader); } - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handle(nonapi.io.github.classgraph.ScanSpec, - * java.lang.ClassLoader, nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) + /** + * 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. */ - @Override - public void handle(final ScanSpec scanSpec, final ClassLoader classLoader, - final ClasspathOrder classpathOrderOut, final LogNode 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); - classpathOrderOut.addClasspathEntryObject(baseURLs, classLoader, scanSpec, log); + classpathOrder.addClasspathEntryObject(baseURLs, classLoader, scanSpec, log); // type List> // members: preResources, mainResources, classResources, jarResources, postResources @SuppressWarnings("unchecked") @@ -117,12 +123,12 @@ public void handle(final ScanSpec scanSpec, final ClassLoader classLoader, final String internalPath = (String) ReflectionUtils.invokeMethod(webResourceSet, "getInternalPath", false); if (internalPath != null && !internalPath.isEmpty() && !internalPath.equals("/")) { - classpathOrderOut.addClasspathEntryObject( + classpathOrder.addClasspathEntryObject( base + (isJar ? "!" : "") + (internalPath.startsWith("/") ? internalPath : "/" + internalPath), classLoader, scanSpec, log); } else { - classpathOrderOut.addClasspathEntryObject(base, classLoader, scanSpec, log); + classpathOrder.addClasspathEntryObject(base, classLoader, scanSpec, log); } } } @@ -130,6 +136,6 @@ public void handle(final ScanSpec scanSpec, final ClassLoader classLoader, } // This may or may not duplicate the above final Object urls = ReflectionUtils.invokeMethod(classLoader, "getURLs", false); - classpathOrderOut.addClasspathEntryObject(urls, classLoader, scanSpec, log); + classpathOrder.addClasspathEntryObject(urls, classLoader, scanSpec, log); } } 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 e25c2bb56..3b863d972 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/URLClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/URLClassLoaderHandler.java @@ -32,48 +32,56 @@ import java.net.URLClassLoader; import nonapi.io.github.classgraph.ScanSpec; +import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; import nonapi.io.github.classgraph.utils.LogNode; /** ClassLoaderHandler that is able to extract the URLs from a URLClassLoader. */ class URLClassLoaderHandler implements ClassLoaderHandler { - - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handledClassLoaders() - */ - @Override - public String[] handledClassLoaders() { - return new String[] { "java.net.URLClassLoader" }; - } - - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#getEmbeddedClassLoader(java.lang.ClassLoader) + /** + * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. + * + * @param classLoader + * the {@link ClassLoader}. + * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ - @Override - public ClassLoader getEmbeddedClassLoader(final ClassLoader outerClassLoaderInstance) { - return null; + public static boolean canHandle(final ClassLoader classLoader) { + return "java.net.URLClassLoader".equals(classLoader.getClass().getName()); } - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#getDelegationOrder(java.lang.ClassLoader) + /** + * 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. */ - @Override - public DelegationOrder getDelegationOrder(final ClassLoader classLoaderInstance) { - return DelegationOrder.PARENT_FIRST; + public static void findClassLoaderOrder(final ClassLoader classLoader, + final ClassLoaderOrder classLoaderOrder) { + classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true); + classLoaderOrder.add(classLoader); } - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handle(nonapi.io.github.classgraph.ScanSpec, - * java.lang.ClassLoader, nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) + /** + * 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. */ - @Override - public void handle(final ScanSpec scanSpec, final ClassLoader classLoader, - final ClasspathOrder classpathOrderOut, final LogNode log) { + public static void findClasspathOrder(final ClassLoader classLoader, final ClasspathOrder classpathOrder, + final ScanSpec scanSpec, final LogNode log) { final URL[] urls = ((URLClassLoader) classLoader).getURLs(); if (urls != null) { for (final URL url : urls) { if (url != null) { - classpathOrderOut.addClasspathEntry(url.toString(), classLoader, scanSpec, log); + classpathOrder.addClasspathEntry(url.toString(), classLoader, scanSpec, log); } } } 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 686f9f215..eb03e69af 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WeblogicClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WeblogicClassLoaderHandler.java @@ -29,55 +29,62 @@ package nonapi.io.github.classgraph.classloaderhandler; import nonapi.io.github.classgraph.ScanSpec; +import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; 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 { - - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handledClassLoaders() - */ - @Override - public String[] handledClassLoaders() { - return new String[] { // - "weblogic.utils.classloaders.ChangeAwareClassLoader", // - "weblogic.utils.classloaders.GenericClassLoader", // - "weblogic.utils.classloaders.FilteringClassLoader", // - // TODO: other known classloader names: - // weblogic.servlet.jsp.JspClassLoader - // weblogic.servlet.jsp.TagFileClassLoader - }; - } - - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#getEmbeddedClassLoader(java.lang.ClassLoader) + /** + * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. + * + * @param classLoader + * the {@link ClassLoader}. + * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ - @Override - public ClassLoader getEmbeddedClassLoader(final ClassLoader outerClassLoaderInstance) { - return null; + public static boolean canHandle(final ClassLoader classLoader) { + return "weblogic.utils.classloaders.ChangeAwareClassLoader".equals(classLoader.getClass().getName()) + || "weblogic.utils.classloaders.GenericClassLoader".equals(classLoader.getClass().getName()) + || "weblogic.utils.classloaders.FilteringClassLoader".equals(classLoader.getClass().getName()) + // TODO: The following two known classloader names have not been tested, and the fields/methods + // may not match those of the above classloaders. + || "weblogic.utils.classloaders.GenericClassLoader".equals(classLoader.getClass().getName()) + || "weblogic.utils.classloaders.FilteringClassLoader".equals(classLoader.getClass().getName()); } - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#getDelegationOrder(java.lang.ClassLoader) + /** + * 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. */ - @Override - public DelegationOrder getDelegationOrder(final ClassLoader classLoaderInstance) { - return DelegationOrder.PARENT_FIRST; + public static void findClassLoaderOrder(final ClassLoader classLoader, + final ClassLoaderOrder classLoaderOrder) { + classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true); + classLoaderOrder.add(classLoader); } - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handle(nonapi.io.github.classgraph.ScanSpec, - * java.lang.ClassLoader, nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) + /** + * 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. */ - @Override - public void handle(final ScanSpec scanSpec, final ClassLoader classLoader, - final ClasspathOrder classpathOrderOut, final LogNode log) { - classpathOrderOut.addClasspathEntries( // + public static void findClasspathOrder(final ClassLoader classLoader, final ClasspathOrder classpathOrder, + final ScanSpec scanSpec, final LogNode log) { + classpathOrder.addClasspathEntries( // (String) ReflectionUtils.invokeMethod(classLoader, "getFinderClassPath", false), classLoader, scanSpec, log); - classpathOrderOut.addClasspathEntries( // + classpathOrder.addClasspathEntries( // (String) ReflectionUtils.invokeMethod(classLoader, "getClassPath", false), 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 f3bf0b8d8..a25350c49 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java @@ -32,6 +32,7 @@ import java.util.List; import nonapi.io.github.classgraph.ScanSpec; +import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; import nonapi.io.github.classgraph.utils.LogNode; import nonapi.io.github.classgraph.utils.ReflectionUtils; @@ -45,7 +46,6 @@ * @author R. Kempees */ class WebsphereLibertyClassLoaderHandler implements ClassLoaderHandler { - /** {@code "com.ibm.ws.classloading.internal."} */ private static final String PKG_PREFIX = "com.ibm.ws.classloading.internal."; @@ -55,29 +55,30 @@ class WebsphereLibertyClassLoaderHandler implements ClassLoaderHandler { /** {@code "com.ibm.ws.classloading.internal.ThreadContextClassLoader"} */ private static final String IBM_THREAD_CONTEXT_CLASS_LOADER = PKG_PREFIX + "ThreadContextClassLoader"; - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handledClassLoaders() - */ - @Override - public String[] handledClassLoaders() { - return new String[] { IBM_APP_CLASS_LOADER, IBM_THREAD_CONTEXT_CLASS_LOADER }; - } - - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#getEmbeddedClassLoader(java.lang.ClassLoader) + /** + * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. + * + * @param classLoader + * the {@link ClassLoader}. + * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ - @Override - public ClassLoader getEmbeddedClassLoader(final ClassLoader outerClassLoaderInstance) { - return null; + public static boolean canHandle(final ClassLoader classLoader) { + return IBM_APP_CLASS_LOADER.equals(classLoader.getClass().getName()) + || IBM_THREAD_CONTEXT_CLASS_LOADER.equals(classLoader.getClass().getName()); } - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#getDelegationOrder(java.lang.ClassLoader) + /** + * 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. */ - @Override - public DelegationOrder getDelegationOrder(final ClassLoader classLoaderInstance) { - // TODO: Read correct delegation order from ClassLoader - return DelegationOrder.PARENT_FIRST; + public static void findClassLoaderOrder(final ClassLoader classLoader, + final ClassLoaderOrder classLoaderOrder) { + classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true); + classLoaderOrder.add(classLoader); } /** @@ -87,7 +88,7 @@ public DelegationOrder getDelegationOrder(final ClassLoader classLoaderInstance) * the classpath object * @return the path object as a {@link File} or {@link String}. */ - private String getPath(final Object classpath) { + private static String getPath(final Object classpath) { final Object container = ReflectionUtils.getFieldVal(classpath, "container", false); if (container == null) { return ""; @@ -117,13 +118,20 @@ private String getPath(final Object classpath) { return ""; } - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handle(nonapi.io.github.classgraph.ScanSpec, - * java.lang.ClassLoader, nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) + /** + * 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. */ - @Override - public void handle(final ScanSpec scanSpec, final ClassLoader classLoader, - final ClasspathOrder classpathOrderOut, final LogNode log) { + 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); if (appLoader != null) { @@ -138,7 +146,7 @@ public void handle(final ScanSpec scanSpec, final ClassLoader classLoader, for (final Object classpath : classPathElements) { final String path = getPath(classpath); if (path != null && path.length() > 0) { - classpathOrderOut.addClasspathEntry(path, classLoader, scanSpec, log); + 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 2cddaf680..e858f0cf1 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereTraditionalClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereTraditionalClassLoaderHandler.java @@ -29,6 +29,7 @@ package nonapi.io.github.classgraph.classloaderhandler; import nonapi.io.github.classgraph.ScanSpec; +import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; import nonapi.io.github.classgraph.utils.LogNode; import nonapi.io.github.classgraph.utils.ReflectionUtils; @@ -39,44 +40,48 @@ * @author lukehutch */ class WebsphereTraditionalClassLoaderHandler implements ClassLoaderHandler { - - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handledClassLoaders() - */ - @Override - public String[] handledClassLoaders() { - // All three class loaders implement the getClassPath method call. - return new String[] { // - "com.ibm.ws.classloader.CompoundClassLoader", // - "com.ibm.ws.classloader.ProtectionClassLoader", // - "com.ibm.ws.bootstrap.ExtClassLoader" }; - } - - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#getEmbeddedClassLoader(java.lang.ClassLoader) + /** + * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. + * + * @param classLoader + * the {@link ClassLoader}. + * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ - @Override - public ClassLoader getEmbeddedClassLoader(final ClassLoader outerClassLoaderInstance) { - return null; + public static boolean canHandle(final ClassLoader classLoader) { + return "com.ibm.ws.classloader.CompoundClassLoader".equals(classLoader.getClass().getName()) + || "com.ibm.ws.classloader.ProtectionClassLoader".equals(classLoader.getClass().getName()) + || "com.ibm.ws.bootstrap.ExtClassLoader".equals(classLoader.getClass().getName()); } - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#getDelegationOrder(java.lang.ClassLoader) + /** + * 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. */ - @Override - public DelegationOrder getDelegationOrder(final ClassLoader classLoaderInstance) { - // TODO: Read correct delegation order from ClassLoader - return DelegationOrder.PARENT_FIRST; + public static void findClassLoaderOrder(final ClassLoader classLoader, + final ClassLoaderOrder classLoaderOrder) { + classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true); + classLoaderOrder.add(classLoader); } - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handle(nonapi.io.github.classgraph.ScanSpec, - * java.lang.ClassLoader, nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) + /** + * 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. */ - @Override - public void handle(final ScanSpec scanSpec, final ClassLoader classloader, - final ClasspathOrder classpathOrderOut, final LogNode log) { - final String classpath = (String) ReflectionUtils.invokeMethod(classloader, "getClassPath", false); - classpathOrderOut.addClasspathEntries(classpath, classloader, scanSpec, log); + 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); + classpathOrder.addClasspathEntries(classpath, classLoader, scanSpec, log); } } diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderOrder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderOrder.java new file mode 100644 index 000000000..964548276 --- /dev/null +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderOrder.java @@ -0,0 +1,160 @@ +/* + * 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.classpath; + +import java.util.AbstractMap.SimpleEntry; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +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; + +/** A class to find all unique classloaders. */ +public class ClassLoaderOrder { + /** The {@link ClassLoader} order. */ + private final List> classLoaderOrder = new ArrayList<>(); + + /** + * 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<>(); + + /** + * 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<>(); + + /** + * 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<>(); + + /** A map from {@link ClassLoader} to {@link ClassLoaderHandlerRegistryEntry}. */ + private final Map classLoaderToClassLoaderHandlerRegistryEntry = // + new HashMap<>(); + + /** + * Get the {@link ClassLoader} order. + * + * @return the {@link ClassLoader} order, as a pair: {@link ClassLoader}, + * {@link ClassLoaderHandlerRegistryEntry}. + */ + public List> getClassLoaderOrder() { + return classLoaderOrder; + } + + /** + * Get the all parent classloaders. + * + * @return all parent classloaders + */ + public Set getAllParentClassLoaders() { + return allParentClassLoaders; + } + + /** + * Find the {@link ClassLoaderHandler} that can handle a given {@link ClassLoader} instance. + * + * @param classLoader + * the {@link ClassLoader}. + * @return the {@link ClassLoaderHandlerRegistryEntry} for the {@link ClassLoader}. + */ + private ClassLoaderHandlerRegistryEntry getRegistryEntry(final ClassLoader classLoader) { + ClassLoaderHandlerRegistryEntry entry = classLoaderToClassLoaderHandlerRegistryEntry.get(classLoader); + if (entry == null) { + // Find a ClassLoaderHandler that can handle the ClassLoader + for (final ClassLoaderHandlerRegistryEntry handler : ClassLoaderHandlerRegistry.CLASS_LOADER_HANDLERS) { + if (handler.canHandle(classLoader)) { + // This ClassLoaderHandler can handle the ClassLoader + entry = handler; + break; + } + } + if (entry == null) { + // Use fallback handler + entry = ClassLoaderHandlerRegistry.FALLBACK_HANDLER; + } + classLoaderToClassLoaderHandlerRegistryEntry.put(classLoader, entry); + } + return entry; + } + + /** + * Adds the. + * + * @param classLoader + * the class loader + */ + public void add(final ClassLoader classLoader) { + if (classLoader == null) { + return; + } + if (added.add(classLoader)) { + final ClassLoaderHandlerRegistryEntry entry = getRegistryEntry(classLoader); + if (entry != null) { + classLoaderOrder.add(new SimpleEntry<>(classLoader, entry)); + } + } + } + + /** + * Delegate to. + * + * @param classLoader + * the class loader + */ + public void delegateTo(final ClassLoader classLoader, final boolean isParent) { + if (classLoader == null) { + return; + } + // Check if this is a parent before checking if the classloader is already in the delegatedTo set, + // so that if the classloader is a context classloader but also a parent, it still gets marked as + // a parent classloader. + if (isParent) { + allParentClassLoaders.add(classLoader); + } + if (delegatedTo.add(classLoader)) { + final ClassLoaderHandlerRegistryEntry entry = getRegistryEntry(classLoader); + if (entry != null) { + // Delegate to the named classloader, by recursing to that classloader to get its classloader order + entry.findClassLoaderOrder(classLoader, this); + } + } + } +} 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 9edb1a022..fb7c0a3e2 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java @@ -28,20 +28,12 @@ */ package nonapi.io.github.classgraph.classpath; -import java.util.AbstractMap.SimpleEntry; -import java.util.ArrayDeque; import java.util.ArrayList; -import java.util.Deque; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashSet; import java.util.List; -import java.util.Map; import java.util.Map.Entry; import java.util.Set; import nonapi.io.github.classgraph.ScanSpec; -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.utils.FastPathResolver; @@ -94,220 +86,6 @@ public ClassLoader[] getClassLoaderOrderRespectingParentDelegation() { // ------------------------------------------------------------------------------------------------------------- - /** - * Find a {@link ClassLoaderHandler} that can handle a given {@link ClassLoader}. - * - * @param classLoader - * the classloader - * @param visitedEmbedded - * the visited embedded classloaders - * @param allClassLoaderHandlerRegistryEntries - * the ClassLoaderHandler registry entries - * @param log - * the log - * @return the {@link ClassLoaderHandler} - */ - private static ClassLoaderHandler findClassLoaderHandler(final ClassLoader classLoader, - final Set visitedEmbedded, - final List allClassLoaderHandlerRegistryEntries, final LogNode log) { - // Iterate through each superclass of the classloader - final Class classLoaderClass = classLoader.getClass(); - for (Class superclass = classLoaderClass; superclass != null; superclass = superclass.getSuperclass()) { - final String superclassName = superclass.getName(); - // Compare against the class names handled by each ClassLoaderHandler - for (final ClassLoaderHandlerRegistryEntry registryEntry : allClassLoaderHandlerRegistryEntries) { - for (final String handledClassLoaderName : registryEntry.handledClassLoaderNames) { - if (handledClassLoaderName.equals(superclassName)) { - // This ClassLoaderHandler can handle this class -- instantiate it - final ClassLoaderHandler classLoaderHandler = registryEntry.instantiate(log); - if (classLoaderHandler != null) { - // If there is an embedded classloader, recursively find the ClassLoaderHandler - // for the embedded classloader - final ClassLoader embeddedClassLoader = classLoaderHandler - .getEmbeddedClassLoader(classLoader); - if (embeddedClassLoader != null && embeddedClassLoader != classLoader - && visitedEmbedded.add(embeddedClassLoader)) { - if (log != null) { - log.log("Delegating from " + classLoader.getClass().getName() - + " to embedded ClassLoader " - + embeddedClassLoader.getClass().getName()); - } - return findClassLoaderHandler(embeddedClassLoader, visitedEmbedded, - allClassLoaderHandlerRegistryEntries, log); - } - // Otherwise, return the found ClassLoaderHandler - if (log != null) { - log.log("ClassLoader " + classLoader.getClass().getName() + " will be handled by " - + classLoaderHandler.getClass().getName()); - } - return classLoaderHandler; - } - } - } - } - } - final ClassLoaderHandler classLoaderHandler = ClassLoaderHandlerRegistry.FALLBACK_CLASS_LOADER_HANDLER - .instantiate(log); - if (classLoaderHandler == null) { - // Should not happen - throw new RuntimeException("Could not instantiate fallback ClassLoaderHandler"); - } - if (log != null) { - log.log("ClassLoader " + classLoader.getClass().getName() + " will be handled by " - + classLoaderHandler.getClass().getName()); - } - return classLoaderHandler; - } - - /** - * Perform a topological sort on classloader delegation ordering constraints. - * - * @param curr - * the current classloader - * @param visited - * the visited classloaders - * @param orderingConstraints - * the ordering constraints - * @param orderOut - * the order out - */ - private static void topoSort(final ClassLoader curr, final Set visited, - final Map> orderingConstraints, final Deque orderOut) { - if (visited.add(curr)) { - final List downstreamNodes = orderingConstraints.get(curr); - if (downstreamNodes != null) { - for (final ClassLoader downstreamNode : downstreamNodes) { - topoSort(downstreamNode, visited, orderingConstraints, orderOut); - } - } - orderOut.push(curr); - } - } - - /** - * Add an ordering constraint between two classloaders. - * - * @param processFirst - * the first classloader to try loading a class from - * @param processSecond - * the second classloader to try loading a class from - * @param orderingConstraints - * the ordering constraints - * @param allDownstreamClassLoaders - * all downstream class loaders - */ - private static void addOrderingConstraint(final ClassLoader processFirst, final ClassLoader processSecond, - final Map> orderingConstraints, - final Set allDownstreamClassLoaders) { - List immediateDownstreamClassLoaders = orderingConstraints.get(processFirst); - if (immediateDownstreamClassLoaders == null) { - orderingConstraints.put(processFirst, immediateDownstreamClassLoaders = new ArrayList<>()); - } - immediateDownstreamClassLoaders.add(processSecond); - allDownstreamClassLoaders.add(processSecond); - } - - /** - * Recursively find the {@link ClassLoaderHandler} that can handle each ClassLoader and its parent(s), - * respecting parent delegation order (PARENT_FIRST or PARENT_LAST). - * - * @param scanSpec - * the scan spec - * @param contextClassLoaders - * the context classloader order - * @param allClassLoaderHandlerRegistryEntries - * the ClassLoaderHandler registry entries - * @param classLoaderAndHandlerOrderOut - * the classloader and ClassLoaderHandler order - * @param ignoredClassLoaderAndHandlerOrderOut - * the ignored classloader and handler order out - * @param log - * the log - */ - private void findClassLoaderHandlerForClassLoaderAndParents(final ScanSpec scanSpec, - final ClassLoader[] contextClassLoaders, - final List allClassLoaderHandlerRegistryEntries, - final List> classLoaderAndHandlerOrderOut, - final List> ignoredClassLoaderAndHandlerOrderOut, - final LogNode log) { - // Iterate through all classloaders and their ancestors. - // allClassLoaders is a LinkedHashSet so that the order in which nodes are added, when iterating - // depth first starting at contextClassLoaders, is preserved. - final Set allClassLoaders = new LinkedHashSet<>(); - final Map classLoaderToClassLoaderHandler = new HashMap<>(); - final Map> orderingConstraints = new HashMap<>(); - final Set visitedEmbedded = new HashSet<>(); - final Set allParentClassLoaders = new HashSet<>(); - final Set allDownstreamClassLoaders = new HashSet<>(); - for (final ClassLoader classLoader : contextClassLoaders) { - // Iterate through classloader parent hierarchy, trying to find a ClassLoaderHandler that can - // handle the classloader - for (ClassLoader ancestorClassLoader = classLoader; ancestorClassLoader != null; // - ancestorClassLoader = ancestorClassLoader.getParent()) { - // Only process each ancestor classloader once - if (allClassLoaders.add(ancestorClassLoader)) { - // Find ClassLoaderHandler for ancestor classloader (will be fallback handler if none found) - final ClassLoaderHandler classLoaderHandler = findClassLoaderHandler(ancestorClassLoader, - visitedEmbedded, allClassLoaderHandlerRegistryEntries, log); - classLoaderToClassLoaderHandler.put(ancestorClassLoader, classLoaderHandler); - - // Find delegation order of ancestor classloader relative to its parent - final ClassLoader ancestorParent = ancestorClassLoader.getParent(); - if (ancestorParent != null) { - // Record parents - allParentClassLoaders.add(ancestorParent); - - // Build a DAG using the delegation order - switch (classLoaderHandler.getDelegationOrder(ancestorClassLoader)) { - case PARENT_FIRST: - addOrderingConstraint(ancestorParent, ancestorClassLoader, orderingConstraints, - allDownstreamClassLoaders); - break; - case PARENT_LAST: - addOrderingConstraint(ancestorClassLoader, ancestorParent, orderingConstraints, - allDownstreamClassLoaders); - break; - default: - // Should not happen - throw new RuntimeException("Invalid delegation order"); - } - } - } else { - // Already processed this ancestor and all of its ancestors - break; - } - } - } - - // Find all DAG root nodes, which are all nodes that are not downstream of another node - final Set rootClassLoaders = new LinkedHashSet<>(allClassLoaders); - rootClassLoaders.removeAll(allDownstreamClassLoaders); - - // Perform a topological sort on the DAG of parent delegation order constraints, starting at the roots - final Set visited = new HashSet<>(); - final Deque topoSortOrder = new ArrayDeque<>(); - for (final ClassLoader rootClassLoader : rootClassLoaders) { - topoSort(rootClassLoader, visited, orderingConstraints, topoSortOrder); - } - - // If ignoring parent classloaders, split resulting list into parents and non-parents - for (final ClassLoader classLoader : topoSortOrder) { - final ClassLoaderHandler classLoaderHandler = classLoaderToClassLoaderHandler.get(classLoader); - final Entry ent = new SimpleEntry<>(classLoader, classLoaderHandler); - if (classLoaderHandler == null) { - // Should not happen - throw new RuntimeException("ClassLoaderHandler not found"); - } - if (scanSpec.ignoreParentClassLoaders && allParentClassLoaders.contains(classLoader)) { - ignoredClassLoaderAndHandlerOrderOut.add(ent); - } else { - classLoaderAndHandlerOrderOut.add(ent); - } - } - } - - // ------------------------------------------------------------------------------------------------------------- - /** * A class to find the unique ordered classpath elements. * @@ -384,60 +162,42 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { } } - // Find all unique parent ClassLoaders, and put all ClassLoaders into a single order, according to the - // delegation order (PARENT_FIRST or PARENT_LAST) - final List> classLoaderAndHandlerOrder = new ArrayList<>(); - final List> ignoredClassLoaderAndHandlerOrder = // - new ArrayList<>(); + // Find all unique classloaders, in delegation order + final ClassLoaderOrder classLoaderOrder = new ClassLoaderOrder(); if (contextClassLoaders != null) { - findClassLoaderHandlerForClassLoaderAndParents(scanSpec, contextClassLoaders, - ClassLoaderHandlerRegistry.CLASS_LOADER_HANDLERS, classLoaderAndHandlerOrder, - ignoredClassLoaderAndHandlerOrder, classpathFinderLog); - } - - // Call each ClassLoaderHandler on its corresponding ClassLoader to get the classpath URLs or paths - final LogNode classLoaderClasspathLoopLog = classpathFinderLog == null ? null - : classpathFinderLog.log("Finding classpath elements in ClassLoaders"); - final LinkedHashSet classLoaderOrderRespectingParentDelegationSet = new LinkedHashSet<>(); - for (final Entry classLoaderAndHandler : classLoaderAndHandlerOrder) { - final ClassLoader classLoader = classLoaderAndHandler.getKey(); - classLoaderOrderRespectingParentDelegationSet.add(classLoader); - final ClassLoaderHandler classLoaderHandler = classLoaderAndHandler.getValue(); - final LogNode classLoaderClasspathLog = classLoaderClasspathLoopLog == null ? null - : classLoaderClasspathLoopLog.log( - "Finding classpath elements in ClassLoader " + classLoader.getClass().getName()); - try { - classLoaderHandler.handle(scanSpec, classLoader, classpathOrder, classLoaderClasspathLog); - } catch (final RuntimeException | LinkageError e) { - if (classLoaderClasspathLog != null) { - classLoaderClasspathLog.log("Exception in ClassLoaderHandler", e); - } + for (final ClassLoader classLoader : contextClassLoaders) { + classLoaderOrder.delegateTo(classLoader, /* isParent = */ false); } } - // Need to record the proper order in which classloaders should be called, in particular to - // respect parent-last delegation order, since this is not the default (issue #267). - classLoaderOrderRespectingParentDelegation = classLoaderOrderRespectingParentDelegationSet - .toArray(new ClassLoader[0]); - // Repeat the process for ignored parent ClassLoaders - for (final Entry classLoaderAndHandler : // - ignoredClassLoaderAndHandlerOrder) { - final ClassLoader classLoader = classLoaderAndHandler.getKey(); - final ClassLoaderHandler classLoaderHandler = classLoaderAndHandler.getValue(); - final LogNode classLoaderClasspathLog = classpathFinderLog == null ? null - : classpathFinderLog - .log("Will not scan the following classpath elements from ignored ClassLoader " - + classLoader.getClass().getName()); - try { - classLoaderHandler.handle(scanSpec, classLoader, ignoredClasspathOrder, - classLoaderClasspathLog); - } catch (final RuntimeException | LinkageError e) { - if (classLoaderClasspathLog != null) { - classLoaderClasspathLog.log("Exception in ClassLoaderHandler", e); - } + // Get all parent classloaders + final Set allParentClassLoaders = classLoaderOrder.getAllParentClassLoaders(); + + // Get the classpath URLs from each ClassLoader + final List finalClassLoaderOrder = new ArrayList<>(); + 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)) { + // If this is a parent and parent classloaders are being ignored, add classpath entries + // to ignoredClasspathOrder + classLoaderHandlerRegistryEntry.findClasspathOrder(classLoader, ignoredClasspathOrder, scanSpec, + classpathFinderLog); + } else { + // Otherwise add classpath entries to classpathOrder, and add the classloader to the + // final classloader ordering + classLoaderHandlerRegistryEntry.findClasspathOrder(classLoader, classpathOrder, scanSpec, + classpathFinderLog); + finalClassLoaderOrder.add(classLoader); } } + // Need to record the classloader delegation order, in particular to respect parent-last delegation + // order, since this is not the default (issue #267). + classLoaderOrderRespectingParentDelegation = finalClassLoaderOrder.toArray(new ClassLoader[0]); + // Get classpath elements from java.class.path, but don't add them if the element is in an ignored // parent classloader and not in a child classloader (and don't use java.class.path at all if // overrideClassLoaders is true or overrideClasspath is set) 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 5832bdf79..11a32c506 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java @@ -131,7 +131,8 @@ boolean addSystemClasspathEntry(final String pathEntry, final ClassLoader classL * the scan spec * @return true, if added and unique */ - private boolean addClasspathEntry(final String pathEntry, final ClassLoader classLoader, ScanSpec scanSpec) { + private boolean addClasspathEntry(final String pathEntry, final ClassLoader classLoader, + final ScanSpec scanSpec) { if (scanSpec.overrideClasspath != null // && (SystemJarFinder.getJreLibOrExtJars().contains(pathEntry) || pathEntry.equals(SystemJarFinder.getJreRtJarPath()))) { @@ -161,8 +162,8 @@ private boolean addClasspathEntry(final String pathEntry, final ClassLoader clas * @return true (and add the classpath element) if pathElement is not null, empty, nonexistent, or filtered out * by user-specified criteria, otherwise return false. */ - public boolean addClasspathEntry(final String pathElement, final ClassLoader classLoader, ScanSpec scanSpec, - final LogNode log) { + public boolean addClasspathEntry(final String pathElement, final ClassLoader classLoader, + final ScanSpec scanSpec, final LogNode log) { if (pathElement == null || pathElement.isEmpty()) { return false; } @@ -284,7 +285,7 @@ public boolean addClasspathEntry(final String pathElement, final ClassLoader cla * the LogNode instance to use if logging in verbose mode. * @return true (and add the classpath element) if pathElement is not null or empty, otherwise return false. */ - public boolean addClasspathEntries(final String pathStr, final ClassLoader classLoader, ScanSpec scanSpec, + public boolean addClasspathEntries(final String pathStr, final ClassLoader classLoader, final ScanSpec scanSpec, final LogNode log) { if (pathStr == null || pathStr.isEmpty()) { return false; @@ -318,7 +319,7 @@ public boolean addClasspathEntries(final String pathStr, final ClassLoader class * @return true (and add the classpath element) if pathEl)ement is not null or empty, otherwise return false. */ public boolean addClasspathEntryObject(final Object pathObject, final ClassLoader classLoader, - ScanSpec scanSpec, final LogNode log) { + final ScanSpec scanSpec, final LogNode log) { boolean valid = false; if (pathObject != null) { if (pathObject instanceof String) { From 8292ea18c651562c1f1ca2c35dbeb344912b8987 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 22 Mar 2019 09:33:59 -0600 Subject: [PATCH 0056/1778] Add support for Plexus ClassWorlds ClassRealm ClassLoader --- ...assWorldsClassRealmClassLoaderHandler.java | 163 +++++++++--------- 1 file changed, 82 insertions(+), 81 deletions(-) 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 1fe82ca6e..198a28237 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/PlexusClassWorldsClassRealmClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/PlexusClassWorldsClassRealmClassLoaderHandler.java @@ -28,13 +28,10 @@ */ package nonapi.io.github.classgraph.classloaderhandler; -import java.io.File; -import java.util.LinkedHashSet; -import java.util.Map.Entry; -import java.util.Set; import java.util.SortedSet; import nonapi.io.github.classgraph.ScanSpec; +import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; import nonapi.io.github.classgraph.utils.LogNode; import nonapi.io.github.classgraph.utils.ReflectionUtils; @@ -45,95 +42,99 @@ * @author lukehutch */ class PlexusClassWorldsClassRealmClassLoaderHandler implements ClassLoaderHandler { - - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handledClassLoaders() - */ - @Override - public String[] handledClassLoaders() { - return new String[] { "org.codehaus.plexus.classworlds.realm.ClassRealm" }; - } - - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#getEmbeddedClassLoader(java.lang.ClassLoader) + /** + * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. + * + * @param classLoader + * the {@link ClassLoader}. + * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ - @Override - public ClassLoader getEmbeddedClassLoader(final ClassLoader classRealmInstance) { - // Set classLoaderOrder = new LinkedHashSet<>(); - // - // // From ClassRealm#loadClassFromImport(String) -> getImportClassLoader(String) - // final Object foreignImports = ReflectionUtils.getFieldVal(classRealmInstance, "foreignImports", false); - // if (foreignImports != null) { - // @SuppressWarnings("unchecked") - // SortedSet foreignImportEntries = (SortedSet) foreignImports; - // for (Object entry : foreignImportEntries) { - // final Object classLoader = ReflectionUtils.invokeMethod(entry, "getClassLoader", false); - // if (classLoader instanceof ClassLoader) { - // classLoaderOrder.add((ClassLoader) classLoader); - // } - // } - // } - // - // // Get delegation order -- different strategies have different delegation orders - // DelegationOrder delegationOrder = getDelegationOrder(classRealmInstance); - // - // // From ClassRealm#loadClassFromSelf(String) -> findLoadedClass(String) for self-first strategy - // if (delegationOrder == DelegationOrder.PARENT_LAST) { - // classLoaderOrder.add(classRealmInstance); - // } - // - // // 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 Object parentClassLoader = ReflectionUtils.invokeMethod(classRealmInstance, "getParentClassLoader", - // false); - // if (parentClassLoader instanceof ClassLoader) { - // classLoaderOrder.add((ClassLoader) parentClassLoader); - // } - // - // // From ClassRealm#loadClassFromSelf(String) -> findLoadedClass(String) for parent-first strategy - // if (delegationOrder == DelegationOrder.PARENT_FIRST) { - // classLoaderOrder.add(classRealmInstance); - // } - // - // return classLoaderOrder; - return null; + public static boolean canHandle(final ClassLoader classLoader) { + return "org.codehaus.plexus.classworlds.realm.ClassRealm".equals(classLoader.getClass().getName()); } - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#getDelegationOrder(java.lang.ClassLoader) + /** + * Checks if is this classloader uses a parent-first strategy. + * + * @param classRealmInstance + * the ClassRealm instance + * @return true if classloader uses a parent-first strategy */ - @Override - public DelegationOrder getDelegationOrder(final ClassLoader classRealmInstance) { + private static boolean isParentFirstStrategy(final ClassLoader classRealmInstance) { final Object strategy = ReflectionUtils.getFieldVal(classRealmInstance, "strategy", false); if (strategy != null) { - String strategyClassName = strategy.getClass().getName(); + final String strategyClassName = strategy.getClass().getName(); if (strategyClassName.equals("org.codehaus.plexus.classworlds.strategy.SelfFirstStrategy") || strategyClassName.equals("org.codehaus.plexus.classworlds.strategy.OsgiBundleStrategy")) { - return DelegationOrder.PARENT_LAST; + // Strategy is self-first + return false; } } - // org.codehaus.plexus.classworlds.strategy.ParentFirstStrategy (or failed to find strategy) - return DelegationOrder.PARENT_FIRST; + // Strategy is org.codehaus.plexus.classworlds.strategy.ParentFirstStrategy (or failed to find strategy) + return true; + } + + /** + * Find the {@link ClassLoader} delegation order for a {@link ClassLoader}. + * + * @param classRealm + * the {@link ClassLoader} to find the order for. + * @param classLoaderOrder + * a {@link ClassLoaderOrder} object to update. + */ + public static void findClassLoaderOrder(final ClassLoader classRealm, final ClassLoaderOrder classLoaderOrder) { + // From ClassRealm#loadClassFromImport(String) -> getImportClassLoader(String) + final Object foreignImports = ReflectionUtils.getFieldVal(classRealm, "foreignImports", false); + if (foreignImports != null) { + @SuppressWarnings("unchecked") + final SortedSet foreignImportEntries = (SortedSet) foreignImports; + for (final Object entry : foreignImportEntries) { + final ClassLoader foreignImportClassLoader = (ClassLoader) ReflectionUtils.invokeMethod(entry, + "getClassLoader", false); + // Treat foreign import classloader as if it is a parent classloader + classLoaderOrder.delegateTo(foreignImportClassLoader, /* isParent = */ true); + } + } + + // Get delegation order -- different strategies have different delegation orders + final boolean isParentFirst = isParentFirstStrategy(classRealm); + + // From ClassRealm#loadClassFromSelf(String) -> findLoadedClass(String) for self-first strategy + if (!isParentFirst) { + // Add self before parent + classLoaderOrder.add(classRealm); + } + + // 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); + classLoaderOrder.delegateTo(parentClassLoader, /* isParent = */ true); + classLoaderOrder.delegateTo(classRealm.getParent(), /* isParent = */ true); + + // From ClassRealm#loadClassFromSelf(String) -> findLoadedClass(String) for parent-first strategy + if (isParentFirst) { + // Add self after parent + classLoaderOrder.add(classRealm); + } } - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler#handle( - * nonapi.io.github.classgraph.ScanSpec, java.lang.ClassLoader, - * nonapi.io.github.classgraph.classpath.ClasspathOrder, nonapi.io.github.classgraph.utils.LogNode) + /** + * 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. */ - @Override - public void handle(final ScanSpec scanSpec, final ClassLoader classloader, - final ClasspathOrder classpathOrderOut, final LogNode log) { - // final Object[] entries = (Object[]) ReflectionUtils.getFieldVal(classpathManager, "entries", false); - // 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); - // if (baseFile != null) { - // classpathOrderOut.addClasspathEntry(baseFile.getPath(), classloader, log); - // } - // } - // } + public static void findClasspathOrder(final ClassLoader classLoader, final ClasspathOrder classpathOrder, + final ScanSpec scanSpec, final LogNode log) { + // ClassRealm extends URLClassLoader + URLClassLoaderHandler.findClasspathOrder(classLoader, classpathOrder, scanSpec, log); } } From 4ba4503902408a2055c31cf256f6dc88f8e537c6 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 22 Mar 2019 09:38:28 -0600 Subject: [PATCH 0057/1778] JDK 7 compat fixes --- .../ClassLoaderHandlerRegistry.java | 35 ++++++++----------- 1 file changed, 14 insertions(+), 21 deletions(-) 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 fcc4d50aa..03099e3f4 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java @@ -28,8 +28,6 @@ */ package nonapi.io.github.classgraph.classloaderhandler; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; import java.lang.reflect.Method; import java.util.Arrays; import java.util.Collections; @@ -120,14 +118,14 @@ private ClassLoaderHandlerRegistry() { * A list of fully-qualified ClassLoader class names paired with the ClassLoaderHandler that can handle them. */ public static class ClassLoaderHandlerRegistryEntry { - /** canHandle method handle. */ - private final MethodHandle canHandle; + /** canHandle method. */ + private final Method canHandleMethod; - /** findClassLoaderOrder method handle. */ - private final MethodHandle findClassLoaderOrder; + /** findClassLoaderOrder method. */ + private final Method findClassLoaderOrderMethod; - /** findClasspathOrder method handle. */ - private final MethodHandle findClasspathOrder; + /** findClasspathOrder method. */ + private final Method findClasspathOrderMethod; /** The ClassLoaderHandler class. */ public final Class classLoaderHandlerClass; @@ -141,26 +139,21 @@ public static class ClassLoaderHandlerRegistryEntry { private ClassLoaderHandlerRegistryEntry(final Class classLoaderHandlerClass) { this.classLoaderHandlerClass = classLoaderHandlerClass; try { - final Method canHandleMethod = classLoaderHandlerClass.getDeclaredMethod("canHandle", - ClassLoader.class); - canHandle = MethodHandles.lookup().unreflect(canHandleMethod); + canHandleMethod = classLoaderHandlerClass.getDeclaredMethod("canHandle", ClassLoader.class); } catch (final Exception e) { throw new RuntimeException( "Could not find canHandle method for " + classLoaderHandlerClass.getName(), e); } try { - final Method findClassLoaderOrderMethod = classLoaderHandlerClass - .getDeclaredMethod("findClassLoaderOrder", ClassLoader.class, ClassLoaderOrder.class); - findClassLoaderOrder = MethodHandles.lookup().unreflect(findClassLoaderOrderMethod); + findClassLoaderOrderMethod = classLoaderHandlerClass.getDeclaredMethod("findClassLoaderOrder", + ClassLoader.class, ClassLoaderOrder.class); } catch (final Exception e) { throw new RuntimeException( "Could not find findClassLoaderOrder method for " + classLoaderHandlerClass.getName(), e); } try { - final Method findClasspathOrderMethod = classLoaderHandlerClass.getDeclaredMethod( - "findClasspathOrder", ClassLoader.class, ClasspathOrder.class, ScanSpec.class, - LogNode.class); - findClasspathOrder = MethodHandles.lookup().unreflect(findClasspathOrderMethod); + findClasspathOrderMethod = classLoaderHandlerClass.getDeclaredMethod("findClasspathOrder", + ClassLoader.class, ClasspathOrder.class, ScanSpec.class, LogNode.class); } catch (final Exception e) { throw new RuntimeException( "Could not find findClasspathOrder method for " + classLoaderHandlerClass.getName(), e); @@ -176,7 +169,7 @@ private ClassLoaderHandlerRegistryEntry(final Class Date: Fri, 22 Mar 2019 09:38:58 -0600 Subject: [PATCH 0058/1778] [maven-release-plugin] prepare release classgraph-4.8.19 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e8dab5d71..f4bd96c99 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.19-SNAPSHOT + 4.8.19 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.19 From c45aee8036106f1de01f9a66cb0dc6645f44b0cc Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 22 Mar 2019 09:39:07 -0600 Subject: [PATCH 0059/1778] [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 f4bd96c99..03eb940a1 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.19 + 4.8.20-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.19 + HEAD From 64c76b2dc5592ce03c8c9b6765ae7e314e609b72 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 22 Mar 2019 10:13:47 -0600 Subject: [PATCH 0060/1778] Add private constructor --- .../classgraph/classloaderhandler/AntClassLoaderHandler.java | 4 ++++ .../classloaderhandler/EquinoxClassLoaderHandler.java | 4 ++++ .../EquinoxContextFinderClassLoaderHandler.java | 4 ++++ .../classloaderhandler/FallbackClassLoaderHandler.java | 4 ++++ .../classloaderhandler/FelixClassLoaderHandler.java | 4 ++++ .../classloaderhandler/JBossClassLoaderHandler.java | 4 ++++ .../classgraph/classloaderhandler/JPMSClassLoaderHandler.java | 4 ++++ .../classloaderhandler/OSGiDefaultClassLoaderHandler.java | 4 ++++ .../ParentLastDelegationOrderTestClassLoaderHandler.java | 4 ++++ .../PlexusClassWorldsClassRealmClassLoaderHandler.java | 4 ++++ .../SpringBootRestartClassLoaderHandler.java | 4 ++++ .../TomcatWebappClassLoaderBaseHandler.java | 4 ++++ .../classgraph/classloaderhandler/URLClassLoaderHandler.java | 4 ++++ .../classloaderhandler/WeblogicClassLoaderHandler.java | 4 ++++ .../WebsphereLibertyClassLoaderHandler.java | 4 ++++ .../WebsphereTraditionalClassLoaderHandler.java | 4 ++++ 16 files changed, 64 insertions(+) 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 5c6905607..6d9b78494 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/AntClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/AntClassLoaderHandler.java @@ -36,6 +36,10 @@ /** Extract classpath entries from the Ant ClassLoader. */ class AntClassLoaderHandler implements ClassLoaderHandler { + /** Class cannot be constructed. */ + private AntClassLoaderHandler() { + } + /** * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. * 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 5ca1e114c..66cdc5000 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxClassLoaderHandler.java @@ -49,6 +49,10 @@ class EquinoxClassLoaderHandler implements ClassLoaderHandler { private static final List FIELD_NAMES = Collections .unmodifiableList(Arrays.asList("cp", "nestedDirName")); + /** Class cannot be constructed. */ + private EquinoxClassLoaderHandler() { + } + /** * True if system bundles have been read. We assume there is only one system bundle on the classpath, so this is * static. 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 3b4685a59..ea0aad6c5 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxContextFinderClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxContextFinderClassLoaderHandler.java @@ -36,6 +36,10 @@ /** Extract classpath entries from the Eclipse Equinox ContextFinder ClassLoader. */ class EquinoxContextFinderClassLoaderHandler implements ClassLoaderHandler { + /** Class cannot be constructed. */ + private EquinoxContextFinderClassLoaderHandler() { + } + /** * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. * 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 1a8e615b1..cd45a75b6 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FallbackClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FallbackClassLoaderHandler.java @@ -38,6 +38,10 @@ * Fallback ClassLoaderHandler. Tries to get classpath from a range of possible method and field names. */ class FallbackClassLoaderHandler implements ClassLoaderHandler { + /** Class cannot be constructed. */ + private FallbackClassLoaderHandler() { + } + /** * Check whether this {@link ClassLoaderHandler} can handle a given {@link 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 49528a4b5..6cd77aeb1 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FelixClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FelixClassLoaderHandler.java @@ -48,6 +48,10 @@ * @author elrufaie */ class FelixClassLoaderHandler implements ClassLoaderHandler { + /** Class cannot be constructed. */ + private FelixClassLoaderHandler() { + } + /** * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. * 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 c50657a9e..3be3e27e7 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java @@ -51,6 +51,10 @@ * https://github.com/jboss-modules/jboss-modules/blob/master/src/main/java/org/jboss/modules/ModuleClassLoader.java */ class JBossClassLoaderHandler implements ClassLoaderHandler { + /** Class cannot be constructed. */ + private JBossClassLoaderHandler() { + } + /** * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. * 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 b6a06e33c..edf0b26bb 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JPMSClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JPMSClassLoaderHandler.java @@ -38,6 +38,10 @@ * them (module scanning uses a different mechanism from classpath scanning). */ class JPMSClassLoaderHandler implements ClassLoaderHandler { + /** Class cannot be constructed. */ + private JPMSClassLoaderHandler() { + } + /** * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. * 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 19ead93a3..2ab2f0406 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/OSGiDefaultClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/OSGiDefaultClassLoaderHandler.java @@ -42,6 +42,10 @@ * @author lukehutch */ class OSGiDefaultClassLoaderHandler implements ClassLoaderHandler { + /** Class cannot be constructed. */ + private OSGiDefaultClassLoaderHandler() { + } + /** * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. * 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 50a84a213..f9db69e13 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ParentLastDelegationOrderTestClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ParentLastDelegationOrderTestClassLoaderHandler.java @@ -36,6 +36,10 @@ /** ClassLoaderHandler that is used to test PARENT_LAST delegation order. */ class ParentLastDelegationOrderTestClassLoaderHandler implements ClassLoaderHandler { + /** Class cannot be constructed. */ + private ParentLastDelegationOrderTestClassLoaderHandler() { + } + /** * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. * 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 198a28237..2dd865e67 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/PlexusClassWorldsClassRealmClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/PlexusClassWorldsClassRealmClassLoaderHandler.java @@ -42,6 +42,10 @@ * @author lukehutch */ class PlexusClassWorldsClassRealmClassLoaderHandler implements ClassLoaderHandler { + /** Class cannot be constructed. */ + private PlexusClassWorldsClassRealmClassLoaderHandler() { + } + /** * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. * 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 86303a452..2489c519b 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/SpringBootRestartClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/SpringBootRestartClassLoaderHandler.java @@ -42,6 +42,10 @@ * handler for that class loader also has to delegate in PARENT_LAST order. */ class SpringBootRestartClassLoaderHandler implements ClassLoaderHandler { + /** Class cannot be constructed. */ + private SpringBootRestartClassLoaderHandler() { + } + /** * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. * 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 36dd357f8..657dfdbc4 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java @@ -39,6 +39,10 @@ /** Extract classpath entries from the Tomcat/Catalina WebappClassLoaderBase. */ class TomcatWebappClassLoaderBaseHandler implements ClassLoaderHandler { + /** Class cannot be constructed. */ + private TomcatWebappClassLoaderBaseHandler() { + } + /** * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. * 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 3b863d972..e604a6c4d 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/URLClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/URLClassLoaderHandler.java @@ -38,6 +38,10 @@ /** ClassLoaderHandler that is able to extract the URLs from a URLClassLoader. */ class URLClassLoaderHandler implements ClassLoaderHandler { + /** Class cannot be constructed. */ + private URLClassLoaderHandler() { + } + /** * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. * 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 eb03e69af..d69097b0e 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WeblogicClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WeblogicClassLoaderHandler.java @@ -36,6 +36,10 @@ /** Extract classpath entries from the Weblogic ClassLoaders. */ class WeblogicClassLoaderHandler implements ClassLoaderHandler { + /** Class cannot be constructed. */ + private WeblogicClassLoaderHandler() { + } + /** * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. * 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 a25350c49..d1edc5aa9 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java @@ -55,6 +55,10 @@ class WebsphereLibertyClassLoaderHandler implements ClassLoaderHandler { /** {@code "com.ibm.ws.classloading.internal.ThreadContextClassLoader"} */ private static final String IBM_THREAD_CONTEXT_CLASS_LOADER = PKG_PREFIX + "ThreadContextClassLoader"; + /** Class cannot be constructed. */ + private WebsphereLibertyClassLoaderHandler() { + } + /** * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. * 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 e858f0cf1..959f1d90f 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereTraditionalClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereTraditionalClassLoaderHandler.java @@ -40,6 +40,10 @@ * @author lukehutch */ class WebsphereTraditionalClassLoaderHandler implements ClassLoaderHandler { + /** Class cannot be constructed. */ + private WebsphereTraditionalClassLoaderHandler() { + } + /** * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. * From 4f266b2ba24b217966cb1f6d4ee16fc84827bab5 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 22 Mar 2019 10:17:46 -0600 Subject: [PATCH 0061/1778] Fix servlet class names --- .../classloaderhandler/WeblogicClassLoaderHandler.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 d69097b0e..a9c773c54 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WeblogicClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WeblogicClassLoaderHandler.java @@ -53,8 +53,8 @@ public static boolean canHandle(final ClassLoader classLoader) { || "weblogic.utils.classloaders.FilteringClassLoader".equals(classLoader.getClass().getName()) // TODO: The following two known classloader names have not been tested, and the fields/methods // may not match those of the above classloaders. - || "weblogic.utils.classloaders.GenericClassLoader".equals(classLoader.getClass().getName()) - || "weblogic.utils.classloaders.FilteringClassLoader".equals(classLoader.getClass().getName()); + || "weblogic.servlet.jsp.JspClassLoader".equals(classLoader.getClass().getName()) + || "weblogic.servlet.jsp.TagFileClassLoader".equals(classLoader.getClass().getName()); } /** From 2dfaeae98a3ac28b3785fbc65111c487f673ea87 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 22 Mar 2019 11:14:12 -0600 Subject: [PATCH 0062/1778] Tweaks to parent classloader delegation --- .../SpringBootRestartClassLoaderHandler.java | 4 ++++ 1 file changed, 4 insertions(+) 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 2489c519b..13fb66319 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/SpringBootRestartClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/SpringBootRestartClassLoaderHandler.java @@ -72,6 +72,10 @@ public static void findClassLoaderOrder(final ClassLoader classLoader, // classloader(s) before the parent. classLoaderOrder.delegateTo(Thread.currentThread().getContextClassLoader(), /* isParent = */ false); classLoaderOrder.delegateTo(ClassGraph.class.getClassLoader(), /* isParent = */ false); + // Also delegate to system classloader, in case the above two are actually the same as `classLoader` + classLoaderOrder.delegateTo(ClassLoader.getSystemClassLoader(), /* isParent = */ true); + // Finally delegate to the parent of the RestartClassLoader, in case that is different from + // SystemClassLoader classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true); } From 6d681217e849f70d2a9614cc6b9350990a34bc6b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 22 Mar 2019 12:38:13 -0600 Subject: [PATCH 0063/1778] Fix for manually scanning rt.jar (@jechlin) --- .../nonapi/io/github/classgraph/classpath/ClasspathOrder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 11a32c506..51ac79e38 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java @@ -133,7 +133,7 @@ boolean addSystemClasspathEntry(final String pathEntry, final ClassLoader classL */ private boolean addClasspathEntry(final String pathEntry, final ClassLoader classLoader, final ScanSpec scanSpec) { - if (scanSpec.overrideClasspath != null // + if (scanSpec.overrideClasspath == null // && (SystemJarFinder.getJreLibOrExtJars().contains(pathEntry) || pathEntry.equals(SystemJarFinder.getJreRtJarPath()))) { // JRE lib and ext jars are handled separately, so reject them as duplicates if they are From 358582241a9e6e9e4a7f27c97a1a3e08d8c9307d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 22 Mar 2019 12:38:52 -0600 Subject: [PATCH 0064/1778] [maven-release-plugin] prepare release classgraph-4.8.20 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 03eb940a1..b4465056f 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.20-SNAPSHOT + 4.8.20 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.20 From eb07b0044d294cda242dcca2f521ef97f130fc53 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 22 Mar 2019 12:39:00 -0600 Subject: [PATCH 0065/1778] [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 b4465056f..5ef452d0e 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.20 + 4.8.21-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.20 + HEAD From df9c2c7a2853a4a93c2adbaeaf09044322a23f74 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 22 Mar 2019 13:06:06 -0600 Subject: [PATCH 0066/1778] Remove donate link --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index ecf72415f..6be4a2a4f 100644 --- a/README.md +++ b/README.md @@ -103,10 +103,6 @@ In particular, the Maven group id has changed from `io.github.lukehutch.fast-cla ClassGraph was written by Luke Hutchison ([@LH](http://twitter.com/LH) on Twitter). -Please donate if this library makes your life easier: - -[![](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=luke.hutch@gmail.com&lc=US&item_name=Luke%20Hutchison&item_number=ClassGraph&no_note=0¤cy_code=USD&bn=PP-DonationsBF:btn_donateCC_LG.gif:NonHostedGuest) - ### Acknowledgments ClassGraph would not be possible without contributions from numerous users, including in the form of bug reports, feature requests, code contributions, and assistance with testing. From a5d6db58ead400b27b94961e83cdd99890423728 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 23 Mar 2019 21:06:03 -0600 Subject: [PATCH 0067/1778] Don't sort empty lists to avoid ConcurrentModificationException (#334) --- .../github/classgraph/AnnotationInfoList.java | 6 +- .../io/github/classgraph/ClassInfoList.java | 5 +- .../java/io/github/classgraph/Classfile.java | 4 +- .../classgraph/ClasspathElementModule.java | 4 +- .../classgraph/GraphvizDotfileGenerator.java | 8 +-- .../java/io/github/classgraph/ModuleInfo.java | 5 +- .../java/io/github/classgraph/ModuleRef.java | 4 +- .../io/github/classgraph/PackageInfo.java | 5 +- .../io/github/classgraph/ResourceList.java | 5 +- .../java/io/github/classgraph/ScanResult.java | 7 +- .../java/io/github/classgraph/Scanner.java | 5 +- .../io/github/classgraph/WhiteBlackList.java | 6 +- .../AntClassLoaderHandler.java | 2 +- .../WebsphereLibertyClassLoaderHandler.java | 2 +- .../classpath/ClassLoaderAndModuleFinder.java | 3 +- .../fastzipfilereader/LogicalZipFile.java | 5 +- .../classgraph/json/JSONSerializer.java | 6 +- .../classgraph/utils/CollectionUtils.java | 71 +++++++++++++++++++ .../io/github/classgraph/utils/JarUtils.java | 3 +- 19 files changed, 117 insertions(+), 39 deletions(-) create mode 100644 src/main/java/nonapi/io/github/classgraph/utils/CollectionUtils.java diff --git a/src/main/java/io/github/classgraph/AnnotationInfoList.java b/src/main/java/io/github/classgraph/AnnotationInfoList.java index eeb19e1d6..366b95e4c 100644 --- a/src/main/java/io/github/classgraph/AnnotationInfoList.java +++ b/src/main/java/io/github/classgraph/AnnotationInfoList.java @@ -31,12 +31,12 @@ import java.lang.annotation.Repeatable; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import io.github.classgraph.ClassInfo.RelType; +import nonapi.io.github.classgraph.utils.CollectionUtils; /** A list of {@link AnnotationInfo} objects. */ public class AnnotationInfoList extends MappableInfoList { @@ -333,10 +333,10 @@ static AnnotationInfoList getIndirectAnnotations(final AnnotationInfoList direct final AnnotationInfoList directAnnotationInfoSorted = directAnnotationInfo == null ? AnnotationInfoList.EMPTY_LIST : new AnnotationInfoList(directAnnotationInfo); - Collections.sort(directAnnotationInfoSorted); + CollectionUtils.sortIfNotEmpty(directAnnotationInfoSorted); final AnnotationInfoList annotationInfoList = new AnnotationInfoList(reachableAnnotationInfo, directAnnotationInfoSorted); - Collections.sort(annotationInfoList); + CollectionUtils.sortIfNotEmpty(annotationInfoList); return annotationInfoList; } diff --git a/src/main/java/io/github/classgraph/ClassInfoList.java b/src/main/java/io/github/classgraph/ClassInfoList.java index 4c7c9eb62..955b35760 100644 --- a/src/main/java/io/github/classgraph/ClassInfoList.java +++ b/src/main/java/io/github/classgraph/ClassInfoList.java @@ -42,6 +42,7 @@ import io.github.classgraph.ClassInfo.ReachableAndDirectlyRelatedClasses; import nonapi.io.github.classgraph.ScanSpec; +import nonapi.io.github.classgraph.utils.CollectionUtils; /** * A list of {@link ClassInfo} objects, which stores both reachable classes (obtained through a given class @@ -79,9 +80,9 @@ public class ClassInfoList extends MappableInfoList { super(reachableClasses); this.sortByName = sortByName; if (sortByName) { - // It's a bit dicey calling Collections.sort(this) from within a constructor, but the super-constructor + // It's a bit dicey calling CollectionUtils.sortIfNotEmpty(this) from within a constructor, but the super-constructor // has been called, so it should be fine :-) - Collections.sort(this); + CollectionUtils.sortIfNotEmpty(this); } // If directlyRelatedClasses was not provided, then assume all reachable classes were directly related this.directlyRelatedClasses = directlyRelatedClasses == null ? reachableClasses : directlyRelatedClasses; diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index 7290b5ad5..16b36dfc0 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -33,7 +33,6 @@ import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -43,6 +42,7 @@ import nonapi.io.github.classgraph.ScanSpec; import nonapi.io.github.classgraph.concurrency.WorkQueue; import nonapi.io.github.classgraph.types.ParseException; +import nonapi.io.github.classgraph.utils.CollectionUtils; import nonapi.io.github.classgraph.utils.InputStreamOrByteBufferAdapter; import nonapi.io.github.classgraph.utils.JarUtils; import nonapi.io.github.classgraph.utils.Join; @@ -1531,7 +1531,7 @@ private void readClassAttributes() throws IOException, ClassfileFormatException } if (refdClassNames != null) { final List refdClassNamesSorted = new ArrayList<>(refdClassNames); - Collections.sort(refdClassNamesSorted); + CollectionUtils.sortIfNotEmpty(refdClassNamesSorted); subLog.log("Referenced class names: " + Join.join(", ", refdClassNamesSorted)); } } diff --git a/src/main/java/io/github/classgraph/ClasspathElementModule.java b/src/main/java/io/github/classgraph/ClasspathElementModule.java index ae5adf781..d65ae8a7b 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementModule.java +++ b/src/main/java/io/github/classgraph/ClasspathElementModule.java @@ -33,7 +33,6 @@ import java.io.InputStream; import java.net.URI; import java.nio.ByteBuffer; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -46,6 +45,7 @@ import nonapi.io.github.classgraph.fastzipfilereader.NestedJarHandler; import nonapi.io.github.classgraph.recycler.RecycleOnClose; import nonapi.io.github.classgraph.recycler.Recycler; +import nonapi.io.github.classgraph.utils.CollectionUtils; import nonapi.io.github.classgraph.utils.InputStreamOrByteBufferAdapter; import nonapi.io.github.classgraph.utils.LogNode; @@ -257,7 +257,7 @@ void scanPaths(final LogNode log) { } return; } - Collections.sort(resourceRelativePaths); + CollectionUtils.sortIfNotEmpty(resourceRelativePaths); String prevParentRelativePath = null; ScanSpecPathMatch prevParentMatchStatus = null; diff --git a/src/main/java/io/github/classgraph/GraphvizDotfileGenerator.java b/src/main/java/io/github/classgraph/GraphvizDotfileGenerator.java index c582a5b64..3bc42b215 100644 --- a/src/main/java/io/github/classgraph/GraphvizDotfileGenerator.java +++ b/src/main/java/io/github/classgraph/GraphvizDotfileGenerator.java @@ -29,11 +29,11 @@ package io.github.classgraph; import java.util.BitSet; -import java.util.Collections; import java.util.HashSet; import java.util.Set; import nonapi.io.github.classgraph.ScanSpec; +import nonapi.io.github.classgraph.utils.CollectionUtils; /** Builds a class graph visualization in Graphviz .dot file format. */ final class GraphvizDotfileGenerator { @@ -269,7 +269,7 @@ private static void labelClassNodeHTML(final ClassInfo ci, final String shape, f buf.append("ANNOTATIONS"); final AnnotationInfoList annotationInfoSorted = new AnnotationInfoList(annotationInfo); - Collections.sort(annotationInfoSorted); + CollectionUtils.sortIfNotEmpty(annotationInfoSorted); for (final AnnotationInfo ai : annotationInfoSorted) { final String annotationName = ai.getName(); if (!annotationName.startsWith("java.lang.annotation.")) { @@ -285,7 +285,7 @@ private static void labelClassNodeHTML(final ClassInfo ci, final String shape, f final FieldInfoList fieldInfo = ci.fieldInfo; if (showFields && fieldInfo != null && !fieldInfo.isEmpty()) { final FieldInfoList fieldInfoSorted = new FieldInfoList(fieldInfo); - Collections.sort(fieldInfoSorted); + CollectionUtils.sortIfNotEmpty(fieldInfoSorted); for (int i = fieldInfoSorted.size() - 1; i >= 0; --i) { // Remove serialVersionUID field if (fieldInfoSorted.get(i).getName().equals("serialVersionUID")) { @@ -345,7 +345,7 @@ private static void labelClassNodeHTML(final ClassInfo ci, final String shape, f final MethodInfoList methodInfo = ci.methodInfo; if (showMethods && methodInfo != null) { final MethodInfoList methodInfoSorted = new MethodInfoList(methodInfo); - Collections.sort(methodInfoSorted); + CollectionUtils.sortIfNotEmpty(methodInfoSorted); for (int i = methodInfoSorted.size() - 1; i >= 0; --i) { // Don't list static initializer blocks or methods of Object final MethodInfo mi = methodInfoSorted.get(i); diff --git a/src/main/java/io/github/classgraph/ModuleInfo.java b/src/main/java/io/github/classgraph/ModuleInfo.java index 1017eb97c..203268e5e 100644 --- a/src/main/java/io/github/classgraph/ModuleInfo.java +++ b/src/main/java/io/github/classgraph/ModuleInfo.java @@ -29,10 +29,11 @@ package io.github.classgraph; import java.net.URI; -import java.util.Collections; import java.util.HashSet; import java.util.Set; +import nonapi.io.github.classgraph.utils.CollectionUtils; + /** Holds metadata about a package encountered during a scan. */ public class ModuleInfo implements Comparable, HasName { /** The name of the module. */ @@ -201,7 +202,7 @@ public PackageInfoList getPackageInfo() { return new PackageInfoList(1); } final PackageInfoList packageInfoList = new PackageInfoList(packageInfoSet); - Collections.sort(packageInfoList); + CollectionUtils.sortIfNotEmpty(packageInfoList); return packageInfoList; } diff --git a/src/main/java/io/github/classgraph/ModuleRef.java b/src/main/java/io/github/classgraph/ModuleRef.java index 11e04e4b4..7edee00eb 100644 --- a/src/main/java/io/github/classgraph/ModuleRef.java +++ b/src/main/java/io/github/classgraph/ModuleRef.java @@ -32,10 +32,10 @@ import java.io.IOException; import java.net.URI; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Set; +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. */ @@ -104,7 +104,7 @@ public ModuleRef(final Object moduleReference, final Object moduleLayer) { throw new IllegalArgumentException("moduleReference.descriptor().packages() should not return null"); } this.packages = new ArrayList<>(modulePackages); - Collections.sort(this.packages); + CollectionUtils.sortIfNotEmpty(this.packages); final Object optionalRawVersion = ReflectionUtils.invokeMethod(this.descriptor, "rawVersion", /* throwException = */ true); if (optionalRawVersion != null) { diff --git a/src/main/java/io/github/classgraph/PackageInfo.java b/src/main/java/io/github/classgraph/PackageInfo.java index 5824035cf..7c422f1e7 100644 --- a/src/main/java/io/github/classgraph/PackageInfo.java +++ b/src/main/java/io/github/classgraph/PackageInfo.java @@ -28,13 +28,14 @@ */ package io.github.classgraph; -import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; +import nonapi.io.github.classgraph.utils.CollectionUtils; + /** Holds metadata about a package encountered during a scan. */ public class PackageInfo implements Comparable, HasName { /** Name of the package. */ @@ -168,7 +169,7 @@ public PackageInfoList getChildren() { } final PackageInfoList childrenSorted = new PackageInfoList(children); // Ensure children are sorted - Collections.sort(childrenSorted, new Comparator() { + CollectionUtils.sortIfNotEmpty(childrenSorted, new Comparator() { @Override public int compare(final PackageInfo o1, final PackageInfo o2) { return o1.name.compareTo(o2.name); diff --git a/src/main/java/io/github/classgraph/ResourceList.java b/src/main/java/io/github/classgraph/ResourceList.java index 6063c6de1..b21f00a20 100644 --- a/src/main/java/io/github/classgraph/ResourceList.java +++ b/src/main/java/io/github/classgraph/ResourceList.java @@ -35,13 +35,14 @@ import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import nonapi.io.github.classgraph.utils.CollectionUtils; + /** An AutoCloseable list of AutoCloseable {@link Resource} objects. */ public class ResourceList extends ArrayList implements AutoCloseable { /** serialVersionUID. */ @@ -249,7 +250,7 @@ public List> findDuplicatePaths() { duplicatePaths.add(new SimpleEntry<>(pathAndResourceList.getKey(), pathAndResourceList.getValue())); } } - Collections.sort(duplicatePaths, new Comparator>() { + CollectionUtils.sortIfNotEmpty(duplicatePaths, new Comparator>() { @Override public int compare(final Entry o1, final Entry o2) { // Sort in lexicographic order of path diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index 827098388..b5ed323b8 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -59,6 +59,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.utils.CollectionUtils; import nonapi.io.github.classgraph.utils.FileUtils; import nonapi.io.github.classgraph.utils.JarUtils; import nonapi.io.github.classgraph.utils.LogNode; @@ -1381,11 +1382,11 @@ public String toJSON(final int indentWidth) { throw new IllegalArgumentException("Please call ClassGraph#enableClassInfo() before #scan()"); } final List allClassInfo = new ArrayList<>(classNameToClassInfo.values()); - Collections.sort(allClassInfo); + CollectionUtils.sortIfNotEmpty(allClassInfo); final List allPackageInfo = new ArrayList<>(packageNameToPackageInfo.values()); - Collections.sort(allPackageInfo); + CollectionUtils.sortIfNotEmpty(allPackageInfo); final List allModuleInfo = new ArrayList<>(moduleNameToModuleInfo.values()); - Collections.sort(allModuleInfo); + CollectionUtils.sortIfNotEmpty(allModuleInfo); return JSONSerializer.serializeObject(new SerializationFormat(CURRENT_SERIALIZATION_FORMAT, scanSpec, allClassInfo, allPackageInfo, allModuleInfo, rawClasspathEltOrderStrs), indentWidth, false); } diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 67df14319..88e5ce981 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -64,6 +64,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.utils.CollectionUtils; import nonapi.io.github.classgraph.utils.FastPathResolver; import nonapi.io.github.classgraph.utils.FileUtils; import nonapi.io.github.classgraph.utils.JarUtils; @@ -278,7 +279,7 @@ private static List orderClasspathElements( final Collection> classpathEltsIndexed) { final List> classpathEltsIndexedOrdered = new ArrayList<>( classpathEltsIndexed); - Collections.sort(classpathEltsIndexedOrdered, INDEXED_CLASSPATH_ELEMENT_COMPARATOR); + CollectionUtils.sortIfNotEmpty(classpathEltsIndexedOrdered, INDEXED_CLASSPATH_ELEMENT_COMPARATOR); final List classpathEltsOrdered = new ArrayList<>(classpathEltsIndexedOrdered.size()); for (final Entry ent : classpathEltsIndexedOrdered) { classpathEltsOrdered.add(ent.getValue()); @@ -633,7 +634,7 @@ public void processWorkUnit(final ClassfileScanWorkUnit workUnit, private void findNestedClasspathElements(final List> classpathElts, final LogNode log) { // Sort classpath elements into lexicographic order - Collections.sort(classpathElts, new Comparator>() { + CollectionUtils.sortIfNotEmpty(classpathElts, new Comparator>() { @Override public int compare(final SimpleEntry o1, final SimpleEntry o2) { diff --git a/src/main/java/nonapi/io/github/classgraph/WhiteBlackList.java b/src/main/java/nonapi/io/github/classgraph/WhiteBlackList.java index 31178071d..a147f4208 100644 --- a/src/main/java/nonapi/io/github/classgraph/WhiteBlackList.java +++ b/src/main/java/nonapi/io/github/classgraph/WhiteBlackList.java @@ -30,12 +30,12 @@ import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.regex.Pattern; +import nonapi.io.github.classgraph.utils.CollectionUtils; import nonapi.io.github.classgraph.utils.FileUtils; import nonapi.io.github.classgraph.utils.JarUtils; @@ -560,10 +560,10 @@ public boolean isSpecificallyWhitelisted(final String str) { /** Need to sort prefixes to ensure correct whitelist/blacklist evaluation (see Issue #167). */ void sortPrefixes() { if (whitelistPrefixes != null) { - Collections.sort(whitelistPrefixes); + CollectionUtils.sortIfNotEmpty(whitelistPrefixes); } if (blacklistPrefixes != null) { - Collections.sort(blacklistPrefixes); + CollectionUtils.sortIfNotEmpty(blacklistPrefixes); } } 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 6d9b78494..1c77bfb8a 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/AntClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/AntClassLoaderHandler.java @@ -39,7 +39,7 @@ class AntClassLoaderHandler implements ClassLoaderHandler { /** Class cannot be constructed. */ private AntClassLoaderHandler() { } - + /** * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. * 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 d1edc5aa9..3860bca20 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java @@ -58,7 +58,7 @@ class WebsphereLibertyClassLoaderHandler implements ClassLoaderHandler { /** Class cannot be constructed. */ private WebsphereLibertyClassLoaderHandler() { } - + /** * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. * diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderAndModuleFinder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderAndModuleFinder.java index 0df9cf8f7..e4004875b 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderAndModuleFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderAndModuleFinder.java @@ -39,6 +39,7 @@ import io.github.classgraph.ModuleRef; import nonapi.io.github.classgraph.ScanSpec; +import nonapi.io.github.classgraph.utils.CollectionUtils; import nonapi.io.github.classgraph.utils.LogNode; import nonapi.io.github.classgraph.utils.ReflectionUtils; @@ -190,7 +191,7 @@ private static List findModuleRefs(final LinkedHashSet layers } } // Sort modules in layer by name - Collections.sort(modulesInLayer); + CollectionUtils.sortIfNotEmpty(modulesInLayer); moduleRefOrder.addAll(modulesInLayer); } } 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 f543df77f..a26366066 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java @@ -46,6 +46,7 @@ import io.github.classgraph.ClassGraphException; import nonapi.io.github.classgraph.recycler.RecycleOnClose; +import nonapi.io.github.classgraph.utils.CollectionUtils; import nonapi.io.github.classgraph.utils.FileUtils; import nonapi.io.github.classgraph.utils.Join; import nonapi.io.github.classgraph.utils.LogNode; @@ -696,12 +697,12 @@ private void readCentralDirectory(final ZipFileSliceReader zipFileSliceReader, f } } final List versionsFoundSorted = new ArrayList<>(versionsFound); - Collections.sort(versionsFoundSorted); + CollectionUtils.sortIfNotEmpty(versionsFoundSorted); log.log("This is a multi-release jar, with versions: " + Join.join(", ", versionsFoundSorted)); } // Sort in decreasing order of version in preparation for version masking - Collections.sort(entries); + CollectionUtils.sortIfNotEmpty(entries); // Mask files that appear in multiple version sections, so that there is only one entry // for each unversioned path, i.e. the versioned path with the highest version number 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 0c69d67ca..da8a3941b 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/JSONSerializer.java +++ b/src/main/java/nonapi/io/github/classgraph/json/JSONSerializer.java @@ -34,7 +34,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; @@ -45,6 +44,7 @@ import java.util.concurrent.atomic.AtomicInteger; import io.github.classgraph.ClassGraphException; +import nonapi.io.github.classgraph.utils.CollectionUtils; /** * Fast, lightweight Java object to JSON serializer, and JSON to Java object deserializer. Handles cycles in the @@ -285,7 +285,7 @@ private static Object toJSONGraph(final Object obj, final Set) keys); + CollectionUtils.sortIfNotEmpty((ArrayList) keys); keysComparable = true; } @@ -344,7 +344,7 @@ private static Object toJSONGraph(final Object obj, final Set convertedValsList = new ArrayList<>(collection); if (Set.class.isAssignableFrom(cls)) { - Collections.sort(convertedValsList, SET_COMPARATOR); + CollectionUtils.sortIfNotEmpty(convertedValsList, SET_COMPARATOR); } // Convert items to JSON values diff --git a/src/main/java/nonapi/io/github/classgraph/utils/CollectionUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/CollectionUtils.java new file mode 100644 index 000000000..a78c6a6d6 --- /dev/null +++ b/src/main/java/nonapi/io/github/classgraph/utils/CollectionUtils.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) 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.util.Collections; +import java.util.Comparator; +import java.util.ConcurrentModificationException; +import java.util.List; + +/** + * Collection utilities. + */ +public final class CollectionUtils { + /** + * Sort a collection if it is not empty (to prevent {@link ConcurrentModificationException} if an immutable + * empty list that has been returned more than once is being sorted in one thread and iterated through in + * another thread -- #334). + * + * @param + * the element type + * @param list + * the list + */ + public static > void sortIfNotEmpty(final List list) { + if (!list.isEmpty()) { + Collections.sort(list); + } + } + + /** + * Sort a collection if it is not empty (to prevent {@link ConcurrentModificationException} if an immutable + * empty list that has been returned more than once is being sorted in one thread and iterated through in + * another thread -- #334). + * + * @param + * the element type + * @param list + * the list + */ + public static void sortIfNotEmpty(final List list, final Comparator comparator) { + if (!list.isEmpty()) { + Collections.sort(list, comparator); + } + } +} 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 74e1619e3..7339183b0 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/JarUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/JarUtils.java @@ -30,7 +30,6 @@ import java.io.File; import java.util.ArrayList; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -162,7 +161,7 @@ public static String[] smartPathSplit(final String pathStr, final char separator } } final List splitPointsSorted = new ArrayList<>(splitPoints); - Collections.sort(splitPointsSorted); + CollectionUtils.sortIfNotEmpty(splitPointsSorted); final List parts = new ArrayList<>(); for (int i = 1; i < splitPointsSorted.size(); i++) { final int idx0 = splitPointsSorted.get(i - 1); From e58b4a6bb1312fd07e04d5cabaa3d3f0b3b897dd Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 23 Mar 2019 21:07:19 -0600 Subject: [PATCH 0068/1778] [maven-release-plugin] prepare release classgraph-4.8.21 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 5ef452d0e..2146deb75 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.21-SNAPSHOT + 4.8.21 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.21 From 352eb881a98c2f215efe22d7d3ed0ea3fa15b51c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 23 Mar 2019 21:07:27 -0600 Subject: [PATCH 0069/1778] [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 2146deb75..d16e942d7 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.21 + 4.8.22-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.21 + HEAD From 0317b5fedb2180e15ad2a96fdffa1fc770d4eb25 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 25 Mar 2019 08:45:35 -0600 Subject: [PATCH 0070/1778] Update comments --- .../github/classgraph/classpath/ClassLoaderOrder.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 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 964548276..b6e5c49c7 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderOrder.java @@ -116,7 +116,7 @@ private ClassLoaderHandlerRegistryEntry getRegistryEntry(final ClassLoader class } /** - * Adds the. + * Add a {@link ClassLoader} to the {@link ClassLoader} order at the current position. * * @param classLoader * the class loader @@ -134,7 +134,7 @@ public void add(final ClassLoader classLoader) { } /** - * Delegate to. + * Recursively delegate to another {@link ClassLoader}. * * @param classLoader * the class loader @@ -150,11 +150,10 @@ public void delegateTo(final ClassLoader classLoader, final boolean isParent) { allParentClassLoaders.add(classLoader); } if (delegatedTo.add(classLoader)) { + // Find ClassLoaderHandlerRegistryEntry for this classloader final ClassLoaderHandlerRegistryEntry entry = getRegistryEntry(classLoader); - if (entry != null) { - // Delegate to the named classloader, by recursing to that classloader to get its classloader order - entry.findClassLoaderOrder(classLoader, this); - } + // Delegate to this classloader, by recursing to that classloader to get its classloader order + entry.findClassLoaderOrder(classLoader, this); } } } From 323340272b5308d70fc67974ea1d6182b84835d2 Mon Sep 17 00:00:00 2001 From: Tony <4016013+T101N@users.noreply.github.com> Date: Thu, 28 Mar 2019 03:57:21 +1000 Subject: [PATCH 0071/1778] Added tests for parameter annotation detection. --- ...cyForFunctionParameterAnnotationsTest.java | 138 ++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 src/test/java/io/github/classgraph/test/parameterannotation/RetentionPolicyForFunctionParameterAnnotationsTest.java diff --git a/src/test/java/io/github/classgraph/test/parameterannotation/RetentionPolicyForFunctionParameterAnnotationsTest.java b/src/test/java/io/github/classgraph/test/parameterannotation/RetentionPolicyForFunctionParameterAnnotationsTest.java new file mode 100644 index 000000000..8ee90ada0 --- /dev/null +++ b/src/test/java/io/github/classgraph/test/parameterannotation/RetentionPolicyForFunctionParameterAnnotationsTest.java @@ -0,0 +1,138 @@ +package io.github.classgraph.test.parameterannotation; + + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ClassInfo; +import io.github.classgraph.MethodInfo; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * This class tests for function parameter annotations with different retention + * policies. + * + * @author Tony Nguyen + * @version 4.8.22 + * @see + * RetentionPolicy + */ +public class RetentionPolicyForFunctionParameterAnnotationsTest { + private static ClassInfo classInfo; + + private final static String RETENTION_CLASS = "retention_class"; + private final static String RETENTION_RUNTIME = "retention_runtime"; + private final static String RETENTION_SOURCE = "retention_source"; + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.PARAMETER) + public @interface ParamAnnoRuntime { + String value() default RETENTION_RUNTIME; + } + + @Retention(RetentionPolicy.CLASS) + @Target(ElementType.PARAMETER) + public @interface ParamAnnoClass { + String value() default RETENTION_CLASS; + } + + @Retention(RetentionPolicy.SOURCE) + @Target(ElementType.PARAMETER) + public @interface ParamAnnoSource { + String value() default RETENTION_SOURCE; + } + + @BeforeClass + public static void beforeClass() { + classInfo = new ClassGraph() + .enableAllInfo() + .scan() + .getClassInfo(RetentionPolicyForFunctionParameterAnnotationsTest.class.getName()); + } + + /*------------------------------------------------------------------------*/ + + /** + * Must be able to detect parameter annotation with RUNTIME retention. + */ + @Test + public void canDetect_ParameterAnnotation_WithRuntimeRetention() { + MethodInfo methodInfo = classInfo.getMethodInfo() + .getSingleMethod("parameterAnnotation_WithRuntimeRetention"); + + assertThat(methodInfo.hasParameterAnnotation(ParamAnnoRuntime.class.getName())) + .isTrue(); + } + + public void parameterAnnotation_WithRuntimeRetention(@ParamAnnoRuntime int input) { + } + + /*------------------------------------------------------------------------*/ + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.PARAMETER) + public @interface SecondParamAnnoRuntime { + } + + /** + * Should be able to detect multiple annotations with RUNTIME retention for a + * single function parameter. + */ + @Test + public void canDetect_TwoAnnotations_WithRuntimeRetention_ForSingleParam() { + MethodInfo methodInfo = classInfo.getMethodInfo() + .getSingleMethod("twoAnnotations_WithRuntimeRetention_ForSingleParam"); + + assertThat(methodInfo.hasParameterAnnotation(ParamAnnoRuntime.class.getName())) + .isTrue(); + + assertThat(methodInfo.hasParameterAnnotation(SecondParamAnnoRuntime.class.getName())) + .isTrue(); + } + + public void twoAnnotations_WithRuntimeRetention_ForSingleParam( + @ParamAnnoRuntime @SecondParamAnnoRuntime int arg1) { + } + + /*------------------------------------------------------------------------*/ + + /** + * Annotations with CLASS retention does not need to be retained by vm at run + * time, but annotations with RUNTIME retention should still be detectable. + */ + @Test + public void canDetect_ParameterAnnotation_OneRuntimeRetention_OneClassRetention() { + MethodInfo methodInfo = classInfo.getMethodInfo() + .getSingleMethod("oneRuntimeRetention_OneClassRetention"); + + assertThat(methodInfo.hasParameterAnnotation(ParamAnnoRuntime.class.getName())) + .isTrue(); + } + + public void oneRuntimeRetention_OneClassRetention(@ParamAnnoRuntime @ParamAnnoClass int input) { + } + + /*------------------------------------------------------------------------*/ + + /** + * Annotations with SOURCE retention are discarded on compilation, but + * annotations with RUNTIME retention should still be detectable. + */ + @Test + public void canDetect_ParameterAnnotation_OneRuntimeRetention_OneSourceRetention() { + MethodInfo methodInfo = classInfo.getMethodInfo() + .getSingleMethod("oneRuntimeRetention_OneSourceRetention"); + + assertThat(methodInfo.hasParameterAnnotation(ParamAnnoRuntime.class.getName())) + .isTrue(); + } + + public void oneRuntimeRetention_OneSourceRetention(@ParamAnnoRuntime @ParamAnnoSource int input) { + } +} \ No newline at end of file From 2c108f89e8ae26bfbbc3843056d871024f00e6ba Mon Sep 17 00:00:00 2001 From: Tony <4016013+T101N@users.noreply.github.com> Date: Thu, 28 Mar 2019 06:29:18 +1000 Subject: [PATCH 0072/1778] Added test for changed ordering of parameter annotations --- ...cyForFunctionParameterAnnotationsTest.java | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) 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 8ee90ada0..f0ce10f26 100644 --- a/src/test/java/io/github/classgraph/test/parameterannotation/RetentionPolicyForFunctionParameterAnnotationsTest.java +++ b/src/test/java/io/github/classgraph/test/parameterannotation/RetentionPolicyForFunctionParameterAnnotationsTest.java @@ -97,7 +97,7 @@ public void canDetect_TwoAnnotations_WithRuntimeRetention_ForSingleParam() { } public void twoAnnotations_WithRuntimeRetention_ForSingleParam( - @ParamAnnoRuntime @SecondParamAnnoRuntime int arg1) { + @ParamAnnoRuntime @SecondParamAnnoRuntime int input) { } /*------------------------------------------------------------------------*/ @@ -120,6 +120,27 @@ public void oneRuntimeRetention_OneClassRetention(@ParamAnnoRuntime @ParamAnnoCl /*------------------------------------------------------------------------*/ + /** + * Annotations with CLASS retention does not need to be retained by vm at run + * time, but annotations with RUNTIME retention should still be detectable. + * + * This tests a changed ordering of the annotations with different retention + * policies. + */ + @Test + public void canDetect_ParameterAnnotation_OneRuntimeRetention_OneClassRetention_ChangedAnnotationOrder() { + MethodInfo methodInfo = classInfo.getMethodInfo() + .getSingleMethod("oneRuntimeRetention_OneClassRetention_ChangedAnnotationOrder"); + + assertThat(methodInfo.hasParameterAnnotation(ParamAnnoRuntime.class.getName())) + .isTrue(); + } + + public void oneRuntimeRetention_OneClassRetention_ChangedAnnotationOrder(@ParamAnnoClass @ParamAnnoRuntime int input) { + } + + /*------------------------------------------------------------------------*/ + /** * Annotations with SOURCE retention are discarded on compilation, but * annotations with RUNTIME retention should still be detectable. From f1cb6e59dc0ab099f0ce4f21d1019e086e8ea1f8 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 28 Mar 2019 03:38:01 -0600 Subject: [PATCH 0073/1778] Close ScanResult in afterClass() --- ...cyForFunctionParameterAnnotationsTest.java | 93 +++++++++---------- 1 file changed, 46 insertions(+), 47 deletions(-) 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 f0ce10f26..99176c5a9 100644 --- a/src/test/java/io/github/classgraph/test/parameterannotation/RetentionPolicyForFunctionParameterAnnotationsTest.java +++ b/src/test/java/io/github/classgraph/test/parameterannotation/RetentionPolicyForFunctionParameterAnnotationsTest.java @@ -1,29 +1,32 @@ package io.github.classgraph.test.parameterannotation; - -import io.github.classgraph.ClassGraph; -import io.github.classgraph.ClassInfo; -import io.github.classgraph.MethodInfo; -import org.junit.BeforeClass; -import org.junit.Test; +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 static org.assertj.core.api.Assertions.assertThat; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ClassInfo; +import io.github.classgraph.MethodInfo; +import io.github.classgraph.ScanResult; /** - * This class tests for function parameter annotations with different retention - * policies. + * This class tests for function parameter annotations with different retention policies. * * @author Tony Nguyen * @version 4.8.22 - * @see - * RetentionPolicy + * @see + * RetentionPolicy */ public class RetentionPolicyForFunctionParameterAnnotationsTest { + private static ScanResult scanResult; private static ClassInfo classInfo; private final static String RETENTION_CLASS = "retention_class"; @@ -50,10 +53,13 @@ public class RetentionPolicyForFunctionParameterAnnotationsTest { @BeforeClass public static void beforeClass() { - classInfo = new ClassGraph() - .enableAllInfo() - .scan() - .getClassInfo(RetentionPolicyForFunctionParameterAnnotationsTest.class.getName()); + scanResult = new ClassGraph().enableAllInfo().scan(); + classInfo = scanResult.getClassInfo(RetentionPolicyForFunctionParameterAnnotationsTest.class.getName()); + } + + @AfterClass + public static void afterClass() { + scanResult.close(); } /*------------------------------------------------------------------------*/ @@ -63,14 +69,13 @@ public static void beforeClass() { */ @Test public void canDetect_ParameterAnnotation_WithRuntimeRetention() { - MethodInfo methodInfo = classInfo.getMethodInfo() + final MethodInfo methodInfo = classInfo.getMethodInfo() .getSingleMethod("parameterAnnotation_WithRuntimeRetention"); - assertThat(methodInfo.hasParameterAnnotation(ParamAnnoRuntime.class.getName())) - .isTrue(); + assertThat(methodInfo.hasParameterAnnotation(ParamAnnoRuntime.class.getName())).isTrue(); } - public void parameterAnnotation_WithRuntimeRetention(@ParamAnnoRuntime int input) { + public void parameterAnnotation_WithRuntimeRetention(@ParamAnnoRuntime final int input) { } /*------------------------------------------------------------------------*/ @@ -81,79 +86,73 @@ public void parameterAnnotation_WithRuntimeRetention(@ParamAnnoRuntime int input } /** - * Should be able to detect multiple annotations with RUNTIME retention for a - * single function parameter. + * Should be able to detect multiple annotations with RUNTIME retention for a single function parameter. */ @Test public void canDetect_TwoAnnotations_WithRuntimeRetention_ForSingleParam() { - MethodInfo methodInfo = classInfo.getMethodInfo() + final MethodInfo methodInfo = classInfo.getMethodInfo() .getSingleMethod("twoAnnotations_WithRuntimeRetention_ForSingleParam"); - assertThat(methodInfo.hasParameterAnnotation(ParamAnnoRuntime.class.getName())) - .isTrue(); + assertThat(methodInfo.hasParameterAnnotation(ParamAnnoRuntime.class.getName())).isTrue(); - assertThat(methodInfo.hasParameterAnnotation(SecondParamAnnoRuntime.class.getName())) - .isTrue(); + assertThat(methodInfo.hasParameterAnnotation(SecondParamAnnoRuntime.class.getName())).isTrue(); } public void twoAnnotations_WithRuntimeRetention_ForSingleParam( - @ParamAnnoRuntime @SecondParamAnnoRuntime int input) { + @ParamAnnoRuntime @SecondParamAnnoRuntime final int input) { } /*------------------------------------------------------------------------*/ /** - * Annotations with CLASS retention does not need to be retained by vm at run - * time, but annotations with RUNTIME retention should still be detectable. + * Annotations with CLASS retention does not need to be retained by vm at run time, but annotations with RUNTIME + * retention should still be detectable. */ @Test public void canDetect_ParameterAnnotation_OneRuntimeRetention_OneClassRetention() { - MethodInfo methodInfo = classInfo.getMethodInfo() + final MethodInfo methodInfo = classInfo.getMethodInfo() .getSingleMethod("oneRuntimeRetention_OneClassRetention"); - assertThat(methodInfo.hasParameterAnnotation(ParamAnnoRuntime.class.getName())) - .isTrue(); + assertThat(methodInfo.hasParameterAnnotation(ParamAnnoRuntime.class.getName())).isTrue(); } - public void oneRuntimeRetention_OneClassRetention(@ParamAnnoRuntime @ParamAnnoClass int input) { + public void oneRuntimeRetention_OneClassRetention(@ParamAnnoRuntime @ParamAnnoClass final int input) { } /*------------------------------------------------------------------------*/ /** - * Annotations with CLASS retention does not need to be retained by vm at run - * time, but annotations with RUNTIME retention should still be detectable. + * Annotations with CLASS retention does not need to be retained by vm at run time, but annotations with RUNTIME + * retention should still be detectable. * - * This tests a changed ordering of the annotations with different retention - * policies. + * This tests a changed ordering of the annotations with different retention policies. */ @Test public void canDetect_ParameterAnnotation_OneRuntimeRetention_OneClassRetention_ChangedAnnotationOrder() { - MethodInfo methodInfo = classInfo.getMethodInfo() + final MethodInfo methodInfo = classInfo.getMethodInfo() .getSingleMethod("oneRuntimeRetention_OneClassRetention_ChangedAnnotationOrder"); - assertThat(methodInfo.hasParameterAnnotation(ParamAnnoRuntime.class.getName())) - .isTrue(); + assertThat(methodInfo.hasParameterAnnotation(ParamAnnoRuntime.class.getName())).isTrue(); } - public void oneRuntimeRetention_OneClassRetention_ChangedAnnotationOrder(@ParamAnnoClass @ParamAnnoRuntime int input) { + public void oneRuntimeRetention_OneClassRetention_ChangedAnnotationOrder( + @ParamAnnoClass @ParamAnnoRuntime final int input) { } /*------------------------------------------------------------------------*/ /** - * Annotations with SOURCE retention are discarded on compilation, but - * annotations with RUNTIME retention should still be detectable. + * Annotations with SOURCE retention are discarded on compilation, but annotations with RUNTIME retention should + * still be detectable. */ @Test public void canDetect_ParameterAnnotation_OneRuntimeRetention_OneSourceRetention() { - MethodInfo methodInfo = classInfo.getMethodInfo() + final MethodInfo methodInfo = classInfo.getMethodInfo() .getSingleMethod("oneRuntimeRetention_OneSourceRetention"); - assertThat(methodInfo.hasParameterAnnotation(ParamAnnoRuntime.class.getName())) - .isTrue(); + assertThat(methodInfo.hasParameterAnnotation(ParamAnnoRuntime.class.getName())).isTrue(); } - public void oneRuntimeRetention_OneSourceRetention(@ParamAnnoRuntime @ParamAnnoSource int input) { + public void oneRuntimeRetention_OneSourceRetention(@ParamAnnoRuntime @ParamAnnoSource final int input) { } } \ No newline at end of file From 7e7ab972a810f5e44b0a8daa533fd286d04f72fa Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 28 Mar 2019 03:41:16 -0600 Subject: [PATCH 0074/1778] Source > Cleanup, + add JavaDoc with JAutoDoc --- ...cyForFunctionParameterAnnotationsTest.java | 81 +++++++++++++++++-- 1 file changed, 76 insertions(+), 5 deletions(-) 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 99176c5a9..9710ce784 100644 --- a/src/test/java/io/github/classgraph/test/parameterannotation/RetentionPolicyForFunctionParameterAnnotationsTest.java +++ b/src/test/java/io/github/classgraph/test/parameterannotation/RetentionPolicyForFunctionParameterAnnotationsTest.java @@ -26,43 +26,81 @@ * RetentionPolicy */ public class RetentionPolicyForFunctionParameterAnnotationsTest { + /** The scan result. */ private static ScanResult scanResult; + + /** The class info. */ private static ClassInfo classInfo; + /** The Constant RETENTION_CLASS. */ private final static String RETENTION_CLASS = "retention_class"; + + /** The Constant RETENTION_RUNTIME. */ private final static String RETENTION_RUNTIME = "retention_runtime"; + + /** The Constant RETENTION_SOURCE. */ private final static String RETENTION_SOURCE = "retention_source"; + /** + * The Interface ParamAnnoRuntime. + */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.PARAMETER) public @interface ParamAnnoRuntime { + /** + * Value. + * + * @return the string + */ String value() default RETENTION_RUNTIME; } + /** + * The Interface ParamAnnoClass. + */ @Retention(RetentionPolicy.CLASS) @Target(ElementType.PARAMETER) public @interface ParamAnnoClass { + /** + * Value. + * + * @return the string + */ String value() default RETENTION_CLASS; } + /** + * The Interface ParamAnnoSource. + */ @Retention(RetentionPolicy.SOURCE) @Target(ElementType.PARAMETER) public @interface ParamAnnoSource { + /** + * Value. + * + * @return the string + */ String value() default RETENTION_SOURCE; } + /** + * Generate ScanResult before running tests. + */ @BeforeClass public static void beforeClass() { scanResult = new ClassGraph().enableAllInfo().scan(); classInfo = scanResult.getClassInfo(RetentionPolicyForFunctionParameterAnnotationsTest.class.getName()); } + /** + * Close ScanResult after running tests. + */ @AfterClass public static void afterClass() { scanResult.close(); } - /*------------------------------------------------------------------------*/ + // ------------------------------------------------------------------------------------------------------------- /** * Must be able to detect parameter annotation with RUNTIME retention. @@ -75,11 +113,20 @@ public void canDetect_ParameterAnnotation_WithRuntimeRetention() { assertThat(methodInfo.hasParameterAnnotation(ParamAnnoRuntime.class.getName())).isTrue(); } + /** + * Parameter annotation with runtime retention. + * + * @param input + * the input + */ public void parameterAnnotation_WithRuntimeRetention(@ParamAnnoRuntime final int input) { } - /*------------------------------------------------------------------------*/ + // ------------------------------------------------------------------------------------------------------------- + /** + * The Interface SecondParamAnnoRuntime. + */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.PARAMETER) public @interface SecondParamAnnoRuntime { @@ -98,11 +145,17 @@ public void canDetect_TwoAnnotations_WithRuntimeRetention_ForSingleParam() { assertThat(methodInfo.hasParameterAnnotation(SecondParamAnnoRuntime.class.getName())).isTrue(); } + /** + * Two annotations with runtime retention for single param. + * + * @param input + * the input + */ public void twoAnnotations_WithRuntimeRetention_ForSingleParam( @ParamAnnoRuntime @SecondParamAnnoRuntime final int input) { } - /*------------------------------------------------------------------------*/ + // ------------------------------------------------------------------------------------------------------------- /** * Annotations with CLASS retention does not need to be retained by vm at run time, but annotations with RUNTIME @@ -116,10 +169,16 @@ public void canDetect_ParameterAnnotation_OneRuntimeRetention_OneClassRetention( assertThat(methodInfo.hasParameterAnnotation(ParamAnnoRuntime.class.getName())).isTrue(); } + /** + * One runtime retention annotation, one class retention annotation. + * + * @param input + * the input + */ public void oneRuntimeRetention_OneClassRetention(@ParamAnnoRuntime @ParamAnnoClass final int input) { } - /*------------------------------------------------------------------------*/ + // ------------------------------------------------------------------------------------------------------------- /** * Annotations with CLASS retention does not need to be retained by vm at run time, but annotations with RUNTIME @@ -135,11 +194,17 @@ public void canDetect_ParameterAnnotation_OneRuntimeRetention_OneClassRetention_ assertThat(methodInfo.hasParameterAnnotation(ParamAnnoRuntime.class.getName())).isTrue(); } + /** + * One runtime retention annotation, one class retention annotation, in reverse order. + * + * @param input + * the input + */ public void oneRuntimeRetention_OneClassRetention_ChangedAnnotationOrder( @ParamAnnoClass @ParamAnnoRuntime final int input) { } - /*------------------------------------------------------------------------*/ + // ------------------------------------------------------------------------------------------------------------- /** * Annotations with SOURCE retention are discarded on compilation, but annotations with RUNTIME retention should @@ -153,6 +218,12 @@ public void canDetect_ParameterAnnotation_OneRuntimeRetention_OneSourceRetention assertThat(methodInfo.hasParameterAnnotation(ParamAnnoRuntime.class.getName())).isTrue(); } + /** + * One runtime retention annotation, one source retention annotation. + * + * @param input + * the input + */ public void oneRuntimeRetention_OneSourceRetention(@ParamAnnoRuntime @ParamAnnoSource final int input) { } } \ No newline at end of file From ad7248d0aa4d991e9aae552972adfcc0ab974dff Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 28 Mar 2019 03:47:11 -0600 Subject: [PATCH 0075/1778] Merge together RuntimeVis and RuntimeInvis ParameterAnnotations (#335) --- .../java/io/github/classgraph/Classfile.java | 57 +++++++++++++------ 1 file changed, 40 insertions(+), 17 deletions(-) diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index 16b36dfc0..caf2ef732 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -1167,10 +1167,10 @@ && constantPoolStringEquals(attributeNameCpIdx, "ConstantValue")) { attributeNameCpIdx, "RuntimeInvisibleAnnotations")))) { // Read annotation names final int fieldAnnotationCount = inputStreamOrByteBuffer.readUnsignedShort(); - if (fieldAnnotationInfo == null && fieldAnnotationCount > 0) { - fieldAnnotationInfo = new AnnotationInfoList(1); - } - if (fieldAnnotationInfo != null) { + if (fieldAnnotationCount > 0) { + if (fieldAnnotationInfo == null) { + fieldAnnotationInfo = new AnnotationInfoList(1); + } for (int k = 0; k < fieldAnnotationCount; k++) { final AnnotationInfo fieldAnnotation = readAnnotation(); fieldAnnotationInfo.add(fieldAnnotation); @@ -1247,10 +1247,10 @@ private void readMethods() throws IOException, ClassfileFormatException { || (!scanSpec.disableRuntimeInvisibleAnnotations && constantPoolStringEquals( attributeNameCpIdx, "RuntimeInvisibleAnnotations")))) { final int methodAnnotationCount = inputStreamOrByteBuffer.readUnsignedShort(); - if (methodAnnotationInfo == null && methodAnnotationCount > 0) { - methodAnnotationInfo = new AnnotationInfoList(1); - } - if (methodAnnotationInfo != null) { + if (methodAnnotationCount > 0) { + if (methodAnnotationInfo == null) { + methodAnnotationInfo = new AnnotationInfoList(1); + } for (int k = 0; k < methodAnnotationCount; k++) { final AnnotationInfo annotationInfo = readAnnotation(); methodAnnotationInfo.add(annotationInfo); @@ -1260,14 +1260,35 @@ private void readMethods() throws IOException, ClassfileFormatException { && (constantPoolStringEquals(attributeNameCpIdx, "RuntimeVisibleParameterAnnotations") || (!scanSpec.disableRuntimeInvisibleAnnotations && constantPoolStringEquals( attributeNameCpIdx, "RuntimeInvisibleParameterAnnotations")))) { - final int paramCount = inputStreamOrByteBuffer.readUnsignedByte(); - methodParameterAnnotations = new AnnotationInfo[paramCount][]; - for (int k = 0; k < paramCount; k++) { + // Merge together runtime visible and runtime invisible annotations into a single array + // of annotations for each method parameter (runtime visible and runtime invisible + // annotations are given in separate attributes, so if both attributes are present, + // have to make the parameter annotation arrays larger when the second attribute is + // encountered). + final int numParams = inputStreamOrByteBuffer.readUnsignedByte(); + if (methodParameterAnnotations == null) { + methodParameterAnnotations = new AnnotationInfo[numParams][]; + } else if (methodParameterAnnotations.length != numParams) { + throw new ClassfileFormatException( + "Mismatch in number of parameters between RuntimeVisibleParameterAnnotations " + + "and RuntimeInvisibleParameterAnnotations"); + } + for (int paramIdx = 0; paramIdx < numParams; paramIdx++) { final int numAnnotations = inputStreamOrByteBuffer.readUnsignedShort(); - methodParameterAnnotations[k] = numAnnotations == 0 ? NO_ANNOTATIONS - : new AnnotationInfo[numAnnotations]; - for (int l = 0; l < numAnnotations; l++) { - methodParameterAnnotations[k][l] = readAnnotation(); + if (numAnnotations > 0) { + int annStartIdx = 0; + if (methodParameterAnnotations[paramIdx] != null) { + annStartIdx = methodParameterAnnotations[paramIdx].length; + methodParameterAnnotations[paramIdx] = Arrays.copyOf( + methodParameterAnnotations[paramIdx], annStartIdx + numAnnotations); + } else { + methodParameterAnnotations[paramIdx] = new AnnotationInfo[numAnnotations]; + } + for (int annIdx = 0; annIdx < numAnnotations; annIdx++) { + methodParameterAnnotations[paramIdx][annStartIdx + annIdx] = readAnnotation(); + } + } else if (methodParameterAnnotations[paramIdx] == null) { + methodParameterAnnotations[paramIdx] = NO_ANNOTATIONS; } } } else if (constantPoolStringEquals(attributeNameCpIdx, "MethodParameters")) { @@ -1333,11 +1354,13 @@ private void readClassAttributes() throws IOException, ClassfileFormatException || (!scanSpec.disableRuntimeInvisibleAnnotations && constantPoolStringEquals( attributeNameCpIdx, "RuntimeInvisibleAnnotations")))) { final int annotationCount = inputStreamOrByteBuffer.readUnsignedShort(); - for (int m = 0; m < annotationCount; m++) { + if (annotationCount > 0) { if (classAnnotations == null) { classAnnotations = new AnnotationInfoList(); } - classAnnotations.add(readAnnotation()); + for (int m = 0; m < annotationCount; m++) { + classAnnotations.add(readAnnotation()); + } } } else if (constantPoolStringEquals(attributeNameCpIdx, "InnerClasses")) { final int numInnerClasses = inputStreamOrByteBuffer.readUnsignedShort(); From e936d8922fa21b6d7e7799ce6ff24814363864d6 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 28 Mar 2019 03:47:55 -0600 Subject: [PATCH 0076/1778] [maven-release-plugin] prepare release classgraph-4.8.22 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index d16e942d7..f9db0200d 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.22-SNAPSHOT + 4.8.22 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.22 From e0e9afa6a0f39a3bf055fd4b3398c9e3e2e2e798 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 28 Mar 2019 03:48:01 -0600 Subject: [PATCH 0077/1778] [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 f9db0200d..5ec50632c 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.22 + 4.8.23-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.22 + HEAD From 8c9424cab76adceefba6a3de20c25dc61ac48b00 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 28 Mar 2019 03:59:26 -0600 Subject: [PATCH 0078/1778] Only scan the relevant package --- .../RetentionPolicyForFunctionParameterAnnotationsTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 9710ce784..5f8643714 100644 --- a/src/test/java/io/github/classgraph/test/parameterannotation/RetentionPolicyForFunctionParameterAnnotationsTest.java +++ b/src/test/java/io/github/classgraph/test/parameterannotation/RetentionPolicyForFunctionParameterAnnotationsTest.java @@ -88,7 +88,9 @@ public class RetentionPolicyForFunctionParameterAnnotationsTest { */ @BeforeClass public static void beforeClass() { - scanResult = new ClassGraph().enableAllInfo().scan(); + scanResult = new ClassGraph() + .whitelistPackages(RetentionPolicyForFunctionParameterAnnotationsTest.class.getPackage().getName()) + .enableAllInfo().scan(); classInfo = scanResult.getClassInfo(RetentionPolicyForFunctionParameterAnnotationsTest.class.getName()); } From 8b59c998a4d04eef118ad45d31a59a0913055467 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 28 Mar 2019 04:27:39 -0600 Subject: [PATCH 0079/1778] Fix comment --- src/main/java/io/github/classgraph/ClassInfo.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index de7ac0599..0538b0b95 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -483,6 +483,7 @@ private void addFieldOrMethodAnnotationInfo(final AnnotationInfoList annotationI */ void addFieldInfo(final FieldInfoList fieldInfoList, final Map classNameToClassInfo) { for (final FieldInfo fi : fieldInfoList) { + // Index field annotations addFieldOrMethodAnnotationInfo(fi.annotationInfo, /* isField = */ true, classNameToClassInfo); } if (this.fieldInfo == null) { @@ -502,9 +503,10 @@ void addFieldInfo(final FieldInfoList fieldInfoList, final Map classNameToClassInfo) { for (final MethodInfo mi : methodInfoList) { + // Index method annotations addFieldOrMethodAnnotationInfo(mi.annotationInfo, /* isField = */ false, classNameToClassInfo); - // Currently it is not possible to find methods by method parameter annotation + // Index method parameter annotations if (mi.parameterAnnotationInfo != null) { for (int i = 0; i < mi.parameterAnnotationInfo.length; i++) { final AnnotationInfo[] paramAnnotationInfoArr = mi.parameterAnnotationInfo[i]; From b1dd40112b89b425cfb30b1b8bff4eee88970386 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 4 Apr 2019 10:52:54 -0600 Subject: [PATCH 0080/1778] Add support for `Bundle-ClassPath` (@jechlin) --- .../classgraph/ClasspathElementZip.java | 37 ++++++++++++++++--- .../ClassLoaderHandlerRegistry.java | 2 + .../fastzipfilereader/LogicalZipFile.java | 16 ++++++++ 3 files changed, 50 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index 9c015fbba..b0e3bd8e3 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -200,17 +200,20 @@ void open(final WorkQueue workQueue, final LogNode log) if (logicalZipFile.classPathManifestEntryValue != null) { // Class-Path entries in the manifest file are resolved relative to the dir that // the manifest's jarfile is contained in -- get parent dir of logical zipfile - final String path = logicalZipFile.getPath(); - final int lastPlingIdx = path.lastIndexOf('!'); - final int lastSlashIdx = Math.max(path.lastIndexOf('/'), lastPlingIdx); + final String zipFilePath = logicalZipFile.getPath(); + final int lastPlingIdx = zipFilePath.lastIndexOf('!'); + final int lastSlashIdx = Math.max(zipFilePath.lastIndexOf('/'), lastPlingIdx); // Get path of parent jarfile, if this is a nested jar. If there is a Class-Path entry in a nested // jar, probably the intent is that the Class-Path path is relative to the root of the parent jar // (although this is an unlikely scenario). - final String parentZipPrefix = lastPlingIdx < 0 ? "" : path.substring(0, lastPlingIdx + 1); - String parentPathPrefix = lastSlashIdx < 0 ? "" : path.substring(lastPlingIdx + 1, lastSlashIdx + 1); + final String parentZipPrefix = lastPlingIdx < 0 ? "" : zipFilePath.substring(0, lastPlingIdx + 1); + String parentPathPrefix = lastSlashIdx < 0 ? "" + : zipFilePath.substring(lastPlingIdx + 1, lastSlashIdx + 1); if (parentZipPrefix.isEmpty() && parentPathPrefix.isEmpty()) { parentPathPrefix = FileUtils.CURR_DIR_PATH; } + // Add paths in manifest file's "Class-Path" entry to the classpath, resolving paths relative to + // the parent directory or jar for (final String childClassPathEltPath : logicalZipFile.classPathManifestEntryValue.split(" ")) { if (!childClassPathEltPath.isEmpty()) { // Resolve Class-Path entry relative to containing dir @@ -233,6 +236,30 @@ void open(final WorkQueue workQueue, final LogNode log) } } } + // Add paths in an OSGi bundle jar manifest's "Bundle-ClassPath" entry to the classpath, resolving + // the paths relative to the root of the jarfile + final String zipFilePathPrefix = zipFilePath + "!"; + for (String childClassPathEltPath : logicalZipFile.bundleClassPathManifestEntryValue.split(",")) { + // Assume that Bundle-ClassPath paths have to be given relative to jarfile root + while (childClassPathEltPath.startsWith("/")) { + childClassPathEltPath = childClassPathEltPath.substring(1); + } + // Currently the position of "." relative to child classpath entries is ignored (the + // Bundle-ClassPath path is treated as if "." is in the first position, since child + // classpath entries are always added to the classpath after the parent classpath + // entry that they were obtained from). + if (!childClassPathEltPath.isEmpty() && !childClassPathEltPath.equals(".")) { + // Resolve Bundle-ClassPath entry within jar + final String childClassPathEltPathResolved = FastPathResolver.resolve(zipFilePathPrefix, + childClassPathEltPath); + workQueue.addWorkUnit(new ClasspathEntryWorkUnit( + /* rawClasspathEntry = */ // + new SimpleEntry<>(childClassPathEltPathResolved, classLoader), + /* parentClasspathElement = */ this, + /* orderWithinParentClasspathElement = */ + childClasspathEntryIdx++)); + } + } } } 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 03099e3f4..8d79b2706 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java @@ -84,6 +84,8 @@ public class ClassLoaderHandlerRegistry { "BOOT-INF/lib/", "BOOT-INF/lib-provided/", // Tomcat "WEB-INF/lib/", "WEB-INF/lib-provided/", + // OSGi + "META-INF/lib/", // Tomcat and others "lib/", // Extension dir 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 a26366066..60de3b655 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java @@ -68,6 +68,9 @@ public class LogicalZipFile extends ZipFileSlice implements AutoCloseable { /** The value of the "Class-Path" manifest entry, if present in the manifest, else null. */ public String classPathManifestEntryValue; + /** The value of the "Bundle-ClassPath" manifest entry, if present in the manifest, else null. */ + public String bundleClassPathManifestEntryValue; + /** The value of the "Add-Exports" manifest entry, if present in the manifest, else null. */ public String addExportsManifestEntryValue; @@ -100,6 +103,9 @@ public class LogicalZipFile extends ZipFileSlice implements AutoCloseable { /** The {@code "Class-Path"} manifest key. */ private static final byte[] CLASS_PATH_KEY = manifestKeyToBytes("Class-Path"); + /** The {@code "Bundle-ClassPath"} manifest key. */ + private static final byte[] BUNDLE_CLASSPATH_KEY = manifestKeyToBytes("Bundle-ClassPath"); + /** The {@code "Spring-Boot-Classes"} manifest key. */ private static final byte[] SPRING_BOOT_CLASSES_KEY = manifestKeyToBytes("Spring-Boot-Classes"); @@ -314,6 +320,16 @@ private void parseManifest(final FastZipEntry manifestZipEntry, final LogNode lo } i = manifestValueAndEndIdx.getValue(); + } else if (keyMatchesAtPosition(manifest, BUNDLE_CLASSPATH_KEY, i)) { + final Entry manifestValueAndEndIdx = getManifestValue(manifest, + i + BUNDLE_CLASSPATH_KEY.length + 1); + // Add Bundle-ClassPath manifest entry values to classpath + bundleClassPathManifestEntryValue = manifestValueAndEndIdx.getKey(); + if (log != null) { + log.log("Found Bundle-ClassPath entry in manifest file: " + bundleClassPathManifestEntryValue); + } + i = manifestValueAndEndIdx.getValue(); + } else if (keyMatchesAtPosition(manifest, SPRING_BOOT_CLASSES_KEY, i)) { final Entry manifestValueAndEndIdx = getManifestValue(manifest, i + SPRING_BOOT_CLASSES_KEY.length + 1); From dcc2897e868fabe077956434a22eb3575377851f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 4 Apr 2019 10:56:58 -0600 Subject: [PATCH 0081/1778] Fix NPE` (@jechlin) --- .../classgraph/ClasspathElementZip.java | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index b0e3bd8e3..9471d7d5e 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -238,26 +238,28 @@ void open(final WorkQueue workQueue, final LogNode log) } // Add paths in an OSGi bundle jar manifest's "Bundle-ClassPath" entry to the classpath, resolving // the paths relative to the root of the jarfile - final String zipFilePathPrefix = zipFilePath + "!"; - for (String childClassPathEltPath : logicalZipFile.bundleClassPathManifestEntryValue.split(",")) { - // Assume that Bundle-ClassPath paths have to be given relative to jarfile root - while (childClassPathEltPath.startsWith("/")) { - childClassPathEltPath = childClassPathEltPath.substring(1); - } - // Currently the position of "." relative to child classpath entries is ignored (the - // Bundle-ClassPath path is treated as if "." is in the first position, since child - // classpath entries are always added to the classpath after the parent classpath - // entry that they were obtained from). - if (!childClassPathEltPath.isEmpty() && !childClassPathEltPath.equals(".")) { - // Resolve Bundle-ClassPath entry within jar - final String childClassPathEltPathResolved = FastPathResolver.resolve(zipFilePathPrefix, - childClassPathEltPath); - workQueue.addWorkUnit(new ClasspathEntryWorkUnit( - /* rawClasspathEntry = */ // - new SimpleEntry<>(childClassPathEltPathResolved, classLoader), - /* parentClasspathElement = */ this, - /* orderWithinParentClasspathElement = */ - childClasspathEntryIdx++)); + if (logicalZipFile.bundleClassPathManifestEntryValue != null) { + final String zipFilePathPrefix = zipFilePath + "!"; + for (String childClassPathEltPath : logicalZipFile.bundleClassPathManifestEntryValue.split(",")) { + // Assume that Bundle-ClassPath paths have to be given relative to jarfile root + while (childClassPathEltPath.startsWith("/")) { + childClassPathEltPath = childClassPathEltPath.substring(1); + } + // Currently the position of "." relative to child classpath entries is ignored (the + // Bundle-ClassPath path is treated as if "." is in the first position, since child + // classpath entries are always added to the classpath after the parent classpath + // entry that they were obtained from). + if (!childClassPathEltPath.isEmpty() && !childClassPathEltPath.equals(".")) { + // Resolve Bundle-ClassPath entry within jar + final String childClassPathEltPathResolved = FastPathResolver.resolve(zipFilePathPrefix, + childClassPathEltPath); + workQueue.addWorkUnit(new ClasspathEntryWorkUnit( + /* rawClasspathEntry = */ // + new SimpleEntry<>(childClassPathEltPathResolved, classLoader), + /* parentClasspathElement = */ this, + /* orderWithinParentClasspathElement = */ + childClasspathEntryIdx++)); + } } } } From 364fdf1f62e0cbca3f54b0e90750f1fab9f7e0e6 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 4 Apr 2019 10:58:35 -0600 Subject: [PATCH 0082/1778] More fixes for `Bundle-ClassPath (@jechlin) --- .../classgraph/ClasspathElementZip.java | 59 ++++++++++--------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index 9471d7d5e..6169c6cbb 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -197,18 +197,19 @@ void open(final WorkQueue workQueue, final LogNode log) } // Create child classpath elements from values obtained from Class-Path entry in manifest + final String logicalZipFilePath = logicalZipFile.getPath(); if (logicalZipFile.classPathManifestEntryValue != null) { // Class-Path entries in the manifest file are resolved relative to the dir that // the manifest's jarfile is contained in -- get parent dir of logical zipfile - final String zipFilePath = logicalZipFile.getPath(); - final int lastPlingIdx = zipFilePath.lastIndexOf('!'); - final int lastSlashIdx = Math.max(zipFilePath.lastIndexOf('/'), lastPlingIdx); + final int lastPlingIdx = logicalZipFilePath.lastIndexOf('!'); + final int lastSlashIdx = Math.max(logicalZipFilePath.lastIndexOf('/'), lastPlingIdx); // Get path of parent jarfile, if this is a nested jar. If there is a Class-Path entry in a nested // jar, probably the intent is that the Class-Path path is relative to the root of the parent jar // (although this is an unlikely scenario). - final String parentZipPrefix = lastPlingIdx < 0 ? "" : zipFilePath.substring(0, lastPlingIdx + 1); + final String parentZipPrefix = lastPlingIdx < 0 ? "" + : logicalZipFilePath.substring(0, lastPlingIdx + 1); String parentPathPrefix = lastSlashIdx < 0 ? "" - : zipFilePath.substring(lastPlingIdx + 1, lastSlashIdx + 1); + : logicalZipFilePath.substring(lastPlingIdx + 1, lastSlashIdx + 1); if (parentZipPrefix.isEmpty() && parentPathPrefix.isEmpty()) { parentPathPrefix = FileUtils.CURR_DIR_PATH; } @@ -236,30 +237,30 @@ void open(final WorkQueue workQueue, final LogNode log) } } } - // Add paths in an OSGi bundle jar manifest's "Bundle-ClassPath" entry to the classpath, resolving - // the paths relative to the root of the jarfile - if (logicalZipFile.bundleClassPathManifestEntryValue != null) { - final String zipFilePathPrefix = zipFilePath + "!"; - for (String childClassPathEltPath : logicalZipFile.bundleClassPathManifestEntryValue.split(",")) { - // Assume that Bundle-ClassPath paths have to be given relative to jarfile root - while (childClassPathEltPath.startsWith("/")) { - childClassPathEltPath = childClassPathEltPath.substring(1); - } - // Currently the position of "." relative to child classpath entries is ignored (the - // Bundle-ClassPath path is treated as if "." is in the first position, since child - // classpath entries are always added to the classpath after the parent classpath - // entry that they were obtained from). - if (!childClassPathEltPath.isEmpty() && !childClassPathEltPath.equals(".")) { - // Resolve Bundle-ClassPath entry within jar - final String childClassPathEltPathResolved = FastPathResolver.resolve(zipFilePathPrefix, - childClassPathEltPath); - workQueue.addWorkUnit(new ClasspathEntryWorkUnit( - /* rawClasspathEntry = */ // - new SimpleEntry<>(childClassPathEltPathResolved, classLoader), - /* parentClasspathElement = */ this, - /* orderWithinParentClasspathElement = */ - childClasspathEntryIdx++)); - } + } + // Add paths in an OSGi bundle jar manifest's "Bundle-ClassPath" entry to the classpath, resolving + // the paths relative to the root of the jarfile + if (logicalZipFile.bundleClassPathManifestEntryValue != null) { + final String zipFilePathPrefix = logicalZipFilePath + "!"; + for (String childClassPathEltPath : logicalZipFile.bundleClassPathManifestEntryValue.split(",")) { + // Assume that Bundle-ClassPath paths have to be given relative to jarfile root + while (childClassPathEltPath.startsWith("/")) { + childClassPathEltPath = childClassPathEltPath.substring(1); + } + // Currently the position of "." relative to child classpath entries is ignored (the + // Bundle-ClassPath path is treated as if "." is in the first position, since child + // classpath entries are always added to the classpath after the parent classpath + // entry that they were obtained from). + if (!childClassPathEltPath.isEmpty() && !childClassPathEltPath.equals(".")) { + // Resolve Bundle-ClassPath entry within jar + final String childClassPathEltPathResolved = FastPathResolver.resolve(zipFilePathPrefix, + childClassPathEltPath); + workQueue.addWorkUnit(new ClasspathEntryWorkUnit( + /* rawClasspathEntry = */ // + new SimpleEntry<>(childClassPathEltPathResolved, classLoader), + /* parentClasspathElement = */ this, + /* orderWithinParentClasspathElement = */ + childClasspathEntryIdx++)); } } } From 1abb304f7eae868e9a60a3f91e7dacd38c0567a7 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 4 Apr 2019 11:00:00 -0600 Subject: [PATCH 0083/1778] More fixes for `Bundle-ClassPath (@jechlin) --- .../io/github/classgraph/ClasspathElementZip.java | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index 6169c6cbb..7c1c1684e 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -197,19 +197,17 @@ void open(final WorkQueue workQueue, final LogNode log) } // Create child classpath elements from values obtained from Class-Path entry in manifest - final String logicalZipFilePath = logicalZipFile.getPath(); if (logicalZipFile.classPathManifestEntryValue != null) { // Class-Path entries in the manifest file are resolved relative to the dir that // the manifest's jarfile is contained in -- get parent dir of logical zipfile - final int lastPlingIdx = logicalZipFilePath.lastIndexOf('!'); - final int lastSlashIdx = Math.max(logicalZipFilePath.lastIndexOf('/'), lastPlingIdx); + final int lastPlingIdx = zipFilePath.lastIndexOf('!'); + final int lastSlashIdx = Math.max(zipFilePath.lastIndexOf('/'), lastPlingIdx); // Get path of parent jarfile, if this is a nested jar. If there is a Class-Path entry in a nested // jar, probably the intent is that the Class-Path path is relative to the root of the parent jar // (although this is an unlikely scenario). - final String parentZipPrefix = lastPlingIdx < 0 ? "" - : logicalZipFilePath.substring(0, lastPlingIdx + 1); + final String parentZipPrefix = lastPlingIdx < 0 ? "" : zipFilePath.substring(0, lastPlingIdx + 1); String parentPathPrefix = lastSlashIdx < 0 ? "" - : logicalZipFilePath.substring(lastPlingIdx + 1, lastSlashIdx + 1); + : zipFilePath.substring(lastPlingIdx + 1, lastSlashIdx + 1); if (parentZipPrefix.isEmpty() && parentPathPrefix.isEmpty()) { parentPathPrefix = FileUtils.CURR_DIR_PATH; } @@ -241,7 +239,7 @@ void open(final WorkQueue workQueue, final LogNode log) // Add paths in an OSGi bundle jar manifest's "Bundle-ClassPath" entry to the classpath, resolving // the paths relative to the root of the jarfile if (logicalZipFile.bundleClassPathManifestEntryValue != null) { - final String zipFilePathPrefix = logicalZipFilePath + "!"; + final String zipFilePathPrefix = zipFilePath + "!"; for (String childClassPathEltPath : logicalZipFile.bundleClassPathManifestEntryValue.split(",")) { // Assume that Bundle-ClassPath paths have to be given relative to jarfile root while (childClassPathEltPath.startsWith("/")) { From 275c2dbb7247493a622ead954085f43d4ad1ce12 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 4 Apr 2019 11:02:44 -0600 Subject: [PATCH 0084/1778] Update comments --- .../io/github/classgraph/ClasspathElementZip.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index 7c1c1684e..12c04a58a 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -196,15 +196,15 @@ void open(final WorkQueue workQueue, final LogNode log) } } - // Create child classpath elements from values obtained from Class-Path entry in manifest + // Create child classpath elements from values obtained from Class-Path entry in manifest, resolving + // the paths relative to the dir or parent jarfile that the jarfile is contained in if (logicalZipFile.classPathManifestEntryValue != null) { - // Class-Path entries in the manifest file are resolved relative to the dir that - // the manifest's jarfile is contained in -- get parent dir of logical zipfile + // Get path of parent dir, or parent jarfile if this is a nested jar. + // (If there is a Class-Path entry in a nested jar, probably the intent is that the Class-Path path + // is relative to the root of the parent jar, although it is unlikely that Class-Path will be used + // this way.) final int lastPlingIdx = zipFilePath.lastIndexOf('!'); final int lastSlashIdx = Math.max(zipFilePath.lastIndexOf('/'), lastPlingIdx); - // Get path of parent jarfile, if this is a nested jar. If there is a Class-Path entry in a nested - // jar, probably the intent is that the Class-Path path is relative to the root of the parent jar - // (although this is an unlikely scenario). final String parentZipPrefix = lastPlingIdx < 0 ? "" : zipFilePath.substring(0, lastPlingIdx + 1); String parentPathPrefix = lastSlashIdx < 0 ? "" : zipFilePath.substring(lastPlingIdx + 1, lastSlashIdx + 1); From 68f3fab5092a6bcfa02d352c19596d0742867316 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 4 Apr 2019 11:04:29 -0600 Subject: [PATCH 0085/1778] Add private constructor --- .../nonapi/io/github/classgraph/utils/CollectionUtils.java | 5 +++++ 1 file changed, 5 insertions(+) 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 a78c6a6d6..d7fc50cde 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/CollectionUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/CollectionUtils.java @@ -37,6 +37,11 @@ * Collection utilities. */ public final class CollectionUtils { + /** Class can't be constructed. */ + private CollectionUtils() { + // Empty + } + /** * Sort a collection if it is not empty (to prevent {@link ConcurrentModificationException} if an immutable * empty list that has been returned more than once is being sorted in one thread and iterated through in From 3fb54a3f363dced7d25876a88acf65fee06114e7 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 4 Apr 2019 12:24:37 -0600 Subject: [PATCH 0086/1778] [maven-release-plugin] prepare release classgraph-4.8.23 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 5ec50632c..0d4eea97a 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.23-SNAPSHOT + 4.8.23 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.23 From 613feaa7dd9b45407b89245ccb3ced9af4d10cfc Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 4 Apr 2019 12:24:44 -0600 Subject: [PATCH 0087/1778] [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 0d4eea97a..3b9c75de2 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.23 + 4.8.24-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.23 + HEAD From 56f4bc8a811aa83c60b89b0ba7ef09af56f4c6c8 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 5 Apr 2019 11:11:27 -0600 Subject: [PATCH 0088/1778] Add .isSynthetic() (#337) --- .../java/io/github/classgraph/MethodInfo.java | 9 +++++++ .../classgraph/MethodParameterInfo.java | 27 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index ec3c0c9a9..dd17eb507 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -300,6 +300,15 @@ 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. * diff --git a/src/main/java/io/github/classgraph/MethodParameterInfo.java b/src/main/java/io/github/classgraph/MethodParameterInfo.java index 3b2bb6744..81271d979 100644 --- a/src/main/java/io/github/classgraph/MethodParameterInfo.java +++ b/src/main/java/io/github/classgraph/MethodParameterInfo.java @@ -239,6 +239,33 @@ protected void setScanResult(final ScanResult scanResult) { } } + /** + * Returns true if this method parameter is final. + * + * @return True if this method parameter is final. + */ + public boolean isFinal() { + return Modifier.isFinal(modifiers); + } + + /** + * Returns true if this method parameter is synthetic. + * + * @return True if this method parameter is synthetic. + */ + public boolean isSynthetic() { + return (modifiers & 0x1000) != 0; + } + + /** + * Returns true if this method parameter is mandated. + * + * @return True if this method parameter is mandated. + */ + public boolean isMandated() { + return (modifiers & 0x8000) != 0; + } + // ------------------------------------------------------------------------------------------------------------- /* (non-Javadoc) From 273e89588b6d2d8990c702b154393183e548e51d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 5 Apr 2019 11:12:56 -0600 Subject: [PATCH 0089/1778] [maven-release-plugin] prepare release classgraph-4.8.24 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 3b9c75de2..5188943aa 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.24-SNAPSHOT + 4.8.24 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.24 From 6fd9c4b004cb888b8b1bef2c9651e1c9ad860829 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 5 Apr 2019 11:13:02 -0600 Subject: [PATCH 0090/1778] [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 5188943aa..f863ca1b3 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.24 + 4.8.25-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.24 + HEAD From 4fefa25d5eb1f2ec488b5908eaf2516769cfd16f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 18 Apr 2019 16:36:07 -0600 Subject: [PATCH 0091/1778] Fix reading of big-endian 64-bit vals (#339) --- .../fastzipfilereader/ZipFileSliceReader.java | 36 +++++----- .../classgraph/utils/CollectionUtils.java | 2 +- .../utils/InputStreamOrByteBufferAdapter.java | 19 ++++-- .../classgraph/issues/issue339/Issue339.java | 66 +++++++++++++++++++ 4 files changed, 96 insertions(+), 27 deletions(-) create mode 100644 src/test/java/io/github/classgraph/issues/issue339/Issue339.java diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSliceReader.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSliceReader.java index 0d40fb42b..cdc56fda9 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSliceReader.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSliceReader.java @@ -242,16 +242,14 @@ static long getLong(final byte[] arr, final long off) throws IOException { if (ioff < 0 || ioff > arr.length - 8) { throw new IndexOutOfBoundsException(); } - return // - (((long) (((arr[ioff + 7] & 0xff) << 24) // - | ((arr[ioff + 6] & 0xff) << 16) // - | ((arr[ioff + 5] & 0xff) << 8) // - | (arr[ioff + 4] & 0xff))) // - << 32) // - | (((arr[ioff + 3] & 0xff) << 24) // - | ((arr[ioff + 2] & 0xff) << 16) // - | ((arr[ioff + 1] & 0xff) << 8) // - | (arr[ioff] & 0xff)); + return ((arr[ioff + 7] & 0xffL) << 56) // + | ((arr[ioff + 6] & 0xffL) << 48) // + | ((arr[ioff + 5] & 0xffL) << 40) // + | ((arr[ioff + 4] & 0xffL) << 32) // + | ((arr[ioff + 3] & 0xffL) << 24) // + | ((arr[ioff + 2] & 0xffL) << 16) // + | ((arr[ioff + 1] & 0xffL) << 8) // + | (arr[ioff] & 0xffL); } /** @@ -272,16 +270,14 @@ long getLong(final long off) throws IOException, InterruptedException { if (read(off, scratch, 0, 8) < 8) { throw new EOFException("Unexpected EOF"); } - return // - (((long) (((scratch[7] & 0xff) << 24) // - | ((scratch[6] & 0xff) << 16) // - | ((scratch[5] & 0xff) << 8) // - | (scratch[4] & 0xff))) // - << 32) // - | (((scratch[3] & 0xff) << 24) // - | ((scratch[2] & 0xff) << 16) // - | ((scratch[1] & 0xff) << 8) // - | (scratch[0] & 0xff)); + return ((scratch[7] & 0xffL) << 56) // + | ((scratch[6] & 0xffL) << 48) // + | ((scratch[5] & 0xffL) << 40) // + | ((scratch[4] & 0xffL) << 32) // + | ((scratch[3] & 0xffL) << 24) // + | ((scratch[2] & 0xffL) << 16) // + | ((scratch[1] & 0xffL) << 8) // + | (scratch[0] & 0xffL); } /** 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 d7fc50cde..68b896957 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/CollectionUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/CollectionUtils.java @@ -41,7 +41,7 @@ public final class CollectionUtils { private CollectionUtils() { // Empty } - + /** * Sort a collection if it is not empty (to prevent {@link ConcurrentModificationException} if an immutable * empty list that has been returned more than once is being sorted in one thread and iterated through in diff --git a/src/main/java/nonapi/io/github/classgraph/utils/InputStreamOrByteBufferAdapter.java b/src/main/java/nonapi/io/github/classgraph/utils/InputStreamOrByteBufferAdapter.java index 048affd4b..d63962ec4 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/InputStreamOrByteBufferAdapter.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/InputStreamOrByteBufferAdapter.java @@ -244,7 +244,8 @@ public int readUnsignedShort(final int offset) throws IOException { if (bytesToRead > 0) { readMore(bytesToRead); } - return ((buf[offset] & 0xff) << 8) | (buf[offset + 1] & 0xff); + return ((buf[offset] & 0xff) << 8) // + | (buf[offset + 1] & 0xff); } /** @@ -274,7 +275,9 @@ public int readInt(final int offset) throws IOException { if (bytesToRead > 0) { readMore(bytesToRead); } - return ((buf[offset] & 0xff) << 24) | ((buf[offset + 1] & 0xff) << 16) | ((buf[offset + 2] & 0xff) << 8) + return ((buf[offset] & 0xff) << 24) // + | ((buf[offset + 1] & 0xff) << 16) // + | ((buf[offset + 2] & 0xff) << 8) // | (buf[offset + 3] & 0xff); } @@ -305,10 +308,14 @@ public long readLong(final int offset) throws IOException { if (bytesToRead > 0) { readMore(bytesToRead); } - return (((long) (((buf[offset] & 0xff) << 24) | ((buf[offset + 1] & 0xff) << 16) - | ((buf[offset + 2] & 0xff) << 8) | (buf[offset + 3] & 0xff))) << 32) - | ((buf[offset + 4] & 0xff) << 24) | ((buf[offset + 5] & 0xff) << 16) - | ((buf[offset + 6] & 0xff) << 8) | (buf[offset + 7] & 0xff); + return ((buf[offset] & 0xffL) << 56) // + | ((buf[offset + 1] & 0xffL) << 48) // + | ((buf[offset + 2] & 0xffL) << 40) // + | ((buf[offset + 3] & 0xffL) << 32) // + | ((buf[offset + 4] & 0xffL) << 24) // + | ((buf[offset + 5] & 0xffL) << 16) // + | ((buf[offset + 6] & 0xffL) << 8) // + | (buf[offset + 7] & 0xffL); } /** diff --git a/src/test/java/io/github/classgraph/issues/issue339/Issue339.java b/src/test/java/io/github/classgraph/issues/issue339/Issue339.java new file mode 100644 index 000000000..df21843ce --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue339/Issue339.java @@ -0,0 +1,66 @@ +package io.github.classgraph.issues.issue339; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.junit.Test; + +import io.github.classgraph.AnnotationParameterValueList; +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ClassInfo; +import io.github.classgraph.ScanResult; + +/** + * Unit test. + */ +public class Issue339 { + /** + * Grade. + */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.METHOD) + @Documented + //@Repeatable(Grades.class) + public @interface Grade { + /** + * Points. + * + * @return the double + */ + double points(); + + /** + * Max points. + * + * @return the double + */ + double maxPoints() default 0.0; + } + + /** The Class Cls. */ + public class Cls { + /** Method with annotation. */ + @Grade(points = 0.4, maxPoints = 0.4) + public void method() { + } + } + + /** Test. */ + @Test + public void test() { + try (ScanResult scanResult = new ClassGraph().enableAllInfo().enableExternalClasses() + .whitelistClasses(Cls.class.getName()).scan()) { + final ClassInfo classInfo = scanResult.getClassInfo(Cls.class.getName()); + final AnnotationParameterValueList annotationParamVals = classInfo.getMethodInfo("method").get(0) + .getAnnotationInfo().get(0).getParameterValues(); + assertThat(Math.abs((Double) annotationParamVals.get("points").getValue() - 0.4)).isLessThan(1.0e-12); + assertThat(Math.abs((Double) annotationParamVals.get("maxPoints").getValue() - 0.4)) + .isLessThan(1.0e-12); + } + } +} From dfbadf5352675b35847a903d97f7cd3b7f21f69f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 18 Apr 2019 16:36:53 -0600 Subject: [PATCH 0092/1778] [maven-release-plugin] prepare release classgraph-4.8.25 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index f863ca1b3..dc8f7ac6f 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.25-SNAPSHOT + 4.8.25 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.25 From 7c6678572936b462d4438b234bca4a4d4d2ee0b6 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 18 Apr 2019 16:36:58 -0600 Subject: [PATCH 0093/1778] [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 dc8f7ac6f..b3640803f 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.25 + 4.8.26-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.25 + HEAD From 6b5a4c49ccb1f8bf4c86496242eb2ae8e402dfed Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 24 Apr 2019 18:58:53 -0600 Subject: [PATCH 0094/1778] Custom-intern all classfile constant pool strings to save memory (#338) --- .../java/io/github/classgraph/Classfile.java | 51 ++++++++++++++----- .../java/io/github/classgraph/Scanner.java | 13 +++-- 2 files changed, 49 insertions(+), 15 deletions(-) diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index caf2ef732..352c137cc 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -37,6 +37,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import io.github.classgraph.Scanner.ClassfileScanWorkUnit; import nonapi.io.github.classgraph.ScanSpec; @@ -69,6 +70,9 @@ class Classfile { /** The classfile resource. */ private final Resource classfileResource; + /** The string intern map. */ + private final ConcurrentHashMap stringInternMap; + /** The name of the class. */ private String className; @@ -436,6 +440,24 @@ void link(final Map classNameToClassInfo, // ------------------------------------------------------------------------------------------------------------- + /** + * Intern a string. + */ + private String intern(final String str) { + if (str == null) { + return null; + } + final String interned = stringInternMap.get(str); + if (interned != null) { + return interned; + } + final String interned1 = stringInternMap.putIfAbsent(str, str); + if (interned1 != null) { + return interned1; + } + return str; + } + /** * Get the byte offset within the buffer of a string from the constant pool, or 0 for a null string. * @@ -530,8 +552,8 @@ private String getConstantPoolString(final int cpIdx, final boolean replaceSlash final boolean stripLSemicolon) throws ClassfileFormatException, IOException { final int constantPoolStringOffset = getConstantPoolStringOffset(cpIdx, /* subFieldIdx = */ 0); return constantPoolStringOffset == 0 ? null - : inputStreamOrByteBuffer.readString(constantPoolStringOffset, replaceSlashWithDot, - stripLSemicolon); + : intern(inputStreamOrByteBuffer.readString(constantPoolStringOffset, replaceSlashWithDot, + stripLSemicolon)); } /** @@ -552,8 +574,8 @@ private String getConstantPoolString(final int cpIdx, final int subFieldIdx) throws ClassfileFormatException, IOException { final int constantPoolStringOffset = getConstantPoolStringOffset(cpIdx, subFieldIdx); return constantPoolStringOffset == 0 ? null - : inputStreamOrByteBuffer.readString(constantPoolStringOffset, /* replaceSlashWithDot = */ false, - /* stripLSemicolon = */ false); + : intern(inputStreamOrByteBuffer.readString(constantPoolStringOffset, + /* replaceSlashWithDot = */ false, /* stripLSemicolon = */ false)); } /** @@ -627,34 +649,35 @@ private String getConstantPoolClassDescriptor(final int cpIdx) throws ClassfileF } /** - * Compare a string in the constant pool with a given constant, without constructing the String object. + * Compare a string in the constant pool with a given ASCII string, without constructing the constant pool + * String object. * * @param cpIdx * the constant pool index - * @param otherString - * the other string + * @param asciiString + * the ASCII string to compare to * @return true, if successful * @throws ClassfileFormatException * If a problem occurs. * @throws IOException * If an IO exception occurs. */ - private boolean constantPoolStringEquals(final int cpIdx, final String otherString) + private boolean constantPoolStringEquals(final int cpIdx, final String asciiString) throws ClassfileFormatException, IOException { final int strOffset = getConstantPoolStringOffset(cpIdx, /* subFieldIdx = */ 0); if (strOffset == 0) { - return otherString == null; - } else if (otherString == null) { + return asciiString == null; + } else if (asciiString == null) { return false; } final int strLen = inputStreamOrByteBuffer.readUnsignedShort(strOffset); - final int otherLen = otherString.length(); + final int otherLen = asciiString.length(); if (strLen != otherLen) { return false; } final int strStart = strOffset + 2; for (int i = 0; i < strLen; i++) { - if ((char) (inputStreamOrByteBuffer.buf[strStart + i] & 0xff) != otherString.charAt(i)) { + if ((char) (inputStreamOrByteBuffer.buf[strStart + i] & 0xff) != asciiString.charAt(i)) { return false; } } @@ -1432,6 +1455,8 @@ private void readClassAttributes() throws IOException, ClassfileFormatException * the classfile resource * @param isExternalClass * if this is an external class + * @param stringInternMap + * the string intern map * @param workQueue * the work queue * @param scanSpec @@ -1449,6 +1474,7 @@ private void readClassAttributes() throws IOException, ClassfileFormatException Classfile(final ClasspathElement classpathElement, final List classpathOrder, final Set classNamesScheduledForScanning, final String relativePath, final Resource classfileResource, final boolean isExternalClass, + final ConcurrentHashMap stringInternMap, final WorkQueue workQueue, final ScanSpec scanSpec, final LogNode log) throws IOException, ClassfileFormatException, SkipClassException { this.classpathElement = classpathElement; @@ -1457,6 +1483,7 @@ private void readClassAttributes() throws IOException, ClassfileFormatException this.classNamesScheduledForScanning = classNamesScheduledForScanning; this.classfileResource = classfileResource; this.isExternalClass = isExternalClass; + this.stringInternMap = stringInternMap; this.scanSpec = scanSpec; this.log = log; diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 88e5ce981..017adddef 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -557,6 +557,9 @@ private static class ClassfileScannerWorkUnitProcessor implements WorkUnitProces /** The valid {@link Classfile} objects created by scanning classfiles. */ private final Queue scannedClassfiles; + /** The string intern map. */ + private final ConcurrentHashMap stringInternMap; + /** * Constructor. * @@ -571,11 +574,12 @@ private static class ClassfileScannerWorkUnitProcessor implements WorkUnitProces */ public ClassfileScannerWorkUnitProcessor(final ScanSpec scanSpec, final List classpathOrder, final Set classNamesScheduledForScanning, - final Queue scannedClassfiles) { + final Queue scannedClassfiles, final ConcurrentHashMap stringInternMap) { this.scanSpec = scanSpec; this.classpathOrder = classpathOrder; this.classNamesScheduledForScanning = classNamesScheduledForScanning; this.scannedClassfiles = scannedClassfiles; + this.stringInternMap = stringInternMap; } /* (non-Javadoc) @@ -596,7 +600,8 @@ public void processWorkUnit(final ClassfileScanWorkUnit workUnit, // Parse classfile binary format, creating a Classfile object final Classfile classfile = new Classfile(workUnit.classpathElement, classpathOrder, classNamesScheduledForScanning, workUnit.classfileResource.getPath(), - workUnit.classfileResource, workUnit.isExternalClass, workQueue, scanSpec, subLog); + workUnit.classfileResource, workUnit.isExternalClass, stringInternMap, workQueue, scanSpec, + subLog); // Enqueue the classfile for linking scannedClassfiles.add(classfile); @@ -823,10 +828,12 @@ private ScanResult performScan(final List finalClasspathEltOrd // Scan classfiles in parallel. final Queue scannedClassfiles = new ConcurrentLinkedQueue<>(); + final ConcurrentHashMap stringInternMap = new ConcurrentHashMap<>(); processWorkUnits(classfileScanWorkItems, topLevelLog == null ? null : topLevelLog.log("Scanning classfiles"), new ClassfileScannerWorkUnitProcessor(scanSpec, finalClasspathEltOrder, - classNamesScheduledForScanning, scannedClassfiles)); + classNamesScheduledForScanning, scannedClassfiles, stringInternMap)); + stringInternMap.clear(); // Link the Classfile objects to produce ClassInfo objects. This needs to be done from a single thread. final LogNode linkLog = topLevelLog == null ? null : topLevelLog.log("Linking related classfiles"); From 493ae2314ef011ccd33d22ebf0f95500fbb22dfa Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 24 Apr 2019 23:00:22 -0600 Subject: [PATCH 0095/1778] Possible fix for '..' Class-Path entry segments being dropped (#340) --- .../classgraph/ClasspathElementZip.java | 85 ++++++++++--------- .../classgraph/classpath/SystemJarFinder.java | 7 +- .../fastzipfilereader/ZipFileSlice.java | 58 +++++++------ .../classgraph/utils/FastPathResolver.java | 14 ++- .../io/github/classgraph/utils/FileUtils.java | 17 ++++ .../classgraph/issues/issue340/Issue340.java | 24 ++++++ 6 files changed, 131 insertions(+), 74 deletions(-) create mode 100644 src/test/java/io/github/classgraph/issues/issue340/Issue340.java diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index 12c04a58a..bbce3184f 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -49,6 +49,7 @@ import nonapi.io.github.classgraph.fastzipfilereader.FastZipEntry; import nonapi.io.github.classgraph.fastzipfilereader.LogicalZipFile; import nonapi.io.github.classgraph.fastzipfilereader.NestedJarHandler; +import nonapi.io.github.classgraph.fastzipfilereader.ZipFileSlice; import nonapi.io.github.classgraph.utils.FastPathResolver; import nonapi.io.github.classgraph.utils.FileUtils; import nonapi.io.github.classgraph.utils.InputStreamOrByteBufferAdapter; @@ -196,42 +197,42 @@ void open(final WorkQueue workQueue, final LogNode log) } } + // Don't add child classpath elements that are identical to this classpath element, or that are duplicates + final Set scheduledChildClasspathElements = new HashSet<>(); + scheduledChildClasspathElements.add(rawPath); + // Create child classpath elements from values obtained from Class-Path entry in manifest, resolving // the paths relative to the dir or parent jarfile that the jarfile is contained in if (logicalZipFile.classPathManifestEntryValue != null) { - // Get path of parent dir, or parent jarfile if this is a nested jar. - // (If there is a Class-Path entry in a nested jar, probably the intent is that the Class-Path path - // is relative to the root of the parent jar, although it is unlikely that Class-Path will be used - // this way.) - final int lastPlingIdx = zipFilePath.lastIndexOf('!'); - final int lastSlashIdx = Math.max(zipFilePath.lastIndexOf('/'), lastPlingIdx); - final String parentZipPrefix = lastPlingIdx < 0 ? "" : zipFilePath.substring(0, lastPlingIdx + 1); - String parentPathPrefix = lastSlashIdx < 0 ? "" - : zipFilePath.substring(lastPlingIdx + 1, lastSlashIdx + 1); - if (parentZipPrefix.isEmpty() && parentPathPrefix.isEmpty()) { - parentPathPrefix = FileUtils.CURR_DIR_PATH; - } + // Get parent dir of logical zipfile within grandparent slice, + // e.g. for a zipfile slice path of "/path/to/jar1.jar!/lib/jar2.jar", this is "lib", + // or for "/path/to/jar1.jar", this is "/path/to", or "" if the jar is in the toplevel dir. + final String jarParentDir = FileUtils + .getParentDirPath(logicalZipFile.getPathWithinParentZipFileSlice()); // Add paths in manifest file's "Class-Path" entry to the classpath, resolving paths relative to // the parent directory or jar - for (final String childClassPathEltPath : logicalZipFile.classPathManifestEntryValue.split(" ")) { - if (!childClassPathEltPath.isEmpty()) { + for (final String childClassPathEltPathRelative : logicalZipFile.classPathManifestEntryValue + .split(" ")) { + if (!childClassPathEltPathRelative.isEmpty()) { // Resolve Class-Path entry relative to containing dir - String childClassPathEltPathResolved = FastPathResolver.resolve(parentPathPrefix, - childClassPathEltPath); - if (!parentZipPrefix.isEmpty()) { - childClassPathEltPathResolved = parentZipPrefix - + (childClassPathEltPathResolved.startsWith("/") ? childClassPathEltPathResolved - : "/" + childClassPathEltPathResolved); + String childClassPathEltPath = FastPathResolver.resolve(jarParentDir, + childClassPathEltPathRelative); + // If this is a nested jar, prepend outer jar prefix + final ZipFileSlice parentZipFileSlice = logicalZipFile.getParentZipFileSlice(); + if (parentZipFileSlice != null) { + childClassPathEltPath = parentZipFileSlice.getPath() + + (childClassPathEltPath.startsWith("/") ? "!" : "!/") + childClassPathEltPath; } // Only add child classpath elements once - if (!childClassPathEltPathResolved.equals(rawPath)) { + if (scheduledChildClasspathElements.add(childClassPathEltPath)) { // Schedule child classpath element for scanning - workQueue.addWorkUnit(new ClasspathEntryWorkUnit( - /* rawClasspathEntry = */ new SimpleEntry<>(childClassPathEltPathResolved, - classLoader), - /* parentClasspathElement = */ this, - /* orderWithinParentClasspathElement = */ - childClasspathEntryIdx++)); + workQueue.addWorkUnit( // + new ClasspathEntryWorkUnit( + /* rawClasspathEntry = */ new SimpleEntry<>(childClassPathEltPath, + classLoader), + /* parentClasspathElement = */ this, + /* orderWithinParentClasspathElement = */ + childClasspathEntryIdx++)); } } } @@ -239,26 +240,30 @@ void open(final WorkQueue workQueue, final LogNode log) // Add paths in an OSGi bundle jar manifest's "Bundle-ClassPath" entry to the classpath, resolving // the paths relative to the root of the jarfile if (logicalZipFile.bundleClassPathManifestEntryValue != null) { - final String zipFilePathPrefix = zipFilePath + "!"; - for (String childClassPathEltPath : logicalZipFile.bundleClassPathManifestEntryValue.split(",")) { + final String zipFilePathPrefix = zipFilePath + "!/"; + for (String childBundlePath : logicalZipFile.bundleClassPathManifestEntryValue.split(",")) { // Assume that Bundle-ClassPath paths have to be given relative to jarfile root - while (childClassPathEltPath.startsWith("/")) { - childClassPathEltPath = childClassPathEltPath.substring(1); + while (childBundlePath.startsWith("/")) { + childBundlePath = childBundlePath.substring(1); } // Currently the position of "." relative to child classpath entries is ignored (the // Bundle-ClassPath path is treated as if "." is in the first position, since child // classpath entries are always added to the classpath after the parent classpath // entry that they were obtained from). - if (!childClassPathEltPath.isEmpty() && !childClassPathEltPath.equals(".")) { + if (!childBundlePath.isEmpty() && !childBundlePath.equals(".")) { // Resolve Bundle-ClassPath entry within jar - final String childClassPathEltPathResolved = FastPathResolver.resolve(zipFilePathPrefix, - childClassPathEltPath); - workQueue.addWorkUnit(new ClasspathEntryWorkUnit( - /* rawClasspathEntry = */ // - new SimpleEntry<>(childClassPathEltPathResolved, classLoader), - /* parentClasspathElement = */ this, - /* orderWithinParentClasspathElement = */ - childClasspathEntryIdx++)); + final String childClassPathEltPath = zipFilePathPrefix + + FileUtils.sanitizeEntryPath(childBundlePath, /* removeInitialSlash = */ true); + // Only add child classpath elements once + if (scheduledChildClasspathElements.add(childClassPathEltPath)) { + // Schedule child classpath element for scanning + workQueue.addWorkUnit(new ClasspathEntryWorkUnit( + /* rawClasspathEntry = */ // + new SimpleEntry<>(childClassPathEltPath, classLoader), + /* parentClasspathElement = */ this, + /* orderWithinParentClasspathElement = */ + childClasspathEntryIdx++)); + } } } } 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 4aa89f4bd..206737d94 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/SystemJarFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/SystemJarFinder.java @@ -43,6 +43,9 @@ public final class SystemJarFinder { /** The paths of any "rt.jar" files found in the JRE. */ private static final Set RT_JARS = new LinkedHashSet<>(); + /** The path of the first "rt.jar" found. */ + private static final String RT_JAR; + /** The paths of any "lib/" or "ext/" jars found in the JRE. */ private static final Set JRE_LIB_OR_EXT_JARS = new LinkedHashSet<>(); @@ -163,6 +166,8 @@ private static boolean addJREPath(final File dir) { default: break; } + + RT_JAR = RT_JARS.isEmpty() ? null : FastPathResolver.resolve(RT_JARS.iterator().next()); } /** @@ -172,7 +177,7 @@ private static boolean addJREPath(final File dir) { */ public static String getJreRtJarPath() { // Only include the first rt.jar -- if there is a copy in both the JDK and JRE, no need to scan both - return !RT_JARS.isEmpty() ? FastPathResolver.resolve(RT_JARS.iterator().next()) : null; + return RT_JAR; } /** diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSlice.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSlice.java index ba0a4ddac..45ce9f990 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSlice.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSlice.java @@ -35,7 +35,7 @@ import nonapi.io.github.classgraph.recycler.Recycler; /** A zipfile slice (a sub-range of bytes within a PhysicalZipFile. */ -class ZipFileSlice { +public class ZipFileSlice { /** The parent slice, or null if this is the toplevel slice (the whole zipfile). */ private final ZipFileSlice parentZipFileSlice; /** The underlying physical zipfile. */ @@ -44,8 +44,8 @@ class ZipFileSlice { final long startOffsetWithinPhysicalZipFile; /** The compressed or stored size of the zipfile slice or entry. */ final long len; - /** For the toplevel zipfile slice, the zipfile path; For nested slices, the name of the zipfile entry. */ - private final String name; + /** For the toplevel zipfile slice, the zipfile path; For nested slices, the name/path of the zipfile entry. */ + private final String pathWithinParentZipFileSlice; /** A {@link Recycler} for {@link ZipFileSliceReader} instances. */ final Recycler zipFileSliceReaderRecycler; // N.B. if any fields are added, make sure the clone constructor below is updated @@ -78,7 +78,7 @@ public ZipFileSliceReader newInstance() throws RuntimeException { this.physicalZipFile = physicalZipFile; this.startOffsetWithinPhysicalZipFile = 0; this.len = physicalZipFile.fileLen; - this.name = physicalZipFile.getPath(); + this.pathWithinParentZipFileSlice = physicalZipFile.getPath(); this.zipFileSliceReaderRecycler = newZipFileSliceReaderRecycler(); } @@ -95,7 +95,7 @@ public ZipFileSliceReader newInstance() throws RuntimeException { this.physicalZipFile = physicalZipFileInRam; this.startOffsetWithinPhysicalZipFile = 0; this.len = physicalZipFile.fileLen; - this.name = zipEntry.entryName; + this.pathWithinParentZipFileSlice = zipEntry.entryName; this.zipFileSliceReaderRecycler = newZipFileSliceReaderRecycler(); } @@ -114,7 +114,7 @@ public ZipFileSliceReader newInstance() throws RuntimeException { this.physicalZipFile = zipEntry.parentLogicalZipFile.physicalZipFile; this.startOffsetWithinPhysicalZipFile = zipEntry.getEntryDataStartOffsetWithinPhysicalZipFile(); this.len = zipEntry.compressedSize; - this.name = zipEntry.entryName; + this.pathWithinParentZipFileSlice = zipEntry.entryName; this.zipFileSliceReaderRecycler = newZipFileSliceReaderRecycler(); } @@ -129,7 +129,7 @@ public ZipFileSliceReader newInstance() throws RuntimeException { this.physicalZipFile = other.physicalZipFile; this.startOffsetWithinPhysicalZipFile = other.startOffsetWithinPhysicalZipFile; this.len = other.len; - this.name = other.name; + this.pathWithinParentZipFileSlice = other.pathWithinParentZipFileSlice; // Reuse the recycler for clones this.zipFileSliceReaderRecycler = other.zipFileSliceReaderRecycler; } @@ -144,11 +144,31 @@ public ZipFileSliceReader newInstance() throws RuntimeException { * jarfile white/blacklist. */ public boolean isWhitelistedAndNotBlacklisted(final WhiteBlackListLeafname jarWhiteBlackList) { - return jarWhiteBlackList.isWhitelistedAndNotBlacklisted(name) // + return jarWhiteBlackList.isWhitelistedAndNotBlacklisted(pathWithinParentZipFileSlice) // && (parentZipFileSlice == null || parentZipFileSlice.isWhitelistedAndNotBlacklisted(jarWhiteBlackList)); } + /** + * Get the parent ZipFileslice, or return null if this is a toplevel slice (i.e. if this slice wraps an entire + * physical zipfile). + * + * @return the parent ZipFileslice, or null if this is a toplevel slice. + */ + public ZipFileSlice getParentZipFileSlice() { + return parentZipFileSlice; + } + + /** + * Get the name of the slice (either the entry name/path within the parent zipfile slice, or the path of the + * physical zipfile if this slice is a toplevel slice (i.e. if this slice wraps an entire physical zipfile). + * + * @return the name of the slice. + */ + public String getPathWithinParentZipFileSlice() { + return pathWithinParentZipFileSlice; + } + /** * Recursively append the path in top down ancestral order. * @@ -158,15 +178,15 @@ public boolean isWhitelistedAndNotBlacklisted(final WhiteBlackListLeafname jarWh private void appendPath(final StringBuilder buf) { if (parentZipFileSlice != null) { parentZipFileSlice.appendPath(buf); + if (buf.length() > 0) { + buf.append("!/"); + } } - if (buf.length() > 0) { - buf.append("!/"); - } - buf.append(name); + buf.append(pathWithinParentZipFileSlice); } /** - * Get the path of this zipfile slice, e.g. "/path/to/jarfile.jar!/nestedjar1.jar!/nestedfile". + * Get the path of this zipfile slice, e.g. "/path/to/jarfile.jar!/nestedjar1.jar". * * @return the path of this zipfile slice. */ @@ -176,18 +196,6 @@ public String getPath() { return buf.toString(); } - /** - * Get the path of the parent of this zipfile slice. If this is a toplevel slice (i.e. if this slice corresponds - * to a whole physical zipfile), then the returned path is the directory of the containing dir. - * - * @return the path of this zipfile slice. - */ - public String getParentPath() { - final StringBuilder buf = new StringBuilder(); - appendPath(buf); - return buf.toString(); - } - /** * Get the physical {@link File} that this ZipFileSlice is a slice of. * 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 6c2c5fb1d..483841e30 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FastPathResolver.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FastPathResolver.java @@ -281,19 +281,17 @@ public static String resolve(final String resolveBasePath, final String relative } } - final String resolveBasePathSanitized = isAbsolutePath || resolveBasePath == null - || resolveBasePath.isEmpty() ? null - : FileUtils.sanitizeEntryPath(resolveBasePath, /* removeInitialSlash = */ false); - final String pathStrSanitized = FileUtils.sanitizeEntryPath(pathStr, /* removeInitialSlash = */ false); + // Sanitize path (resolve ".." sections, collapse "//" double separators, etc.) String pathResolved; - if (resolveBasePathSanitized == null || resolveBasePathSanitized.isEmpty()) { + if (isAbsolutePath || resolveBasePath == null || resolveBasePath.isEmpty()) { // There is no base path to resolve against, or path is an absolute path or http(s):// URL // (ignore the base path) - pathResolved = pathStrSanitized; + pathResolved = FileUtils.sanitizeEntryPath(pathStr, /* removeInitialSlash = */ false); } else { // Path is a relative path -- resolve it relative to the base path - pathResolved = resolveBasePath - + (resolveBasePath.endsWith("/") || pathStrSanitized.isEmpty() ? "" : "/") + pathStrSanitized; + pathResolved = FileUtils.sanitizeEntryPath( + resolveBasePath + (resolveBasePath.endsWith("/") ? "" : "/") + pathStr, + /* removeInitialSlash = */ false); } // Add any prefix back, e.g. "https://" 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 0c9215d33..b1e30f81d 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -419,6 +419,23 @@ public static boolean canRead(final File file) { // ------------------------------------------------------------------------------------------------------------- + /** + * Get the parent dir path. + * + * @param path + * the path + * @return the parent dir path + */ + public static String getParentDirPath(final String path) { + final int lastSlashIdx = path.lastIndexOf('/'); + if (lastSlashIdx == 0) { + return ""; + } + return path.substring(0, lastSlashIdx); + } + + // ------------------------------------------------------------------------------------------------------------- + /** * Get the clean() method, attachment() method, and theUnsafe field, called inside doPrivileged. */ diff --git a/src/test/java/io/github/classgraph/issues/issue340/Issue340.java b/src/test/java/io/github/classgraph/issues/issue340/Issue340.java new file mode 100644 index 000000000..7eaaf0118 --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue340/Issue340.java @@ -0,0 +1,24 @@ +package io.github.classgraph.issues.issue340; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Test; + +import nonapi.io.github.classgraph.utils.FastPathResolver; + +/** + * Unit test. + */ +public class Issue340 { + /** Test. */ + @Test + public void test() { + assertThat(FastPathResolver.resolve("", "../../x")).isEqualTo("x"); + assertThat(FastPathResolver.resolve("/", "../../x")).isEqualTo("/x"); + assertThat(FastPathResolver.resolve("/x", "y")).isEqualTo("/x/y"); + assertThat(FastPathResolver.resolve("/x", "../y")).isEqualTo("/y"); + assertThat(FastPathResolver.resolve("/x", "../../y")).isEqualTo("/y"); + assertThat(FastPathResolver.resolve("/x/y/z", "..//..////w")).isEqualTo("/x/w"); + assertThat(FastPathResolver.resolve("/x/y/z", "//p//q")).isEqualTo("/p/q"); + } +} From 3f9db8c649665395bde7f37608f6632e338f1e07 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 25 Apr 2019 12:43:53 -0600 Subject: [PATCH 0096/1778] Don't add "jar:" if it is already prepended to resource URI --- src/main/java/io/github/classgraph/Resource.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/Resource.java b/src/main/java/io/github/classgraph/Resource.java index 861fcb408..a38b48b0d 100644 --- a/src/main/java/io/github/classgraph/Resource.java +++ b/src/main/java/io/github/classgraph/Resource.java @@ -341,7 +341,8 @@ public URI getURI() { // Check if this is a directory-based module (location URI will end in "/") final boolean isDir = locationURIStr.endsWith("/"); try { - return new URI((isDir ? "" : "jar:") + locationURIStr + (isDir ? "" : "!/") + resourcePath); + return new URI((isDir || locationURIStr.startsWith("jar:") ? "" : "jar:") + locationURIStr + + (isDir ? "" : "!/") + resourcePath); } catch (final URISyntaxException e) { throw new IllegalArgumentException("Could not form URL for classpath element: " + locationURIStr + " ; path: " + resourcePath + " : " + e); From e91208c7b8f8b74687eca9795432ae375e5b0c58 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 25 Apr 2019 12:49:13 -0600 Subject: [PATCH 0097/1778] More fixes for '..' path segments in Class-Path (#340) --- .../github/classgraph/ClasspathElementZip.java | 1 + .../classgraph/issues/issue340/Issue340.java | 17 +++++++++++++++++ src/test/resources/issue340.jar | Bin 0 -> 2483 bytes 3 files changed, 18 insertions(+) create mode 100644 src/test/resources/issue340.jar diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index bbce3184f..2d8e9929e 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -241,6 +241,7 @@ void open(final WorkQueue workQueue, final LogNode log) // the paths relative to the root of the jarfile if (logicalZipFile.bundleClassPathManifestEntryValue != null) { final String zipFilePathPrefix = zipFilePath + "!/"; + // Class-Path is split on " ", but Bundle-ClassPath is split on "," for (String childBundlePath : logicalZipFile.bundleClassPathManifestEntryValue.split(",")) { // Assume that Bundle-ClassPath paths have to be given relative to jarfile root while (childBundlePath.startsWith("/")) { diff --git a/src/test/java/io/github/classgraph/issues/issue340/Issue340.java b/src/test/java/io/github/classgraph/issues/issue340/Issue340.java index 7eaaf0118..710cdf996 100644 --- a/src/test/java/io/github/classgraph/issues/issue340/Issue340.java +++ b/src/test/java/io/github/classgraph/issues/issue340/Issue340.java @@ -2,8 +2,13 @@ import static org.assertj.core.api.Assertions.assertThat; +import java.util.stream.Collectors; + import org.junit.Test; +import io.github.classgraph.ClassGraph; +import io.github.classgraph.Resource; +import io.github.classgraph.ScanResult; import nonapi.io.github.classgraph.utils.FastPathResolver; /** @@ -13,6 +18,7 @@ public class Issue340 { /** Test. */ @Test public void test() { + // Test path resolution assertThat(FastPathResolver.resolve("", "../../x")).isEqualTo("x"); assertThat(FastPathResolver.resolve("/", "../../x")).isEqualTo("/x"); assertThat(FastPathResolver.resolve("/x", "y")).isEqualTo("/x/y"); @@ -20,5 +26,16 @@ public void test() { assertThat(FastPathResolver.resolve("/x", "../../y")).isEqualTo("/y"); assertThat(FastPathResolver.resolve("/x/y/z", "..//..////w")).isEqualTo("/x/w"); assertThat(FastPathResolver.resolve("/x/y/z", "//p//q")).isEqualTo("/p/q"); + + try (ScanResult scanResult = new ClassGraph() + .overrideClasspath(getClass().getClassLoader().getResource("issue340.jar").getPath()).scan()) { + // issue340.jar contains Bundle-ClassPath that points to jar2 and jar4. + // jar2 has a Class-Path entry that points to jar1; jar4 has a Class-Path entry that points to jar3. + // jar2 and jar4 also have an invalid Class-Path entry that tries to escape the parent jar root. + // jar1 and jar2 are deflated, jar3 and jar4 are stored. + assertThat(scanResult.getAllResources().stream().map(Resource::getPath) + .filter(path -> path.startsWith("file")).collect(Collectors.toList())) + .containsExactlyInAnyOrder("file1", "file2", "file3", "file4"); + } } } diff --git a/src/test/resources/issue340.jar b/src/test/resources/issue340.jar new file mode 100644 index 0000000000000000000000000000000000000000..dbabb00bc6559a86204c142daa317d7d653964d6 GIT binary patch literal 2483 zcmai$Ye-XJ7{}k^*-V|rEDb3srKF2C-6a-6=`?L-&Qkl(ux!p&D+}^gc?qXovbGtvR`$UzA1X6?o^y7#qwjHUyvGgp{?7mTzt8zU?nq~4 z0R&%#1x^$9!FBN`;Ups^$FM%llp4D$$Dgnh(_Oh$|GIKZPbdf@`@|B$U3LH>g!m9Y z4zXlGbIV#ls9=SN5g7(kT53vmjw&P7P4lH`#p`<0d(wtVU!}#U78+xE+Omv2F`9wC zZE#^1#V6kIuRGxL@fbt!w!HEU`7@Qv;kE_#A~mnN`}aJm`z%gV)X~H1vOsYwtRP|g zAxn{31s6`PFJPYkrHWTf+H4>4iDOT_b~@}5iLMPyHrq`1EeHKpvp=R+EU+d%YOlHG zxI8A;vg}pANn1gqwR?ZH?D~^C!CR6pTF-=?vxG!1GX}3zmb3PXwp3AFdg;0}d|f^B z)fENX&-dOw@&szKGw!-Hu~3r6UDsW&burG{&QpQL>Sn{)$o1OQLu+)Wn;a+q^wp{( za?M@6oiD!EI33J(8F_etgciqDW?uh1KAQd582myj%MWfX%NtQ{-IJN1ugcq+kf|Se zuiTWUZ?hW`n*!o8qBoDmT*;YkM?9|OS>W-qf3S*pNZQPZxFU$ignl-l9 ze`7`Z%DU4XH$p#_2e#BUJR2*k+u5-1)>p@>vYTy5l{FR(IH+H&RLKV>I&I?CD6vYm zhL*Q59L}?zZb46H6~p%3<|s3^1uj=>XwgEg+rp)Q>8D%6gXw?{gWc{IHmJ>BxL?b4 z*1KO_xwxpuit$;_@*PeC!rZWS2Yz(nK*$#A(`(xFQ_w@cacxfCwZ?pT(NbEyUT6c^=^kOFnHilFl=0TiGL2?U>C zh)8*r^)wRPQN!|@Ur56>i&l)mH5|kxSO4H(CWO~8hSxB=D1s?btH5^8xzZ&|ohujs z=PFse2St}Jpp<0VfF~{rbHGVR=atQ61&@Q{CGCzHF>Xe%&qd}NkR>en346c6!Upun zr>BotKK^>9e4ItIoEOVS+{{TNmY*!f&6YnWd9Gn27TN)1*XRK!aP)W)Ne>q;B;3D}OY#3iuspW_ literal 0 HcmV?d00001 From b08a069d1cb0545ed8985cdbb27ccd7000927278 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 25 Apr 2019 12:49:51 -0600 Subject: [PATCH 0098/1778] [maven-release-plugin] prepare release classgraph-4.8.26 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index b3640803f..a9ecc33ae 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.26-SNAPSHOT + 4.8.26 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.26 From e09ed013ff5a31a0d555fec642bbe45c4428689f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 25 Apr 2019 12:49:57 -0600 Subject: [PATCH 0099/1778] [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 a9ecc33ae..8c3a04e25 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.26 + 4.8.27-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.26 + HEAD From 66a3a18aa518500d1c23a12003d2c605dd187e7d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 25 Apr 2019 12:59:11 -0600 Subject: [PATCH 0100/1778] Improve logging --- src/main/java/io/github/classgraph/Scanner.java | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 017adddef..00b2d49b9 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -647,7 +647,6 @@ public int compare(final SimpleEntry o1, } }); // Find any nesting of elements within other elements - LogNode nestedClasspathRootNode = null; for (int i = 0; i < classpathElts.size(); i++) { // See if each classpath element is a prefix of any others (if so, they will immediately follow // in lexicographic order) @@ -677,11 +676,7 @@ public int compare(final SimpleEntry o1, } baseElement.nestedClasspathRootPrefixes.add(nestedClasspathRelativePath + "/"); if (log != null) { - if (nestedClasspathRootNode == null) { - nestedClasspathRootNode = log.log("Found nested classpath elements"); - } - nestedClasspathRootNode - .log(basePath + " is a prefix of the nested element " + comparePath); + log.log(basePath + " is a prefix of the nested element " + comparePath); } } } @@ -892,8 +887,6 @@ private ScanResult performScan(final List finalClasspathEltOrd * if a worker threw an uncaught exception */ private ScanResult openClasspathElementsThenScan() throws InterruptedException, ExecutionException { - final LogNode log = topLevelLog == null ? null : topLevelLog.log("Finding nested classpath elements"); - // Get order of elements in traditional classpath final List rawClasspathEntryWorkUnits = new ArrayList<>(); for (final Entry rawClasspathEntry : classpathFinder.getClasspathOrder().getOrder()) { @@ -919,10 +912,12 @@ private ScanResult openClasspathElementsThenScan() throws InterruptedException, // Find classpath elements that are path prefixes of other classpath elements, and for // ClasspathElementZip, get module-related manifest entry values - preprocessClasspathElementsByType(classpathEltOrder, log); + preprocessClasspathElementsByType(classpathEltOrder, + topLevelLog == null ? null : topLevelLog.log("Finding nested classpath elements")); // Order modules before classpath elements from traditional classpath - final LogNode classpathOrderLog = log == null ? null : log.log("Final classpath element order:"); + final LogNode classpathOrderLog = topLevelLog == null ? null + : topLevelLog.log("Final classpath element order:"); final int numElts = moduleClasspathEltOrder.size() + classpathEltOrder.size(); final List finalClasspathEltOrder = new ArrayList<>(numElts); final List finalClasspathEltOrderStrs = new ArrayList<>(numElts); From dc20a30524b09ab1ec175de58d831dccd93c3a84 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 25 Apr 2019 13:52:02 -0600 Subject: [PATCH 0101/1778] Optimize string intern function --- src/main/java/io/github/classgraph/Classfile.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index 352c137cc..1e2e05667 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -447,14 +447,10 @@ private String intern(final String str) { if (str == null) { return null; } - final String interned = stringInternMap.get(str); + final String interned = stringInternMap.putIfAbsent(str, str); if (interned != null) { return interned; } - final String interned1 = stringInternMap.putIfAbsent(str, str); - if (interned1 != null) { - return interned1; - } return str; } From 1e014fa2e7bfc49498d7a2cd09f47aa180486b6e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 25 Apr 2019 20:24:14 -0600 Subject: [PATCH 0102/1778] Speed up whitelist checking for large whitelists (#338) --- .../nonapi/io/github/classgraph/ScanSpec.java | 24 ++-- .../io/github/classgraph/WhiteBlackList.java | 106 +++++++++++++++--- .../io/github/classgraph/utils/FileUtils.java | 19 +++- 3 files changed, 119 insertions(+), 30 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/ScanSpec.java b/src/main/java/nonapi/io/github/classgraph/ScanSpec.java index c8be41e36..8adfb8faf 100644 --- a/src/main/java/nonapi/io/github/classgraph/ScanSpec.java +++ b/src/main/java/nonapi/io/github/classgraph/ScanSpec.java @@ -48,41 +48,41 @@ */ public class ScanSpec { /** Package white/blacklist (with separator '.'). */ - public WhiteBlackListWholeString packageWhiteBlackList = new WhiteBlackListWholeString(); + public WhiteBlackListWholeString packageWhiteBlackList = new WhiteBlackListWholeString('.'); /** Package prefix white/blacklist, for recursive scanning (with separator '.', ending in '.'). */ - public WhiteBlackListPrefix packagePrefixWhiteBlackList = new WhiteBlackListPrefix(); + public WhiteBlackListPrefix packagePrefixWhiteBlackList = new WhiteBlackListPrefix('.'); /** Path white/blacklist (with separator '/'). */ - public WhiteBlackListWholeString pathWhiteBlackList = new WhiteBlackListWholeString(); + public WhiteBlackListWholeString pathWhiteBlackList = new WhiteBlackListWholeString('/'); /** Path prefix white/blacklist, for recursive scanning (with separator '/', ending in '/'). */ - public WhiteBlackListPrefix pathPrefixWhiteBlackList = new WhiteBlackListPrefix(); + public WhiteBlackListPrefix pathPrefixWhiteBlackList = new WhiteBlackListPrefix('/'); /** Class white/blacklist (fully-qualified class names, with separator '.'). */ - public WhiteBlackListWholeString classWhiteBlackList = new WhiteBlackListWholeString(); + public WhiteBlackListWholeString classWhiteBlackList = new WhiteBlackListWholeString('.'); /** Classfile white/blacklist (path to classfiles, with separator '/', ending in ".class"). */ - public WhiteBlackListWholeString classfilePathWhiteBlackList = new WhiteBlackListWholeString(); + public WhiteBlackListWholeString classfilePathWhiteBlackList = new WhiteBlackListWholeString('/'); /** Package containing white/blacklisted classes (with separator '.'). */ - public WhiteBlackListWholeString classPackageWhiteBlackList = new WhiteBlackListWholeString(); + public WhiteBlackListWholeString classPackageWhiteBlackList = new WhiteBlackListWholeString('.'); /** Path to white/blacklisted classes (with separator '/'). */ - public WhiteBlackListWholeString classPackagePathWhiteBlackList = new WhiteBlackListWholeString(); + public WhiteBlackListWholeString classPackagePathWhiteBlackList = new WhiteBlackListWholeString('/'); /** Module white/blacklist (with separator '.'). */ - public WhiteBlackListWholeString moduleWhiteBlackList = new WhiteBlackListWholeString(); + public WhiteBlackListWholeString moduleWhiteBlackList = new WhiteBlackListWholeString('.'); /** Jar white/blacklist (leafname only, ending in ".jar"). */ - public WhiteBlackListLeafname jarWhiteBlackList = new WhiteBlackListLeafname(); + public WhiteBlackListLeafname jarWhiteBlackList = new WhiteBlackListLeafname('/'); /** Classpath element resource path white/blacklist. */ public WhiteBlackListWholeString classpathElementResourcePathWhiteBlackList = // - new WhiteBlackListWholeString(); + new WhiteBlackListWholeString('/'); /** lib/ext jar white/blacklist (leafname only, ending in ".jar"). */ - public WhiteBlackListLeafname libOrExtJarWhiteBlackList = new WhiteBlackListLeafname(); + public WhiteBlackListLeafname libOrExtJarWhiteBlackList = new WhiteBlackListLeafname('/'); // ------------------------------------------------------------------------------------------------------------- diff --git a/src/main/java/nonapi/io/github/classgraph/WhiteBlackList.java b/src/main/java/nonapi/io/github/classgraph/WhiteBlackList.java index a147f4208..cdab45687 100644 --- a/src/main/java/nonapi/io/github/classgraph/WhiteBlackList.java +++ b/src/main/java/nonapi/io/github/classgraph/WhiteBlackList.java @@ -45,7 +45,9 @@ public abstract class WhiteBlackList { protected Set whitelist; /** Blacklisted items (whole-string match). */ protected Set blacklist; - /** Whitelisted items (prefix match). */ + /** Whitelisted items (prefix match), as a set. */ + protected Set whitelistPrefixesSet; + /** Whitelisted items (prefix match), as a sorted list. */ protected List whitelistPrefixes; /** Blacklisted items (prefix match). */ protected List blacklistPrefixes; @@ -57,14 +59,40 @@ public abstract class WhiteBlackList { protected transient List whitelistPatterns; /** Blacklist regexp patterns. (Not serialized to JSON.) */ protected transient List blacklistPatterns; + /** The separator character. */ + protected char separatorChar; - /** Constructor for deserialization. */ + /** Deserialization constructor. */ public WhiteBlackList() { - // Empty + } + + /** + * Constructor for deserialization. + * + * @param separatorChar + * the separator char + */ + public WhiteBlackList(final char separatorChar) { + this.separatorChar = separatorChar; } /** Whitelist/blacklist for prefix strings. */ public static class WhiteBlackListPrefix extends WhiteBlackList { + /** Deserialization constructor. */ + public WhiteBlackListPrefix() { + super(); + } + + /** + * Instantiate a new whitelist/blacklist for prefix strings. + * + * @param separatorChar + * the separator char + */ + public WhiteBlackListPrefix(final char separatorChar) { + super(separatorChar); + } + /** * Add to the whitelist. * @@ -76,10 +104,10 @@ public void addToWhitelist(final String str) { if (str.contains("*")) { throw new IllegalArgumentException("Cannot use a glob wildcard here: " + str); } - if (this.whitelistPrefixes == null) { - this.whitelistPrefixes = new ArrayList<>(); + if (this.whitelistPrefixesSet == null) { + this.whitelistPrefixesSet = new HashSet<>(); } - this.whitelistPrefixes.add(str); + this.whitelistPrefixesSet.add(str); } /** @@ -187,6 +215,21 @@ public boolean isBlacklisted(final String str) { /** Whitelist/blacklist for whole-strings matches. */ public static class WhiteBlackListWholeString extends WhiteBlackList { + /** Deserialization constructor. */ + public WhiteBlackListWholeString() { + super(); + } + + /** + * Instantiate a new whitelist/blacklist for whole-string matches. + * + * @param separatorChar + * the separator char + */ + public WhiteBlackListWholeString(final char separatorChar) { + super(separatorChar); + } + /** * Add to the whitelist. * @@ -208,6 +251,29 @@ public void addToWhitelist(final String str) { } this.whitelist.add(str); } + + // For WhiteBlackListWholeString, which doesn't perform prefix matches like WhiteBlackListPrefix, + // use whitelistPrefixes to store all parent prefixes of a whitelisted path, so that + // whitelistHasPrefix() can operate efficiently on very large whitelists (#338), + // in particular where the size of the whitelist is much larger than the maximum path depth. + if (this.whitelistPrefixesSet == null) { + this.whitelistPrefixesSet = new HashSet<>(); + whitelistPrefixesSet.add(""); + whitelistPrefixesSet.add("/"); + } + String prefix = str; + if (prefix.contains("*")) { + // Stop performing prefix search at first '*' + prefix = prefix.substring(prefix.indexOf('*')); + if (!prefix.isEmpty() && !prefix.endsWith(Character.toString(separatorChar))) { + // /path/to/wildcard* -> /path/to + prefix = prefix.substring(prefix.lastIndexOf(separatorChar)); + } + } + // Add str itself as a prefix (this will only match a parent dir for + for (; !prefix.isEmpty(); prefix = FileUtils.getParentDirPath(prefix, separatorChar)) { + whitelistPrefixesSet.add(prefix + separatorChar); + } } /** @@ -267,15 +333,10 @@ public boolean isWhitelisted(final String str) { */ @Override public boolean whitelistHasPrefix(final String str) { - if (whitelist == null) { + if (whitelistPrefixesSet == null) { return false; } - for (final String w : whitelist) { - if (w.startsWith(str)) { - return true; - } - } - return false; + return whitelistPrefixesSet.contains(str); } /** @@ -291,8 +352,22 @@ public boolean isBlacklisted(final String str) { } } - /** Whitelist/blacklist for prefix strings. */ + /** Whitelist/blacklist for leaf matches. */ public static class WhiteBlackListLeafname extends WhiteBlackListWholeString { + /** Deserialization constructor. */ + public WhiteBlackListLeafname() { + super(); + } + + /** + * Instantiates a new whitelist/blacklist for leaf matches. + * + * @param separatorChar + * the separator char + */ + public WhiteBlackListLeafname(final char separatorChar) { + super(separatorChar); + } /** * Add to the whitelist. @@ -559,7 +634,8 @@ public boolean isSpecificallyWhitelisted(final String str) { /** Need to sort prefixes to ensure correct whitelist/blacklist evaluation (see Issue #167). */ void sortPrefixes() { - if (whitelistPrefixes != null) { + if (whitelistPrefixesSet != null) { + whitelistPrefixes = new ArrayList<>(whitelistPrefixesSet); CollectionUtils.sortIfNotEmpty(whitelistPrefixes); } if (blacklistPrefixes != 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 b1e30f81d..1ace95b3a 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -424,16 +424,29 @@ public static boolean canRead(final File file) { * * @param path * the path + * @param separator + * the separator * @return the parent dir path */ - public static String getParentDirPath(final String path) { - final int lastSlashIdx = path.lastIndexOf('/'); - if (lastSlashIdx == 0) { + public static String getParentDirPath(final String path, final char separator) { + final int lastSlashIdx = path.lastIndexOf(separator); + if (lastSlashIdx <= 0) { return ""; } return path.substring(0, lastSlashIdx); } + /** + * Get the parent dir path. + * + * @param path + * the path + * @return the parent dir path + */ + public static String getParentDirPath(final String path) { + return getParentDirPath(path, '/'); + } + // ------------------------------------------------------------------------------------------------------------- /** From 0b0dd429203602dd2e5abef0f52992693437aca7 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 25 Apr 2019 20:27:46 -0600 Subject: [PATCH 0103/1778] Robustness fix --- .../java/nonapi/io/github/classgraph/WhiteBlackList.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/nonapi/io/github/classgraph/WhiteBlackList.java b/src/main/java/nonapi/io/github/classgraph/WhiteBlackList.java index cdab45687..c7ee5c8af 100644 --- a/src/main/java/nonapi/io/github/classgraph/WhiteBlackList.java +++ b/src/main/java/nonapi/io/github/classgraph/WhiteBlackList.java @@ -261,15 +261,20 @@ public void addToWhitelist(final String str) { whitelistPrefixesSet.add(""); whitelistPrefixesSet.add("/"); } + final String separator = Character.toString(separatorChar); String prefix = str; if (prefix.contains("*")) { // Stop performing prefix search at first '*' prefix = prefix.substring(prefix.indexOf('*')); - if (!prefix.isEmpty() && !prefix.endsWith(Character.toString(separatorChar))) { + if (!prefix.isEmpty() && !prefix.endsWith(separator)) { // /path/to/wildcard* -> /path/to prefix = prefix.substring(prefix.lastIndexOf(separatorChar)); } } + // Strip off any final separator + while (prefix.endsWith(separator)) { + prefix = prefix.substring(0, prefix.length() - 1); + } // Add str itself as a prefix (this will only match a parent dir for for (; !prefix.isEmpty(); prefix = FileUtils.getParentDirPath(prefix, separatorChar)) { whitelistPrefixesSet.add(prefix + separatorChar); From 2a24a970c0550ef663a05f4540d63418be20a9b8 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 26 Apr 2019 13:57:27 -0600 Subject: [PATCH 0104/1778] Fix for whitelisted prefixes --- src/main/java/nonapi/io/github/classgraph/WhiteBlackList.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/nonapi/io/github/classgraph/WhiteBlackList.java b/src/main/java/nonapi/io/github/classgraph/WhiteBlackList.java index c7ee5c8af..c53ad92cf 100644 --- a/src/main/java/nonapi/io/github/classgraph/WhiteBlackList.java +++ b/src/main/java/nonapi/io/github/classgraph/WhiteBlackList.java @@ -641,6 +641,8 @@ public boolean isSpecificallyWhitelisted(final String str) { void sortPrefixes() { if (whitelistPrefixesSet != null) { whitelistPrefixes = new ArrayList<>(whitelistPrefixesSet); + } + if (whitelistPrefixes != null) { CollectionUtils.sortIfNotEmpty(whitelistPrefixes); } if (blacklistPrefixes != null) { From 64eac5438dd9c71231cb2b0561dae2139941877c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 26 Apr 2019 14:41:24 -0600 Subject: [PATCH 0105/1778] [maven-release-plugin] prepare release classgraph-4.8.27 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 8c3a04e25..9a3594b38 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.27-SNAPSHOT + 4.8.27 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.27 From a133c6e8c3cffec02a8452e944dde158765c5340 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 26 Apr 2019 14:41:29 -0600 Subject: [PATCH 0106/1778] [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 9a3594b38..479139983 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.27 + 4.8.28-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.27 + HEAD From 349323530e0ab73e239b98fe2270dc22284e47a0 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 30 Apr 2019 03:23:16 -0600 Subject: [PATCH 0107/1778] Don't use StackWalker prior to JDK 11.0.4, 12.0.2, 13.x (#341) --- .../classgraph/classpath/CallStackReader.java | 14 ++++++- .../classgraph/utils/VersionFinder.java | 40 +++++++++++++++++-- 2 files changed, 48 insertions(+), 6 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 0eb2f0cf1..b64598c7c 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/CallStackReader.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/CallStackReader.java @@ -141,9 +141,19 @@ private static Class[] getCallStackViaSecurityManager(final LogNode log) { * @return The classes in the call stack. */ static Class[] getClassContext(final LogNode log) { - // For JRE 9+, use StackWalker to get call stack + // 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 Class[] stackClasses = null; - if (VersionFinder.JAVA_MAJOR_VERSION >= 9) { + 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) { // Invoke with doPrivileged -- see: // http://mail.openjdk.java.net/pipermail/jigsaw-dev/2018-October/013974.html stackClasses = AccessController.doPrivileged(new PrivilegedAction[]>() { 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 a5a9e752b..d3b26a549 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/VersionFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/VersionFinder.java @@ -34,6 +34,8 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; import java.util.Locale; import java.util.Properties; @@ -58,22 +60,52 @@ public final class VersionFinder { public static final OperatingSystem OS; /** Java version string. */ - private static final String JAVA_VERSION = getProperty("java.version"); + public static final String JAVA_VERSION = getProperty("java.version"); /** Java major version -- 7 for "1.7", 8 for "1.8.0_244", 9 for "9", 11 for "11-ea", etc. */ public static final int JAVA_MAJOR_VERSION; + /** Java minor version -- 0 for "11.0.4" */ + public static final int JAVA_MINOR_VERSION; + + /** Java minor version -- 4 for "11.0.4" */ + public static final int JAVA_SUB_VERSION; + + /** Java is EA release -- true for "11-ea", etc. */ + public static final boolean JAVA_IS_EA_VERSION; + static { int javaMajorVersion = 0; + int javaMinorVersion = 0; + int javaSubVersion = 0; + final List versionParts = new ArrayList<>(); if (JAVA_VERSION != null) { for (final String versionPart : JAVA_VERSION.split("[^0-9]+")) { - if (!versionPart.isEmpty() && !versionPart.equals("1")) { - javaMajorVersion = Integer.parseInt(versionPart); - break; + try { + versionParts.add(Integer.parseInt(versionPart)); + } catch (final NumberFormatException e) { + // Skip } } + if (versionParts.size() > 0 && versionParts.get(0) == 1) { + // 1.7 or 1.8 -> 7 or 8 + versionParts.remove(0); + } + if (versionParts.size() == 0) { + throw new RuntimeException("Could not determine Java version: " + JAVA_VERSION); + } + javaMajorVersion = versionParts.get(0); + if (versionParts.size() > 1) { + javaMinorVersion = versionParts.get(1); + } + if (versionParts.size() > 2) { + javaSubVersion = versionParts.get(2); + } } JAVA_MAJOR_VERSION = javaMajorVersion; + JAVA_MINOR_VERSION = javaMinorVersion; + JAVA_SUB_VERSION = javaSubVersion; + JAVA_IS_EA_VERSION = JAVA_VERSION.endsWith("-ea"); } /** The operating system type. */ From 0e577e9de3b6114027b1d4de58002079feded25c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 30 Apr 2019 03:26:18 -0600 Subject: [PATCH 0108/1778] [maven-release-plugin] prepare release classgraph-4.8.28 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 479139983..4db6adf8f 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.28-SNAPSHOT + 4.8.28 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.28 From 6d6b337597cbb2dff49669dac18d2ce121190ac1 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 30 Apr 2019 03:26:24 -0600 Subject: [PATCH 0109/1778] [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 4db6adf8f..aad7c24e3 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.28 + 4.8.29-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.28 + HEAD From c8c8b2ca1eb76339f69193fdac33d735c864215c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 30 Apr 2019 09:20:54 -0600 Subject: [PATCH 0110/1778] Also exclude JDK 13-ea from using StackWalker (#341) --- .../nonapi/io/github/classgraph/classpath/CallStackReader.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 b64598c7c..fd4350cbe 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/CallStackReader.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/CallStackReader.java @@ -153,7 +153,8 @@ static Class[] getClassContext(final LogNode log) { || (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_MAJOR_VERSION == 13 && !VersionFinder.JAVA_IS_EA_VERSION) + || VersionFinder.JAVA_MAJOR_VERSION > 13) { // Invoke with doPrivileged -- see: // http://mail.openjdk.java.net/pipermail/jigsaw-dev/2018-October/013974.html stackClasses = AccessController.doPrivileged(new PrivilegedAction[]>() { From aaeda58d6f0b211313193f9ef5bc11c7ca8a35b0 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 May 2019 00:59:37 -0600 Subject: [PATCH 0111/1778] Added Zero Bugs Commitment --- README.md | 10 ++---- Zero-Bugs-Commitment.md | 71 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 7 deletions(-) create mode 100644 Zero-Bugs-Commitment.md diff --git a/README.md b/README.md index 6be4a2a4f..14c62485a 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,8 @@ ClassGraph (formerly **FastClasspathScanner**) is an uber-fast, ultra-lightweigh [![GitHub stars chart](https://img.shields.io/badge/github%20stars-chart-yellow.svg)](https://seladb.github.io/StarTrack-js/?u=classgraph&r=classgraph) [![Gitter chat](https://img.shields.io/badge/gitter-join%20chat-yellow.svg)](https://gitter.im/classgraph/Lobby) +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 vs. Java Introspection ClassGraph has the ability to "invert" the Java class and/or reflection API, or has the ability to index classes and resources. For example, the Java class and reflection API can tell you the interfaces implemented by a given class, or can give you the list of annotations on a class; ClassGraph can find **all classes that implement a given interface**, or can find **all classes that are annotated with a given annotation**. The Java API can load the content of a resource file with a specific path in a specific ClassLoader, but ClassGraph can find and load **all resources in all classloaders with paths matching a given pattern**. @@ -86,13 +88,7 @@ ClassGraph provides a number of important capabilities to the JVM ecosystem: [See the wiki for complete documentation and usage information.](https://github.com/classgraph/classgraph/wiki) -## Status - -**FastClasspathScanner was renamed to ClassGraph, and released as version 4**. - -ClassGraph has a completely revamped API. See the [porting notes](https://github.com/classgraph/classgraph/wiki/Porting-FastClasspathScanner-code-to-ClassGraph) for information on porting from the older FastClasspathScanner version 3 API. - -In particular, the Maven group id has changed from `io.github.lukehutch.fast-classpath-scanner` to **`io.github.classgraph`** in version 4. Please see the new [Maven dependency rule](https://github.com/classgraph/classgraph/wiki) and module "requires" line in the Wiki documentation. +**ClassGraph was known as FastClasspathScanner prior to version 4**. See the [porting notes](https://github.com/classgraph/classgraph/wiki/Porting-FastClasspathScanner-code-to-ClassGraph) for information on porting from the older FastClasspathScanner API. ## Mailing List diff --git a/Zero-Bugs-Commitment.md b/Zero-Bugs-Commitment.md new file mode 100644 index 000000000..2c850f284 --- /dev/null +++ b/Zero-Bugs-Commitment.md @@ -0,0 +1,71 @@ + +# The Zero Bugs Commitment + +This software is developed and maintained under the *Zero Bugs Commitment* +(`#ZeroBugs`). This is a commitment to engage in *responsible software +engineering*, *proactive community participation*, and *positive community +engagement*, by abiding by the principles below. + +## 1. We will prioritize fixing bugs over implementing new features. + +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 open or known +bugs at zero**.* + +## 2. We will take responsibility for code we have written or contributed to. + +As attention shifts between projects, it is difficult to return to work on old +code. This can lead to [bit rot](https://en.wikipedia.org/wiki/Software_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 take over.* + +Note that this is about taking *personal responsibility* for our own work, not +about who has the *official maintainership mantle* for a project or piece of +code we have contributed to. + +## 3. We will be responsive during the bugfixing process + +It is easy to delay responding to a bug report or a request until the bug or +request becomes forgotten or obsolete. This is the unfortunate state of the vast +majority of bug reports across the entire open source ecosystem. + +*We pledge to always be as responsive as we can reasonably be to any bug report +or request we receive, whether in our own bug tracker or the bug tracker of +another project.* + +## 4. We will be respectful and inclusive + +Users have been known to use bug trackers to complain about pet issues, acting +with a sense of entitlement, and/or making demands of the developers or +maintainers of a codebase. Maintainers have been known to push back against +these demands by closing bugs as `#WONTFIX`, without trying to understand the +heart of a complaint, or trying to find a compromise. Maintainers have also been +known to reject the earnest but halting efforts of new contributors. + +*We pledge to cultivate contributions and growth among our community of by +striving to always listen to the needs and requests of our community members; +by trying to find a good solution or middle ground when there is a disagreement; +and by welcoming, encouraging, and helping users who step forward to offer +contributions. + + +### THIS DOCUMENT IS IN THE PUBLIC DOMAIN + +* Please share freely, and encourage others to share. +* You are strongly encouraged to make this commitment yourself for software +that you develop or maintain, and to encourage others to do the same. +* You can add the following wording to your 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)**. + + +### Version history: + +0.1: Original version (author: Luke Hutchison -- http://twitter.com/LH ) + From ae031261ec0f310c2ef1da7eb8cb6b5350a171ea Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 May 2019 01:05:09 -0600 Subject: [PATCH 0112/1778] Fixes --- Zero-Bugs-Commitment.md | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/Zero-Bugs-Commitment.md b/Zero-Bugs-Commitment.md index 2c850f284..0f1b5bf38 100644 --- a/Zero-Bugs-Commitment.md +++ b/Zero-Bugs-Commitment.md @@ -18,7 +18,7 @@ bugs at zero**.* ## 2. We will take responsibility for code we have written or contributed to. As attention shifts between projects, it is difficult to return to work on old -code. This can lead to [bit rot](https://en.wikipedia.org/wiki/Software_rot). +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 @@ -41,18 +41,17 @@ another project.* ## 4. We will be respectful and inclusive -Users have been known to use bug trackers to complain about pet issues, acting -with a sense of entitlement, and/or making demands of the developers or -maintainers of a codebase. Maintainers have been known to push back against -these demands by closing bugs as `#WONTFIX`, without trying to understand the -heart of a complaint, or trying to find a compromise. Maintainers have also been -known to reject the earnest but halting efforts of new contributors. - -*We pledge to cultivate contributions and growth among our community of by -striving to always listen to the needs and requests of our community members; -by trying to find a good solution or middle ground when there is a disagreement; -and by welcoming, encouraging, and helping users who step forward to offer -contributions. +Open source bug trackers have long been used to complain about pet issues, +and/or make demands that are not in line with the developers' priorities. +Developers of a codebase have been known to push back against these demands +by closing bugs as `#WONTFIX` without trying to understand the core issues, +or trying to find a compromise or solution. Maintainers have also been known +to reject the earnest but halting efforts of new contributors. + +*We pledge to cultivate contributions and growth among our community by +striving to always listen to the needs and requests of community members, +by trying to find a good solution or middle ground when there is a disagreement, +and by welcoming, encouraging, and helping users who offer contributions.* ### THIS DOCUMENT IS IN THE PUBLIC DOMAIN From 3b39d4e39d61c8fa68fe30206f1aa298cbac4890 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 May 2019 01:06:56 -0600 Subject: [PATCH 0113/1778] Fixes --- Zero-Bugs-Commitment.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Zero-Bugs-Commitment.md b/Zero-Bugs-Commitment.md index 0f1b5bf38..ff20717c8 100644 --- a/Zero-Bugs-Commitment.md +++ b/Zero-Bugs-Commitment.md @@ -56,15 +56,17 @@ and by welcoming, encouraging, and helping users who offer contributions.* ### THIS DOCUMENT IS IN THE PUBLIC DOMAIN -* Please share freely, and encourage others to share. -* You are strongly encouraged to make this commitment yourself for software -that you develop or maintain, and to encourage others to do the same. -* You can add the following wording to your homepage, linking to this document, +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. + +You can add the following wording to your 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)**. + + **This project adheres to the [Zero Bugs Commitment](https://github.com/classgraph/classgraph/blob/master/Zero-Bugs-Commitment.md).** -### Version history: +#### Version history: 0.1: Original version (author: Luke Hutchison -- http://twitter.com/LH ) From c130dbe836f42b88e11b44d89193974fa9bccfd9 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 May 2019 01:07:18 -0600 Subject: [PATCH 0114/1778] Fixes --- Zero-Bugs-Commitment.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zero-Bugs-Commitment.md b/Zero-Bugs-Commitment.md index ff20717c8..619a0674a 100644 --- a/Zero-Bugs-Commitment.md +++ b/Zero-Bugs-Commitment.md @@ -63,7 +63,7 @@ do the same. You can add the following wording to your 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).** + **This project adheres to the [Zero Bugs Commitment](https://github.com/classgraph/classgraph/blob/master/Zero-Bugs-Commitment.md).** #### Version history: From 8df4bd55c4e6d48db37bd3135f5ee729fc115ceb Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 May 2019 01:08:12 -0600 Subject: [PATCH 0115/1778] Fixes --- Zero-Bugs-Commitment.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Zero-Bugs-Commitment.md b/Zero-Bugs-Commitment.md index 619a0674a..04cb0c000 100644 --- a/Zero-Bugs-Commitment.md +++ b/Zero-Bugs-Commitment.md @@ -63,7 +63,8 @@ do the same. You can add the following wording to your 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).** +| **This project adheres to the [Zero Bugs Commitment](https://github.com/classgraph/classgraph/blob/master/Zero-Bugs-Commitment.md).** | +|-----------------------------| #### Version history: From ffd63d3be966c2abd9d44984fd5ffc0cde8c56a1 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 May 2019 01:08:58 -0600 Subject: [PATCH 0116/1778] Fixes --- Zero-Bugs-Commitment.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Zero-Bugs-Commitment.md b/Zero-Bugs-Commitment.md index 04cb0c000..45991e7e6 100644 --- a/Zero-Bugs-Commitment.md +++ b/Zero-Bugs-Commitment.md @@ -60,8 +60,9 @@ 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. -You can add the following wording to your homepage, linking to this document, -or to your own copy or your own version of this document: +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).** | |-----------------------------| From cddcd1b2a8c5512bd79e4c8aa5121c87a4535ea4 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 May 2019 01:09:32 -0600 Subject: [PATCH 0117/1778] Fixes --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 14c62485a..f9e9c96c1 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,9 @@ ClassGraph (formerly **FastClasspathScanner**) is an uber-fast, ultra-lightweigh [![GitHub stars chart](https://img.shields.io/badge/github%20stars-chart-yellow.svg)](https://seladb.github.io/StarTrack-js/?u=classgraph&r=classgraph) [![Gitter chat](https://img.shields.io/badge/gitter-join%20chat-yellow.svg)](https://gitter.im/classgraph/Lobby) -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 now fully stable. This project adheres to the **[Zero Bugs Commitment](https://github.com/classgraph/classgraph/blob/master/Zero-Bugs-Commitment.md)**. | +|-----------------------------| + ### ClassGraph vs. Java Introspection From 27fb3aec95405ba0283246cf35da5fd6370cdf62 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 May 2019 01:11:52 -0600 Subject: [PATCH 0118/1778] Fixes --- Zero-Bugs-Commitment.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Zero-Bugs-Commitment.md b/Zero-Bugs-Commitment.md index 45991e7e6..aee1cb4ee 100644 --- a/Zero-Bugs-Commitment.md +++ b/Zero-Bugs-Commitment.md @@ -1,10 +1,11 @@ # The Zero Bugs Commitment -This software is developed and maintained under the *Zero Bugs Commitment* -(`#ZeroBugs`). This is a commitment to engage in *responsible software -engineering*, *proactive community participation*, and *positive community -engagement*, by abiding by the principles below. +This project adheres to the **Zero Bugs Commitment** (`#ZeroBugs`). +This is a commitment to engage in *responsible software engineering* and +*proactive community participation*, with a goal of keeping the count of +open or known bugs at zero. It is also a commitment of *positive community +engagement*. ## 1. We will prioritize fixing bugs over implementing new features. From 5ade6b20c59a16223716c7ecdf9aec2667e28f02 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 May 2019 01:12:49 -0600 Subject: [PATCH 0119/1778] Fixes --- Zero-Bugs-Commitment.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Zero-Bugs-Commitment.md b/Zero-Bugs-Commitment.md index aee1cb4ee..e28b2bbcd 100644 --- a/Zero-Bugs-Commitment.md +++ b/Zero-Bugs-Commitment.md @@ -7,6 +7,8 @@ This is a commitment to engage in *responsible software engineering* and open or known bugs at zero. It is also a commitment of *positive community engagement*. +As developers of this project, we pledge that: + ## 1. We will prioritize fixing bugs over implementing new features. It is human nature to be much more interested in building new things than doing From b9446a2a816d2f258b4f4cd71e685d07f81bd836 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 May 2019 01:14:14 -0600 Subject: [PATCH 0120/1778] Fixes --- Zero-Bugs-Commitment.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Zero-Bugs-Commitment.md b/Zero-Bugs-Commitment.md index e28b2bbcd..e8930598a 100644 --- a/Zero-Bugs-Commitment.md +++ b/Zero-Bugs-Commitment.md @@ -4,7 +4,7 @@ This project adheres to the **Zero Bugs Commitment** (`#ZeroBugs`). This is a commitment to engage in *responsible software engineering* and *proactive community participation*, with a goal of keeping the count of -open or known bugs at zero. It is also a commitment of *positive community +open or known bugs at zero. It is also a commitment to *positive community engagement*. As developers of this project, we pledge that: @@ -48,8 +48,8 @@ Open source bug trackers have long been used to complain about pet issues, and/or make demands that are not in line with the developers' priorities. Developers of a codebase have been known to push back against these demands by closing bugs as `#WONTFIX` without trying to understand the core issues, -or trying to find a compromise or solution. Maintainers have also been known -to reject the earnest but halting efforts of new contributors. +and without trying to find a compromise or solution. Maintainers have also +been known to reject the earnest but halting efforts of new contributors. *We pledge to cultivate contributions and growth among our community by striving to always listen to the needs and requests of community members, From f807729197a6d881a9447d956be8e1bb2c064458 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 May 2019 01:16:53 -0600 Subject: [PATCH 0121/1778] Fixes --- Zero-Bugs-Commitment.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Zero-Bugs-Commitment.md b/Zero-Bugs-Commitment.md index e8930598a..f8c362d1d 100644 --- a/Zero-Bugs-Commitment.md +++ b/Zero-Bugs-Commitment.md @@ -2,12 +2,12 @@ # The Zero Bugs Commitment This project adheres to the **Zero Bugs Commitment** (`#ZeroBugs`). -This is a commitment to engage in *responsible software engineering* and -*proactive community participation*, with a goal of keeping the count of -open or known bugs at zero. It is also a commitment to *positive community -engagement*. +This is a commitment to practice *responsible software engineering*, +*proactive community participation*, and *positive community engagement*, +with a goal of keeping the count of open or known bugs at zero while +respecting and cultivating community participation. -As developers of this project, we pledge that: +**As developers of this project, we pledge that:** ## 1. We will prioritize fixing bugs over implementing new features. From 3415072eab415d7a2b3afd9c9c7b11bda83e114a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 May 2019 01:24:11 -0600 Subject: [PATCH 0122/1778] Fixes --- Zero-Bugs-Commitment.md | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/Zero-Bugs-Commitment.md b/Zero-Bugs-Commitment.md index f8c362d1d..8eaba35a0 100644 --- a/Zero-Bugs-Commitment.md +++ b/Zero-Bugs-Commitment.md @@ -4,7 +4,7 @@ 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 open or known bugs at zero while +with a goal of keeping the count of known or open bugs at zero, while respecting and cultivating community participation. **As developers of this project, we pledge that:** @@ -15,7 +15,7 @@ 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 open or known +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. @@ -26,11 +26,10 @@ 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 take over.* +who can assume the responsibility for our code.* Note that this is about taking *personal responsibility* for our own work, not -about who has the *official maintainership mantle* for a project or piece of -code we have contributed to. +about who has *official maintainership* for a project or piece of code. ## 3. We will be responsive during the bugfixing process @@ -38,22 +37,19 @@ It is easy to delay responding to a bug report or a request until the bug or request becomes forgotten or obsolete. This is the unfortunate state of the vast majority of bug reports across the entire open source ecosystem. -*We pledge to always be as responsive as we can reasonably be to any bug report -or request we receive, whether in our own bug tracker or the bug tracker of -another project.* +*We pledge to endeavor to be responsive to any bug report or request we receive, +whether in our own bug tracker or the bug tracker of another project.* ## 4. We will be respectful and inclusive -Open source bug trackers have long been used to complain about pet issues, -and/or make demands that are not in line with the developers' priorities. -Developers of a codebase have been known to push back against these demands -by closing bugs as `#WONTFIX` without trying to understand the core issues, -and without trying to find a compromise or solution. Maintainers have also -been known to reject the earnest but halting efforts of new contributors. +Open source bug trackers are full of pet complaints and bugs closed as +`#WONTFIX`, without a significant attempt to understand core issues, +or to find a solution or compromise. Some development communities have +been known to reject earnest but halting efforts of new contributors. *We pledge to cultivate contributions and growth among our community by -striving to always listen to the needs and requests of community members, -by trying to find a good solution or middle ground when there is a disagreement, +striving to always listen to the needs and requests of community members; +by trying to find a good solution or middle ground when there is a disagreement; and by welcoming, encouraging, and helping users who offer contributions.* From a05bc6901d78f33e03103e63a5f7f10432032396 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 May 2019 01:27:05 -0600 Subject: [PATCH 0123/1778] Fixes --- Zero-Bugs-Commitment.md | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/Zero-Bugs-Commitment.md b/Zero-Bugs-Commitment.md index 8eaba35a0..c64e67597 100644 --- a/Zero-Bugs-Commitment.md +++ b/Zero-Bugs-Commitment.md @@ -14,19 +14,22 @@ respecting and cultivating community participation. 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 +| *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**.* +bugs at zero**.* | +|-| + ## 2. We will take responsibility for code we have written or contributed to. 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 +| *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.* +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. @@ -37,8 +40,10 @@ It is easy to delay responding to a bug report or a request until the bug or request becomes forgotten or obsolete. This is the unfortunate state of the vast majority of bug reports across the entire open source ecosystem. -*We pledge to endeavor to be responsive to any bug report or request we receive, -whether in our own bug tracker or the bug tracker of another project.* +| *We pledge to endeavor to be responsive to any bug report or request we +receive, whether in our own bug tracker or the bug tracker of another +project.* | +|-| ## 4. We will be respectful and inclusive @@ -47,10 +52,11 @@ Open source bug trackers are full of pet complaints and bugs closed as or to find a solution or compromise. Some development communities have been known to reject earnest but halting efforts of new contributors. -*We pledge to cultivate contributions and growth among our community by +| *We pledge to cultivate contributions and growth among our community by striving to always listen to the needs and requests of community members; by trying to find a good solution or middle ground when there is a disagreement; -and by welcoming, encouraging, and helping users who offer contributions.* +and by welcoming, encouraging, and helping users who offer contributions.* | +|-| ### THIS DOCUMENT IS IN THE PUBLIC DOMAIN @@ -64,7 +70,7 @@ 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).** | -|-----------------------------| +|-| #### Version history: From 458a5da312e4140b791f482a52b0ece9271f26ba Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 May 2019 01:27:33 -0600 Subject: [PATCH 0124/1778] Fixes --- Zero-Bugs-Commitment.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Zero-Bugs-Commitment.md b/Zero-Bugs-Commitment.md index c64e67597..d6cc015f5 100644 --- a/Zero-Bugs-Commitment.md +++ b/Zero-Bugs-Commitment.md @@ -17,7 +17,7 @@ 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. @@ -29,7 +29,7 @@ code. This can lead to bit rot. 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. @@ -43,7 +43,7 @@ majority of bug reports across the entire open source ecosystem. | *We pledge to endeavor to be responsive to any bug report or request we receive, whether in our own bug tracker or the bug tracker of another project.* | -|-| +|-----| ## 4. We will be respectful and inclusive @@ -56,7 +56,7 @@ been known to reject earnest but halting efforts of new contributors. striving to always listen to the needs and requests of community members; by trying to find a good solution or middle ground when there is a disagreement; and by welcoming, encouraging, and helping users who offer contributions.* | -|-| +|-----| ### THIS DOCUMENT IS IN THE PUBLIC DOMAIN @@ -70,7 +70,7 @@ 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).** | -|-| +|-----| #### Version history: From dbac1931bce06511f4aaab6955b312673ddcda26 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 May 2019 01:28:01 -0600 Subject: [PATCH 0125/1778] Fixes --- Zero-Bugs-Commitment.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Zero-Bugs-Commitment.md b/Zero-Bugs-Commitment.md index d6cc015f5..f87adcdbf 100644 --- a/Zero-Bugs-Commitment.md +++ b/Zero-Bugs-Commitment.md @@ -17,7 +17,7 @@ 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. @@ -29,7 +29,7 @@ code. This can lead to bit rot. 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. @@ -43,7 +43,7 @@ majority of bug reports across the entire open source ecosystem. | *We pledge to endeavor to be responsive to any bug report or request we receive, whether in our own bug tracker or the bug tracker of another project.* | -|-----| +|-----------------------------| ## 4. We will be respectful and inclusive @@ -56,7 +56,7 @@ been known to reject earnest but halting efforts of new contributors. striving to always listen to the needs and requests of community members; by trying to find a good solution or middle ground when there is a disagreement; and by welcoming, encouraging, and helping users who offer contributions.* | -|-----| +|-----------------------------| ### THIS DOCUMENT IS IN THE PUBLIC DOMAIN @@ -70,7 +70,7 @@ 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).** | -|-----| +|-----------------------------| #### Version history: From eb800ed7d030dc8a7c9671c4fd6613beeffb0660 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 May 2019 01:29:46 -0600 Subject: [PATCH 0126/1778] Fixes --- Zero-Bugs-Commitment.md | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/Zero-Bugs-Commitment.md b/Zero-Bugs-Commitment.md index f87adcdbf..e134558f0 100644 --- a/Zero-Bugs-Commitment.md +++ b/Zero-Bugs-Commitment.md @@ -14,9 +14,9 @@ respecting and cultivating community participation. 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**.* | +| *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**.* | |-----------------------------| @@ -25,10 +25,11 @@ bugs at zero**.* | 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.* | +| *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 @@ -40,9 +41,9 @@ It is easy to delay responding to a bug report or a request until the bug or request becomes forgotten or obsolete. This is the unfortunate state of the vast majority of bug reports across the entire open source ecosystem. -| *We pledge to endeavor to be responsive to any bug report or request we -receive, whether in our own bug tracker or the bug tracker of another -project.* | +| *We pledge to endeavor to be responsive to any bug report or request we | +| receive, whether in our own bug tracker or the bug tracker of another | +| project.* | |-----------------------------| ## 4. We will be respectful and inclusive @@ -52,10 +53,11 @@ Open source bug trackers are full of pet complaints and bugs closed as or to find a solution or compromise. Some development communities have been known to reject earnest but halting efforts of new contributors. -| *We pledge to cultivate contributions and growth among our community by -striving to always listen to the needs and requests of community members; -by trying to find a good solution or middle ground when there is a disagreement; -and by welcoming, encouraging, and helping users who offer contributions.* | +| *We pledge to cultivate contributions and growth among our community by | +| striving to always listen to the needs and requests of community members; | +| by trying to find a good solution or middle ground when there is a | +| disagreement; | +| and by welcoming, encouraging, and helping users who offer contributions.* | |-----------------------------| From 84b81b2d39d132ec2414a25e4254b67f177094c8 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 May 2019 01:32:14 -0600 Subject: [PATCH 0127/1778] Fixes --- Zero-Bugs-Commitment.md | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/Zero-Bugs-Commitment.md b/Zero-Bugs-Commitment.md index e134558f0..971eddd5d 100644 --- a/Zero-Bugs-Commitment.md +++ b/Zero-Bugs-Commitment.md @@ -14,9 +14,9 @@ respecting and cultivating community participation. 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**.* | +| *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**.* | |-----------------------------| @@ -25,11 +25,10 @@ the hard work to fix old, broken things. 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.* | +| *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 @@ -41,9 +40,9 @@ It is easy to delay responding to a bug report or a request until the bug or request becomes forgotten or obsolete. This is the unfortunate state of the vast majority of bug reports across the entire open source ecosystem. -| *We pledge to endeavor to be responsive to any bug report or request we | -| receive, whether in our own bug tracker or the bug tracker of another | -| project.* | +| *We pledge to endeavor to be responsive to any bug report or request we
+receive, whether in our own bug tracker or the bug tracker of another
+project.* | |-----------------------------| ## 4. We will be respectful and inclusive @@ -53,11 +52,11 @@ Open source bug trackers are full of pet complaints and bugs closed as or to find a solution or compromise. Some development communities have been known to reject earnest but halting efforts of new contributors. -| *We pledge to cultivate contributions and growth among our community by | -| striving to always listen to the needs and requests of community members; | -| by trying to find a good solution or middle ground when there is a | -| disagreement; | -| and by welcoming, encouraging, and helping users who offer contributions.* | +| *We pledge to cultivate contributions and growth among our community by
+striving to always listen to the needs and requests of community members;
+by trying to find a good solution or middle ground when there is a
+disagreement;
+and by welcoming, encouraging, and helping users who offer contributions.* | |-----------------------------| @@ -71,7 +70,8 @@ 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).** | +| **This project adheres to the
+[Zero Bugs Commitment](https://github.com/classgraph/classgraph/blob/master/Zero-Bugs-Commitment.md).** | |-----------------------------| From 6980927bda555ec9fe7f78faed3c3ff5fc61e068 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 May 2019 01:35:42 -0600 Subject: [PATCH 0128/1778] Fixes --- Zero-Bugs-Commitment.md | 40 ++++++++++++++++------------------------ 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/Zero-Bugs-Commitment.md b/Zero-Bugs-Commitment.md index 971eddd5d..e49562830 100644 --- a/Zero-Bugs-Commitment.md +++ b/Zero-Bugs-Commitment.md @@ -14,10 +14,9 @@ respecting and cultivating community participation. 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**.* | -|-----------------------------| +> *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. @@ -25,11 +24,10 @@ open bugs at zero**.* | 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.* | -|-----------------------------| +> *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. @@ -40,10 +38,9 @@ It is easy to delay responding to a bug report or a request until the bug or request becomes forgotten or obsolete. This is the unfortunate state of the vast majority of bug reports across the entire open source ecosystem. -| *We pledge to endeavor to be responsive to any bug report or request we
-receive, whether in our own bug tracker or the bug tracker of another
-project.* | -|-----------------------------| +> *We pledge to endeavor to be responsive to any bug report or request we +receive, whether in our own bug tracker or the bug tracker of another +project.* ## 4. We will be respectful and inclusive @@ -52,13 +49,11 @@ Open source bug trackers are full of pet complaints and bugs closed as or to find a solution or compromise. Some development communities have been known to reject earnest but halting efforts of new contributors. -| *We pledge to cultivate contributions and growth among our community by
-striving to always listen to the needs and requests of community members;
-by trying to find a good solution or middle ground when there is a
-disagreement;
-and by welcoming, encouraging, and helping users who offer contributions.* | -|-----------------------------| - +> *We pledge to cultivate contributions and growth among our community by +striving to always listen to the needs and requests of community members; +by trying to find a good solution or middle ground when there is a +disagreement; and by welcoming, encouraging, and helping users who offer +contributions.* ### THIS DOCUMENT IS IN THE PUBLIC DOMAIN @@ -70,12 +65,9 @@ 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).** | +| **This project adheres to the [Zero Bugs Commitment](https://github.com/classgraph/classgraph/blob/master/Zero-Bugs-Commitment.md).** | |-----------------------------| - #### Version history: 0.1: Original version (author: Luke Hutchison -- http://twitter.com/LH ) - From 3f8b9d12d8fc6e61261bf686b177483d6af103aa Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 May 2019 01:35:54 -0600 Subject: [PATCH 0129/1778] Fixes --- Zero-Bugs-Commitment.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zero-Bugs-Commitment.md b/Zero-Bugs-Commitment.md index e49562830..e86525770 100644 --- a/Zero-Bugs-Commitment.md +++ b/Zero-Bugs-Commitment.md @@ -55,7 +55,7 @@ by trying to find a good solution or middle ground when there is a disagreement; and by welcoming, encouraging, and helping users who offer contributions.* -### THIS DOCUMENT IS IN THE PUBLIC DOMAIN +#### 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 From ca23901e848cced8fa2e75d5996db28792efc129 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 May 2019 01:38:40 -0600 Subject: [PATCH 0130/1778] Fixes --- Zero-Bugs-Commitment.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Zero-Bugs-Commitment.md b/Zero-Bugs-Commitment.md index e86525770..89f46aa02 100644 --- a/Zero-Bugs-Commitment.md +++ b/Zero-Bugs-Commitment.md @@ -44,16 +44,16 @@ project.* ## 4. We will be respectful and inclusive -Open source bug trackers are full of pet complaints and bugs closed as -`#WONTFIX`, without a significant attempt to understand core issues, -or to find a solution or compromise. Some development communities have -been known to reject earnest but halting efforts of new contributors. - -> *We pledge to cultivate contributions and growth among our community by -striving to always listen to the needs and requests of community members; -by trying to find a good solution or middle ground when there is a -disagreement; and by welcoming, encouraging, and helping users who offer -contributions.* +Open source communities have been known to reject halting but earnest efforts +of new contributors. Bug trackers are 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; +striving to listen to the needs and requests of community members; +and trying to find a good solution or middle ground when there is a +disagreement.* #### THIS DOCUMENT IS IN THE PUBLIC DOMAIN From b301e79748083339f65caad371a0a2817281f98b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 May 2019 01:46:21 -0600 Subject: [PATCH 0131/1778] Fixes --- Zero-Bugs-Commitment.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zero-Bugs-Commitment.md b/Zero-Bugs-Commitment.md index 89f46aa02..3a274303c 100644 --- a/Zero-Bugs-Commitment.md +++ b/Zero-Bugs-Commitment.md @@ -5,7 +5,7 @@ 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 community participation. +respecting and cultivating contributions. **As developers of this project, we pledge that:** From 288640ae3ec4100a216c5dc83f2bf1febc5d8047 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 May 2019 01:48:27 -0600 Subject: [PATCH 0132/1778] Fixes --- Zero-Bugs-Commitment.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Zero-Bugs-Commitment.md b/Zero-Bugs-Commitment.md index 3a274303c..c0f5c7805 100644 --- a/Zero-Bugs-Commitment.md +++ b/Zero-Bugs-Commitment.md @@ -45,14 +45,14 @@ project.* ## 4. We will be respectful and inclusive Open source communities have been known to reject halting but earnest efforts -of new contributors. Bug trackers are full of pet complaints and bugs closed -as `#WONTFIX`, without a significant attempt to understand core issues, +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; -striving to listen to the needs and requests of community members; -and trying to find a good solution or middle ground when there is a +by striving to listen to the needs and requests of community members; +and by trying to find a good solution or middle ground when there is a disagreement.* #### THIS DOCUMENT IS IN THE PUBLIC DOMAIN From 411508b19da0c8916ec455c45386ba8cfc5161fa Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 May 2019 01:49:03 -0600 Subject: [PATCH 0133/1778] Fixes --- Zero-Bugs-Commitment.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Zero-Bugs-Commitment.md b/Zero-Bugs-Commitment.md index c0f5c7805..9276c3607 100644 --- a/Zero-Bugs-Commitment.md +++ b/Zero-Bugs-Commitment.md @@ -52,8 +52,8 @@ 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 good solution or middle ground when there is a -disagreement.* +and by trying to find a reasonable solution or middle ground when there is +a disagreement.* #### THIS DOCUMENT IS IN THE PUBLIC DOMAIN From 467f5ee61c4eb8e4343d4819be25786b5d119080 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 May 2019 01:54:27 -0600 Subject: [PATCH 0134/1778] Fixes --- Zero-Bugs-Commitment.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Zero-Bugs-Commitment.md b/Zero-Bugs-Commitment.md index 9276c3607..71601a6d8 100644 --- a/Zero-Bugs-Commitment.md +++ b/Zero-Bugs-Commitment.md @@ -32,7 +32,7 @@ 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 +## 3. We will be responsive during the bugfixing process. It is easy to delay responding to a bug report or a request until the bug or request becomes forgotten or obsolete. This is the unfortunate state of the vast @@ -42,7 +42,7 @@ majority of bug reports across the entire open source ecosystem. receive, whether in our own bug tracker or the bug tracker of another project.* -## 4. We will be respectful and inclusive +## 4. We will be respectful and inclusive. 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 From 14d830c10fdb3e13fa006699a425c82d0b568d98 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 May 2019 01:56:33 -0600 Subject: [PATCH 0135/1778] Fixes --- Zero-Bugs-Commitment.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Zero-Bugs-Commitment.md b/Zero-Bugs-Commitment.md index 71601a6d8..bc9470578 100644 --- a/Zero-Bugs-Commitment.md +++ b/Zero-Bugs-Commitment.md @@ -11,20 +11,20 @@ respecting and cultivating contributions. ## 1. We will prioritize fixing bugs over implementing new features. -It is human nature to be much more interested in building new things than doing -the hard work to fix old, broken things. +> 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 +💡 *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. -As attention shifts between projects, it is difficult to return to work on old +> 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 +💡 *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.* @@ -34,22 +34,22 @@ about who has *official maintainership* for a project or piece of code. ## 3. We will be responsive during the bugfixing process. -It is easy to delay responding to a bug report or a request until the bug or +> It is easy to delay responding to a bug report or a request until the bug or request becomes forgotten or obsolete. This is the unfortunate state of the vast majority of bug reports across the entire open source ecosystem. -> *We pledge to endeavor to be responsive to any bug report or request we +💡 *We pledge to endeavor to be responsive to any bug report or request we receive, whether in our own bug tracker or the bug tracker of another project.* ## 4. We will be respectful and inclusive. -Open source communities have been known to reject halting but earnest efforts +> 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 +💡 *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 @@ -61,9 +61,9 @@ 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: +**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).** | |-----------------------------| From 996943277ded0ed35ddd91eeb1f31fc40de25ecd Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 May 2019 01:58:31 -0600 Subject: [PATCH 0136/1778] Fixes --- Zero-Bugs-Commitment.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Zero-Bugs-Commitment.md b/Zero-Bugs-Commitment.md index bc9470578..75a370d62 100644 --- a/Zero-Bugs-Commitment.md +++ b/Zero-Bugs-Commitment.md @@ -68,6 +68,9 @@ 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 -- http://twitter.com/LH ) From e9ef6f6c69178831e1e5ad12abea92554ca27f49 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 May 2019 01:59:31 -0600 Subject: [PATCH 0137/1778] Fixes --- Zero-Bugs-Commitment.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Zero-Bugs-Commitment.md b/Zero-Bugs-Commitment.md index 75a370d62..75e9dd5f9 100644 --- a/Zero-Bugs-Commitment.md +++ b/Zero-Bugs-Commitment.md @@ -55,6 +55,8 @@ 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 From e1090ba090fe2fd6305ff52345863cd0f8adc1ac Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 May 2019 02:01:20 -0600 Subject: [PATCH 0138/1778] Fixes --- Zero-Bugs-Commitment.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Zero-Bugs-Commitment.md b/Zero-Bugs-Commitment.md index 75e9dd5f9..057e72ae4 100644 --- a/Zero-Bugs-Commitment.md +++ b/Zero-Bugs-Commitment.md @@ -11,8 +11,8 @@ respecting and cultivating contributions. ## 1. We will prioritize fixing bugs over implementing new features. -> It is human nature to be much more interested in building new things than -doing the hard work to fix old, broken things. +(Background: 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 @@ -21,8 +21,8 @@ open bugs at zero**.* ## 2. We will take responsibility for code we have written or contributed to. -> As attention shifts between projects, it is difficult to return to work on old -code. This can lead to bit rot. +(Background: 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 @@ -34,9 +34,9 @@ about who has *official maintainership* for a project or piece of code. ## 3. We will be responsive during the bugfixing process. -> It is easy to delay responding to a bug report or a request until the bug or -request becomes forgotten or obsolete. This is the unfortunate state of the vast -majority of bug reports across the entire open source ecosystem. +(Background: It is easy to delay responding to a bug report or a request until +the bug or request becomes forgotten or obsolete. This is the unfortunate state +of the vast majority of bug reports across the entire open source ecosystem.) 💡 *We pledge to endeavor to be responsive to any bug report or request we receive, whether in our own bug tracker or the bug tracker of another @@ -44,10 +44,10 @@ project.* ## 4. We will be respectful and inclusive. -> 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. +(Background: 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; From 35c3327fff629a0427a9c3db63d84776dc33e3e4 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 May 2019 02:03:28 -0600 Subject: [PATCH 0139/1778] Fixes --- Zero-Bugs-Commitment.md | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/Zero-Bugs-Commitment.md b/Zero-Bugs-Commitment.md index 057e72ae4..23288c5ec 100644 --- a/Zero-Bugs-Commitment.md +++ b/Zero-Bugs-Commitment.md @@ -11,49 +11,49 @@ respecting and cultivating contributions. ## 1. We will prioritize fixing bugs over implementing new features. -(Background: It is human nature to be much more interested in building new -things than doing the hard work to fix old, broken things.) +*(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 +💡 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**.* +open bugs at zero**. ## 2. We will take responsibility for code we have written or contributed to. -(Background: As attention shifts between projects, it is difficult to return to -work on old code. This can lead to bit rot.) +*(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 +💡 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.* +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. -(Background: It is easy to delay responding to a bug report or a request until +*(Motivation: It is easy to delay responding to a bug report or a request until the bug or request becomes forgotten or obsolete. This is the unfortunate state -of the vast majority of bug reports across the entire open source ecosystem.) +of the vast majority of bug reports across the entire open source ecosystem.)* -💡 *We pledge to endeavor to be responsive to any bug report or request we +💡 We pledge to endeavor to be responsive to any bug report or request we receive, whether in our own bug tracker or the bug tracker of another -project.* +project. ## 4. We will be respectful and inclusive. -(Background: Open source communities have been known to reject halting but +*(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.) +understand core issues, or to find a solution or compromise.)* -💡 *We pledge to cultivate contributions and growth in our community by +💡 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.* +a disagreement. --- From acf2d7fca57fd9c1f78ab6991795b83842dad7e4 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 May 2019 02:05:05 -0600 Subject: [PATCH 0140/1778] Fixes --- Zero-Bugs-Commitment.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Zero-Bugs-Commitment.md b/Zero-Bugs-Commitment.md index 23288c5ec..8085e81d2 100644 --- a/Zero-Bugs-Commitment.md +++ b/Zero-Bugs-Commitment.md @@ -38,9 +38,8 @@ about who has *official maintainership* for a project or piece of code. the bug or request becomes forgotten or obsolete. This is the unfortunate state of the vast majority of bug reports across the entire open source ecosystem.)* -💡 We pledge to endeavor to be responsive to any bug report or request we -receive, whether in our own bug tracker or the bug tracker of another -project. +💡 We pledge to be responsive to bug reports or requests we receive through +our project's bug tracker. ## 4. We will be respectful and inclusive. From 955917a60b388961d06486e97c4da41a4a690f0c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 May 2019 02:07:22 -0600 Subject: [PATCH 0141/1778] Fixes --- Zero-Bugs-Commitment.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Zero-Bugs-Commitment.md b/Zero-Bugs-Commitment.md index 8085e81d2..103fdc0ca 100644 --- a/Zero-Bugs-Commitment.md +++ b/Zero-Bugs-Commitment.md @@ -38,8 +38,8 @@ about who has *official maintainership* for a project or piece of code. the bug or request becomes forgotten or obsolete. This is the unfortunate state of the vast majority of bug reports across the entire open source ecosystem.)* -💡 We pledge to be responsive to bug reports or requests we receive through -our project's bug tracker. +💡 We pledge to be responsive to bug reports and requests currently open in our +project's bug tracker. ## 4. We will be respectful and inclusive. From fd33e7d93d7f13e9ddcd7324c4bef1a055544e9b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 May 2019 02:08:20 -0600 Subject: [PATCH 0142/1778] Fixes --- Zero-Bugs-Commitment.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Zero-Bugs-Commitment.md b/Zero-Bugs-Commitment.md index 103fdc0ca..0c82ce889 100644 --- a/Zero-Bugs-Commitment.md +++ b/Zero-Bugs-Commitment.md @@ -34,9 +34,10 @@ 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 or a request until -the bug or request becomes forgotten or obsolete. This is the unfortunate state -of the vast majority of bug reports across the entire open source ecosystem.)* +*(Motivation: It is easy to delay responding to a bug report, a bug comment or +a request until the bug or request becomes forgotten or obsolete. This is the +unfortunate end of the vast majority of bug reports filed across the entire open +source ecosystem.)* 💡 We pledge to be responsive to bug reports and requests currently open in our project's bug tracker. From a885550d3ba9929b67ac139f238919bf1c613511 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 May 2019 02:09:00 -0600 Subject: [PATCH 0143/1778] Fixes --- Zero-Bugs-Commitment.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zero-Bugs-Commitment.md b/Zero-Bugs-Commitment.md index 0c82ce889..cd52b9d46 100644 --- a/Zero-Bugs-Commitment.md +++ b/Zero-Bugs-Commitment.md @@ -36,7 +36,7 @@ about who has *official maintainership* for a project or piece of code. *(Motivation: It is easy to delay responding to a bug report, a bug comment or a request until the bug or request becomes forgotten or obsolete. This is the -unfortunate end of the vast majority of bug reports filed across the entire open +unfortunate end of significant proportion of bug reports filed across the open source ecosystem.)* 💡 We pledge to be responsive to bug reports and requests currently open in our From a6dfad6fc7f9a214e6a3a3d6575173cc282cd6a1 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 May 2019 02:09:57 -0600 Subject: [PATCH 0144/1778] Fixes --- Zero-Bugs-Commitment.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Zero-Bugs-Commitment.md b/Zero-Bugs-Commitment.md index cd52b9d46..1faa2f28d 100644 --- a/Zero-Bugs-Commitment.md +++ b/Zero-Bugs-Commitment.md @@ -35,8 +35,8 @@ 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 bug or request becomes forgotten or obsolete. This is the -unfortunate end of significant proportion of bug reports filed across the open +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 and requests currently open in our From 40fa1b2aa51be9e417177ed994f678734099c088 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 May 2019 02:10:53 -0600 Subject: [PATCH 0145/1778] Fixes --- Zero-Bugs-Commitment.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Zero-Bugs-Commitment.md b/Zero-Bugs-Commitment.md index 1faa2f28d..b7913aaf7 100644 --- a/Zero-Bugs-Commitment.md +++ b/Zero-Bugs-Commitment.md @@ -39,8 +39,9 @@ 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 and requests currently open in our -project's bug tracker. +💡 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. From 381295f240e45c26c453f96eeb0466cb490c1b7b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 May 2019 02:29:11 -0600 Subject: [PATCH 0146/1778] Fix static analysis warning --- .../java/nonapi/io/github/classgraph/utils/VersionFinder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 d3b26a549..f552983a7 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/VersionFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/VersionFinder.java @@ -105,7 +105,7 @@ public final class VersionFinder { JAVA_MAJOR_VERSION = javaMajorVersion; JAVA_MINOR_VERSION = javaMinorVersion; JAVA_SUB_VERSION = javaSubVersion; - JAVA_IS_EA_VERSION = JAVA_VERSION.endsWith("-ea"); + JAVA_IS_EA_VERSION = JAVA_VERSION != null && JAVA_VERSION.endsWith("-ea"); } /** The operating system type. */ From f882854ec4bba59ae958da2d9f1037c8290cbc20 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 2 May 2019 17:25:03 -0600 Subject: [PATCH 0147/1778] Fix classloading for environment classloaders on JDK9+ (#342) --- .../github/classgraph/ClassGraphClassLoader.java | 15 +++++++++++++++ .../JPMSClassLoaderHandler.java | 14 ++++++++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java index 1ca798015..0243960e7 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.nio.ByteBuffer; import java.util.Enumeration; +import nonapi.io.github.classgraph.classloaderhandler.JPMSClassLoaderHandler; import nonapi.io.github.classgraph.utils.JarUtils; /** {@link ClassLoader} for classes found by ClassGraph during scanning. */ @@ -70,6 +71,20 @@ protected Class findClass(final String className) // Try environment classloaders first boolean triedClassInfoLoader = false; + if (!JPMSClassLoaderHandler.jpmsClassLoaders.isEmpty()) { + // If JPMS classloaders were found, try these first (they are separated out from regular + // classloaders, since it is not possible to get classpath entries from them) + for (final ClassLoader jpmsClassLoader : JPMSClassLoaderHandler.jpmsClassLoaders) { + try { + return Class.forName(className, scanResult.scanSpec.initializeLoadedClasses, jpmsClassLoader); + } catch (ReflectiveOperationException | LinkageError e) { + // Ignore + } + if (classInfo != null && jpmsClassLoader == classInfo.classLoader) { + triedClassInfoLoader = true; + } + } + } final ClassLoader[] classLoaderOrder = scanResult.getClassLoaderOrderRespectingParentDelegation(); if (classLoaderOrder != null) { // Try environment classloaders 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 edf0b26bb..b49276599 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JPMSClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JPMSClassLoaderHandler.java @@ -28,6 +28,9 @@ */ package nonapi.io.github.classgraph.classloaderhandler; +import java.util.LinkedHashSet; +import java.util.Set; + import nonapi.io.github.classgraph.ScanSpec; import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; @@ -37,7 +40,10 @@ * A placeloader ClassLoaderHandler that matches Java 9+ classloaders, but does not attempt to extract URLs from * them (module scanning uses a different mechanism from classpath scanning). */ -class JPMSClassLoaderHandler implements ClassLoaderHandler { +public class JPMSClassLoaderHandler implements ClassLoaderHandler { + /** The JPMS classloaders, for use by ClassGraphClassLoader. */ + public static final Set jpmsClassLoaders = new LinkedHashSet<>(); + /** Class cannot be constructed. */ private JPMSClassLoaderHandler() { } @@ -64,7 +70,11 @@ public static boolean canHandle(final ClassLoader classLoader) { */ public static void findClassLoaderOrder(final ClassLoader classLoader, final ClassLoaderOrder classLoaderOrder) { - // Don't add this classLoader or its parents to the classloader order -- modules are handled separately + // Delegate to parent + classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true); + // Record JPMS classloaders for use by ClassGraphClassLoader + jpmsClassLoaders.add(classLoader); + // Don't add this classLoader or its parents to the classLoaderOrder -- modules are handled separately } /** From 72ecfa093ae685ae66d44a629772eacfaa91836d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 2 May 2019 22:21:54 -0600 Subject: [PATCH 0148/1778] [maven-release-plugin] prepare release classgraph-4.8.29 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index aad7c24e3..cbb0a7c81 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.29-SNAPSHOT + 4.8.29 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.29 From ef0ca1be60821ab82aac7b8dde1926dd32e91eec Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 2 May 2019 22:22:04 -0600 Subject: [PATCH 0149/1778] [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 cbb0a7c81..9e3add6f8 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.29 + 4.8.30-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.29 + HEAD From 0d5a1b08ae7650db91e5e312c734179c1a9c12f1 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 2 May 2019 22:37:21 -0600 Subject: [PATCH 0150/1778] Don't silently ignore LinkageError --- .../java/io/github/classgraph/ClassGraphClassLoader.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java index 0243960e7..98f51159a 100644 --- a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java +++ b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java @@ -77,7 +77,7 @@ protected Class findClass(final String className) for (final ClassLoader jpmsClassLoader : JPMSClassLoaderHandler.jpmsClassLoaders) { try { return Class.forName(className, scanResult.scanSpec.initializeLoadedClasses, jpmsClassLoader); - } catch (ReflectiveOperationException | LinkageError e) { + } catch (ClassNotFoundException | NoClassDefFoundError e) { // Ignore } if (classInfo != null && jpmsClassLoader == classInfo.classLoader) { @@ -91,7 +91,7 @@ protected Class findClass(final String className) for (final ClassLoader envClassLoader : classLoaderOrder) { try { return Class.forName(className, scanResult.scanSpec.initializeLoadedClasses, envClassLoader); - } catch (ReflectiveOperationException | LinkageError e) { + } catch (ClassNotFoundException | NoClassDefFoundError e) { // Ignore } if (classInfo != null && envClassLoader == classInfo.classLoader) { @@ -104,7 +104,7 @@ protected Class findClass(final String className) if (!triedClassInfoLoader && classInfo != null && classInfo.classLoader != null) { try { return Class.forName(className, scanResult.scanSpec.initializeLoadedClasses, classInfo.classLoader); - } catch (final ReflectiveOperationException | LinkageError e) { + } catch (ClassNotFoundException | NoClassDefFoundError e) { // Ignore } } From 48c0e751016afcac937db44eb8e1f68c236d4a12 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 2 May 2019 22:49:52 -0600 Subject: [PATCH 0151/1778] Refactoring --- src/main/java/io/github/classgraph/Scanner.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 00b2d49b9..45f8aa927 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -558,7 +558,7 @@ private static class ClassfileScannerWorkUnitProcessor implements WorkUnitProces private final Queue scannedClassfiles; /** The string intern map. */ - private final ConcurrentHashMap stringInternMap; + private final ConcurrentHashMap stringInternMap = new ConcurrentHashMap<>(); /** * Constructor. @@ -574,12 +574,11 @@ private static class ClassfileScannerWorkUnitProcessor implements WorkUnitProces */ public ClassfileScannerWorkUnitProcessor(final ScanSpec scanSpec, final List classpathOrder, final Set classNamesScheduledForScanning, - final Queue scannedClassfiles, final ConcurrentHashMap stringInternMap) { + final Queue scannedClassfiles) { this.scanSpec = scanSpec; this.classpathOrder = classpathOrder; this.classNamesScheduledForScanning = classNamesScheduledForScanning; this.scannedClassfiles = scannedClassfiles; - this.stringInternMap = stringInternMap; } /* (non-Javadoc) @@ -823,12 +822,10 @@ private ScanResult performScan(final List finalClasspathEltOrd // Scan classfiles in parallel. final Queue scannedClassfiles = new ConcurrentLinkedQueue<>(); - final ConcurrentHashMap stringInternMap = new ConcurrentHashMap<>(); processWorkUnits(classfileScanWorkItems, topLevelLog == null ? null : topLevelLog.log("Scanning classfiles"), new ClassfileScannerWorkUnitProcessor(scanSpec, finalClasspathEltOrder, - classNamesScheduledForScanning, scannedClassfiles, stringInternMap)); - stringInternMap.clear(); + classNamesScheduledForScanning, scannedClassfiles)); // Link the Classfile objects to produce ClassInfo objects. This needs to be done from a single thread. final LogNode linkLog = topLevelLog == null ? null : topLevelLog.log("Linking related classfiles"); From 58b8111968be9eff573b2a1478c6bb3715991c32 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 6 May 2019 01:40:25 -0600 Subject: [PATCH 0152/1778] Cleaner JPMS classloading fix (#342) --- .../classgraph/ClassGraphClassLoader.java | 15 -------------- .../JPMSClassLoaderHandler.java | 20 +++++++------------ 2 files changed, 7 insertions(+), 28 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java index 98f51159a..2efcc9c8d 100644 --- a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java +++ b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java @@ -34,7 +34,6 @@ import java.nio.ByteBuffer; import java.util.Enumeration; -import nonapi.io.github.classgraph.classloaderhandler.JPMSClassLoaderHandler; import nonapi.io.github.classgraph.utils.JarUtils; /** {@link ClassLoader} for classes found by ClassGraph during scanning. */ @@ -71,20 +70,6 @@ protected Class findClass(final String className) // Try environment classloaders first boolean triedClassInfoLoader = false; - if (!JPMSClassLoaderHandler.jpmsClassLoaders.isEmpty()) { - // If JPMS classloaders were found, try these first (they are separated out from regular - // classloaders, since it is not possible to get classpath entries from them) - for (final ClassLoader jpmsClassLoader : JPMSClassLoaderHandler.jpmsClassLoaders) { - try { - return Class.forName(className, scanResult.scanSpec.initializeLoadedClasses, jpmsClassLoader); - } catch (ClassNotFoundException | NoClassDefFoundError e) { - // Ignore - } - if (classInfo != null && jpmsClassLoader == classInfo.classLoader) { - triedClassInfoLoader = true; - } - } - } final ClassLoader[] classLoaderOrder = scanResult.getClassLoaderOrderRespectingParentDelegation(); if (classLoaderOrder != null) { // Try environment classloaders 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 b49276599..f8cf07516 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JPMSClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JPMSClassLoaderHandler.java @@ -28,9 +28,6 @@ */ package nonapi.io.github.classgraph.classloaderhandler; -import java.util.LinkedHashSet; -import java.util.Set; - import nonapi.io.github.classgraph.ScanSpec; import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; @@ -40,10 +37,7 @@ * A placeloader ClassLoaderHandler that matches Java 9+ classloaders, but does not attempt to extract URLs from * them (module scanning uses a different mechanism from classpath scanning). */ -public class JPMSClassLoaderHandler implements ClassLoaderHandler { - /** The JPMS classloaders, for use by ClassGraphClassLoader. */ - public static final Set jpmsClassLoaders = new LinkedHashSet<>(); - +class JPMSClassLoaderHandler implements ClassLoaderHandler { /** Class cannot be constructed. */ private JPMSClassLoaderHandler() { } @@ -70,11 +64,11 @@ public static boolean canHandle(final ClassLoader classLoader) { */ public static void findClassLoaderOrder(final ClassLoader classLoader, final ClassLoaderOrder classLoaderOrder) { - // Delegate to parent + // 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); - // Record JPMS classloaders for use by ClassGraphClassLoader - jpmsClassLoaders.add(classLoader); - // Don't add this classLoader or its parents to the classLoaderOrder -- modules are handled separately + classLoaderOrder.add(classLoader); } /** @@ -91,7 +85,7 @@ public static void findClassLoaderOrder(final ClassLoader classLoader, */ public static void findClasspathOrder(final ClassLoader classLoader, final ClasspathOrder classpathOrder, final ScanSpec scanSpec, final LogNode log) { - // The JDK9 classloaders have a field, URLClassPath ucp, containing URLs for unnamed modules, - // but it is not visible. + // 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. } } From f8920c6031700266a296a3f3a7a321e999e8f0c8 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 7 May 2019 02:03:29 -0600 Subject: [PATCH 0153/1778] Move `ScanSpec` and `WhiteBlackList` into `scanspec` package --- .../java/nonapi/io/github/classgraph/{ => scanspec}/ScanSpec.java | 0 .../io/github/classgraph/{ => scanspec}/WhiteBlackList.java | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/main/java/nonapi/io/github/classgraph/{ => scanspec}/ScanSpec.java (100%) rename src/main/java/nonapi/io/github/classgraph/{ => scanspec}/WhiteBlackList.java (100%) diff --git a/src/main/java/nonapi/io/github/classgraph/ScanSpec.java b/src/main/java/nonapi/io/github/classgraph/scanspec/ScanSpec.java similarity index 100% rename from src/main/java/nonapi/io/github/classgraph/ScanSpec.java rename to src/main/java/nonapi/io/github/classgraph/scanspec/ScanSpec.java diff --git a/src/main/java/nonapi/io/github/classgraph/WhiteBlackList.java b/src/main/java/nonapi/io/github/classgraph/scanspec/WhiteBlackList.java similarity index 100% rename from src/main/java/nonapi/io/github/classgraph/WhiteBlackList.java rename to src/main/java/nonapi/io/github/classgraph/scanspec/WhiteBlackList.java From e172f4ac1d38b0f0bf3774b3a6c61f2d7d5d6253 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 7 May 2019 02:04:12 -0600 Subject: [PATCH 0154/1778] Move `ScanSpec` and `WhiteBlackList` into `scanspec` package --- src/main/java/io/github/classgraph/ClassGraph.java | 4 ++-- src/main/java/io/github/classgraph/ClassInfo.java | 2 +- src/main/java/io/github/classgraph/ClassInfoList.java | 2 +- src/main/java/io/github/classgraph/Classfile.java | 2 +- src/main/java/io/github/classgraph/ClasspathElement.java | 4 ++-- .../java/io/github/classgraph/ClasspathElementDir.java | 4 ++-- .../java/io/github/classgraph/ClasspathElementModule.java | 4 ++-- .../java/io/github/classgraph/ClasspathElementZip.java | 4 ++-- .../io/github/classgraph/GraphvizDotfileGenerator.java | 2 +- src/main/java/io/github/classgraph/ScanResult.java | 2 +- src/main/java/io/github/classgraph/Scanner.java | 2 +- .../classloaderhandler/AntClassLoaderHandler.java | 2 +- .../classloaderhandler/ClassLoaderHandlerRegistry.java | 2 +- .../classloaderhandler/EquinoxClassLoaderHandler.java | 2 +- .../EquinoxContextFinderClassLoaderHandler.java | 2 +- .../classloaderhandler/FallbackClassLoaderHandler.java | 2 +- .../classloaderhandler/FelixClassLoaderHandler.java | 2 +- .../classloaderhandler/JBossClassLoaderHandler.java | 2 +- .../classloaderhandler/JPMSClassLoaderHandler.java | 2 +- .../classloaderhandler/OSGiDefaultClassLoaderHandler.java | 2 +- .../ParentLastDelegationOrderTestClassLoaderHandler.java | 2 +- .../PlexusClassWorldsClassRealmClassLoaderHandler.java | 2 +- .../SpringBootRestartClassLoaderHandler.java | 2 +- .../TomcatWebappClassLoaderBaseHandler.java | 2 +- .../classloaderhandler/URLClassLoaderHandler.java | 2 +- .../classloaderhandler/WeblogicClassLoaderHandler.java | 2 +- .../WebsphereLibertyClassLoaderHandler.java | 2 +- .../WebsphereTraditionalClassLoaderHandler.java | 2 +- .../classgraph/classpath/ClassLoaderAndModuleFinder.java | 2 +- .../io/github/classgraph/classpath/ClasspathFinder.java | 2 +- .../io/github/classgraph/classpath/ClasspathOrder.java | 2 +- .../classgraph/fastzipfilereader/NestedJarHandler.java | 2 +- .../github/classgraph/fastzipfilereader/ZipFileSlice.java | 2 +- .../nonapi/io/github/classgraph/scanspec/ScanSpec.java | 8 ++++---- .../io/github/classgraph/scanspec/WhiteBlackList.java | 2 +- 35 files changed, 43 insertions(+), 43 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index b356fc92b..a806bc9c7 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -41,11 +41,11 @@ import java.util.concurrent.LinkedBlockingQueue; import java.util.regex.Pattern; -import nonapi.io.github.classgraph.ScanSpec; -import nonapi.io.github.classgraph.WhiteBlackList; 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.scanspec.ScanSpec; +import nonapi.io.github.classgraph.scanspec.WhiteBlackList; import nonapi.io.github.classgraph.utils.JarUtils; import nonapi.io.github.classgraph.utils.LogNode; import nonapi.io.github.classgraph.utils.VersionFinder; diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index 0538b0b95..63b760182 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -47,8 +47,8 @@ import java.util.Map.Entry; import java.util.Set; -import nonapi.io.github.classgraph.ScanSpec; import nonapi.io.github.classgraph.json.Id; +import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.types.ParseException; import nonapi.io.github.classgraph.types.TypeUtils; import nonapi.io.github.classgraph.types.TypeUtils.ModifierType; diff --git a/src/main/java/io/github/classgraph/ClassInfoList.java b/src/main/java/io/github/classgraph/ClassInfoList.java index 955b35760..77f71e68c 100644 --- a/src/main/java/io/github/classgraph/ClassInfoList.java +++ b/src/main/java/io/github/classgraph/ClassInfoList.java @@ -41,7 +41,7 @@ import java.util.Set; import io.github.classgraph.ClassInfo.ReachableAndDirectlyRelatedClasses; -import nonapi.io.github.classgraph.ScanSpec; +import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.CollectionUtils; /** diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index 1e2e05667..92b1eceeb 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -40,8 +40,8 @@ import java.util.concurrent.ConcurrentHashMap; import io.github.classgraph.Scanner.ClassfileScanWorkUnit; -import nonapi.io.github.classgraph.ScanSpec; import nonapi.io.github.classgraph.concurrency.WorkQueue; +import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.types.ParseException; import nonapi.io.github.classgraph.utils.CollectionUtils; import nonapi.io.github.classgraph.utils.InputStreamOrByteBufferAdapter; diff --git a/src/main/java/io/github/classgraph/ClasspathElement.java b/src/main/java/io/github/classgraph/ClasspathElement.java index 5f93f51ae..64d092b01 100644 --- a/src/main/java/io/github/classgraph/ClasspathElement.java +++ b/src/main/java/io/github/classgraph/ClasspathElement.java @@ -42,9 +42,9 @@ import java.util.concurrent.atomic.AtomicBoolean; import io.github.classgraph.Scanner.ClasspathEntryWorkUnit; -import nonapi.io.github.classgraph.ScanSpec; -import nonapi.io.github.classgraph.ScanSpec.ScanSpecPathMatch; import nonapi.io.github.classgraph.concurrency.WorkQueue; +import nonapi.io.github.classgraph.scanspec.ScanSpec; +import nonapi.io.github.classgraph.scanspec.ScanSpec.ScanSpecPathMatch; import nonapi.io.github.classgraph.utils.FileUtils; import nonapi.io.github.classgraph.utils.JarUtils; import nonapi.io.github.classgraph.utils.LogNode; diff --git a/src/main/java/io/github/classgraph/ClasspathElementDir.java b/src/main/java/io/github/classgraph/ClasspathElementDir.java index 70f724bcb..ec4ad2115 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementDir.java @@ -44,10 +44,10 @@ import java.util.Set; import io.github.classgraph.Scanner.ClasspathEntryWorkUnit; -import nonapi.io.github.classgraph.ScanSpec; -import nonapi.io.github.classgraph.ScanSpec.ScanSpecPathMatch; import nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandlerRegistry; import nonapi.io.github.classgraph.concurrency.WorkQueue; +import nonapi.io.github.classgraph.scanspec.ScanSpec; +import nonapi.io.github.classgraph.scanspec.ScanSpec.ScanSpecPathMatch; import nonapi.io.github.classgraph.utils.FileUtils; import nonapi.io.github.classgraph.utils.InputStreamOrByteBufferAdapter; import nonapi.io.github.classgraph.utils.LogNode; diff --git a/src/main/java/io/github/classgraph/ClasspathElementModule.java b/src/main/java/io/github/classgraph/ClasspathElementModule.java index d65ae8a7b..9b2f2cdbf 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementModule.java +++ b/src/main/java/io/github/classgraph/ClasspathElementModule.java @@ -38,13 +38,13 @@ import java.util.Set; import io.github.classgraph.Scanner.ClasspathEntryWorkUnit; -import nonapi.io.github.classgraph.ScanSpec; -import nonapi.io.github.classgraph.ScanSpec.ScanSpecPathMatch; import nonapi.io.github.classgraph.concurrency.SingletonMap.NullSingletonException; import nonapi.io.github.classgraph.concurrency.WorkQueue; import nonapi.io.github.classgraph.fastzipfilereader.NestedJarHandler; import nonapi.io.github.classgraph.recycler.RecycleOnClose; import nonapi.io.github.classgraph.recycler.Recycler; +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.InputStreamOrByteBufferAdapter; import nonapi.io.github.classgraph.utils.LogNode; diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index 2d8e9929e..2d67c251b 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -41,8 +41,6 @@ import java.util.concurrent.ConcurrentHashMap; import io.github.classgraph.Scanner.ClasspathEntryWorkUnit; -import nonapi.io.github.classgraph.ScanSpec; -import nonapi.io.github.classgraph.ScanSpec.ScanSpecPathMatch; import nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandlerRegistry; import nonapi.io.github.classgraph.concurrency.SingletonMap.NullSingletonException; import nonapi.io.github.classgraph.concurrency.WorkQueue; @@ -50,6 +48,8 @@ import nonapi.io.github.classgraph.fastzipfilereader.LogicalZipFile; import nonapi.io.github.classgraph.fastzipfilereader.NestedJarHandler; import nonapi.io.github.classgraph.fastzipfilereader.ZipFileSlice; +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.InputStreamOrByteBufferAdapter; diff --git a/src/main/java/io/github/classgraph/GraphvizDotfileGenerator.java b/src/main/java/io/github/classgraph/GraphvizDotfileGenerator.java index 3bc42b215..8037b44f4 100644 --- a/src/main/java/io/github/classgraph/GraphvizDotfileGenerator.java +++ b/src/main/java/io/github/classgraph/GraphvizDotfileGenerator.java @@ -32,7 +32,7 @@ import java.util.HashSet; import java.util.Set; -import nonapi.io.github.classgraph.ScanSpec; +import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.CollectionUtils; /** Builds a class graph visualization in Graphviz .dot file format. */ diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index b5ed323b8..360540ee2 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -55,10 +55,10 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import nonapi.io.github.classgraph.ScanSpec; 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.ScanSpec; import nonapi.io.github.classgraph.utils.CollectionUtils; import nonapi.io.github.classgraph.utils.FileUtils; import nonapi.io.github.classgraph.utils.JarUtils; diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 45f8aa927..00b7a49d0 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -54,7 +54,6 @@ import io.github.classgraph.ClassGraph.ScanResultProcessor; import io.github.classgraph.Classfile.ClassfileFormatException; import io.github.classgraph.Classfile.SkipClassException; -import nonapi.io.github.classgraph.ScanSpec; import nonapi.io.github.classgraph.classpath.ClassLoaderAndModuleFinder; import nonapi.io.github.classgraph.classpath.ClasspathFinder; import nonapi.io.github.classgraph.concurrency.AutoCloseableExecutorService; @@ -64,6 +63,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.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.CollectionUtils; import nonapi.io.github.classgraph.utils.FastPathResolver; import nonapi.io.github.classgraph.utils.FileUtils; 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 1c77bfb8a..8cac4c0d1 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/AntClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/AntClassLoaderHandler.java @@ -28,9 +28,9 @@ */ package nonapi.io.github.classgraph.classloaderhandler; -import nonapi.io.github.classgraph.ScanSpec; 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; 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 8d79b2706..69cff6f00 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java @@ -33,9 +33,9 @@ import java.util.Collections; import java.util.List; -import nonapi.io.github.classgraph.ScanSpec; 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; /** The registry for ClassLoaderHandler classes. */ 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 66cdc5000..37a9bbced 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxClassLoaderHandler.java @@ -35,9 +35,9 @@ import java.util.List; import java.util.Set; -import nonapi.io.github.classgraph.ScanSpec; 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; 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 ea0aad6c5..165a8aad7 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxContextFinderClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxContextFinderClassLoaderHandler.java @@ -28,9 +28,9 @@ */ package nonapi.io.github.classgraph.classloaderhandler; -import nonapi.io.github.classgraph.ScanSpec; 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; 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 cd45a75b6..0e1f68c28 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FallbackClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FallbackClassLoaderHandler.java @@ -28,9 +28,9 @@ */ package nonapi.io.github.classgraph.classloaderhandler; -import nonapi.io.github.classgraph.ScanSpec; 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; 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 6cd77aeb1..477acf61b 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FelixClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FelixClassLoaderHandler.java @@ -33,9 +33,9 @@ import java.util.List; import java.util.Set; -import nonapi.io.github.classgraph.ScanSpec; 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; 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 3be3e27e7..df148c81f 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java @@ -37,9 +37,9 @@ import java.util.Map.Entry; import java.util.Set; -import nonapi.io.github.classgraph.ScanSpec; 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.FileUtils; import nonapi.io.github.classgraph.utils.LogNode; import nonapi.io.github.classgraph.utils.ReflectionUtils; 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 f8cf07516..2dc092abf 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JPMSClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JPMSClassLoaderHandler.java @@ -28,9 +28,9 @@ */ package nonapi.io.github.classgraph.classloaderhandler; -import nonapi.io.github.classgraph.ScanSpec; 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; /** 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 2ab2f0406..60bc6e138 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/OSGiDefaultClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/OSGiDefaultClassLoaderHandler.java @@ -30,9 +30,9 @@ import java.io.File; -import nonapi.io.github.classgraph.ScanSpec; 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; 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 f9db69e13..c3df996a6 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ParentLastDelegationOrderTestClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ParentLastDelegationOrderTestClassLoaderHandler.java @@ -28,9 +28,9 @@ */ package nonapi.io.github.classgraph.classloaderhandler; -import nonapi.io.github.classgraph.ScanSpec; 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; 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 2dd865e67..59a481ed3 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/PlexusClassWorldsClassRealmClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/PlexusClassWorldsClassRealmClassLoaderHandler.java @@ -30,9 +30,9 @@ import java.util.SortedSet; -import nonapi.io.github.classgraph.ScanSpec; 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; 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 13fb66319..b00d1e406 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/SpringBootRestartClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/SpringBootRestartClassLoaderHandler.java @@ -29,9 +29,9 @@ package nonapi.io.github.classgraph.classloaderhandler; import io.github.classgraph.ClassGraph; -import nonapi.io.github.classgraph.ScanSpec; 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; /** 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 657dfdbc4..3cb73057f 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java @@ -31,9 +31,9 @@ import java.io.File; import java.util.List; -import nonapi.io.github.classgraph.ScanSpec; 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; 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 e604a6c4d..d3c4bb954 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/URLClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/URLClassLoaderHandler.java @@ -31,9 +31,9 @@ import java.net.URL; import java.net.URLClassLoader; -import nonapi.io.github.classgraph.ScanSpec; 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; /** ClassLoaderHandler that is able to extract the URLs from a URLClassLoader. */ 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 a9c773c54..c5c8d2bc1 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WeblogicClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WeblogicClassLoaderHandler.java @@ -28,9 +28,9 @@ */ package nonapi.io.github.classgraph.classloaderhandler; -import nonapi.io.github.classgraph.ScanSpec; 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; 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 3860bca20..997024728 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java @@ -31,9 +31,9 @@ import java.io.File; import java.util.List; -import nonapi.io.github.classgraph.ScanSpec; 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; 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 959f1d90f..678eaf0ec 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereTraditionalClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereTraditionalClassLoaderHandler.java @@ -28,9 +28,9 @@ */ package nonapi.io.github.classgraph.classloaderhandler; -import nonapi.io.github.classgraph.ScanSpec; 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; diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderAndModuleFinder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderAndModuleFinder.java index e4004875b..a52563170 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderAndModuleFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderAndModuleFinder.java @@ -38,7 +38,7 @@ import java.util.Set; import io.github.classgraph.ModuleRef; -import nonapi.io.github.classgraph.ScanSpec; +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; 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 fb7c0a3e2..5399bb80e 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java @@ -33,9 +33,9 @@ import java.util.Map.Entry; import java.util.Set; -import nonapi.io.github.classgraph.ScanSpec; import nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandlerRegistry; import nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandlerRegistry.ClassLoaderHandlerRegistryEntry; +import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.FastPathResolver; import nonapi.io.github.classgraph.utils.FileUtils; import nonapi.io.github.classgraph.utils.JarUtils; 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 51ac79e38..935acc8d9 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java @@ -38,7 +38,7 @@ import java.util.Set; import io.github.classgraph.ClassGraph.ClasspathElementFilter; -import nonapi.io.github.classgraph.ScanSpec; +import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.FastPathResolver; import nonapi.io.github.classgraph.utils.FileUtils; import nonapi.io.github.classgraph.utils.JarUtils; 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 c54be5c34..0232c47ca 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java @@ -49,10 +49,10 @@ import io.github.classgraph.ModuleReaderProxy; import io.github.classgraph.ModuleRef; import io.github.classgraph.ScanResult; -import nonapi.io.github.classgraph.ScanSpec; import nonapi.io.github.classgraph.concurrency.InterruptionChecker; import nonapi.io.github.classgraph.concurrency.SingletonMap; import nonapi.io.github.classgraph.recycler.Recycler; +import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.FastPathResolver; import nonapi.io.github.classgraph.utils.FileUtils; import nonapi.io.github.classgraph.utils.LogNode; diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSlice.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSlice.java index 45ce9f990..ea4238aa6 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSlice.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSlice.java @@ -31,8 +31,8 @@ import java.io.File; import java.io.IOException; -import nonapi.io.github.classgraph.WhiteBlackList.WhiteBlackListLeafname; import nonapi.io.github.classgraph.recycler.Recycler; +import nonapi.io.github.classgraph.scanspec.WhiteBlackList.WhiteBlackListLeafname; /** A zipfile slice (a sub-range of bytes within a PhysicalZipFile. */ public class ZipFileSlice { 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 8adfb8faf..28f5ff141 100644 --- a/src/main/java/nonapi/io/github/classgraph/scanspec/ScanSpec.java +++ b/src/main/java/nonapi/io/github/classgraph/scanspec/ScanSpec.java @@ -26,7 +26,7 @@ * 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; +package nonapi.io.github.classgraph.scanspec; import java.lang.reflect.Field; import java.util.ArrayList; @@ -38,9 +38,9 @@ import io.github.classgraph.ClassInfo; import io.github.classgraph.ModulePathInfo; import io.github.classgraph.ScanResult; -import nonapi.io.github.classgraph.WhiteBlackList.WhiteBlackListLeafname; -import nonapi.io.github.classgraph.WhiteBlackList.WhiteBlackListPrefix; -import nonapi.io.github.classgraph.WhiteBlackList.WhiteBlackListWholeString; +import nonapi.io.github.classgraph.scanspec.WhiteBlackList.WhiteBlackListLeafname; +import nonapi.io.github.classgraph.scanspec.WhiteBlackList.WhiteBlackListPrefix; +import nonapi.io.github.classgraph.scanspec.WhiteBlackList.WhiteBlackListWholeString; import nonapi.io.github.classgraph.utils.LogNode; /** diff --git a/src/main/java/nonapi/io/github/classgraph/scanspec/WhiteBlackList.java b/src/main/java/nonapi/io/github/classgraph/scanspec/WhiteBlackList.java index c53ad92cf..f47227247 100644 --- a/src/main/java/nonapi/io/github/classgraph/scanspec/WhiteBlackList.java +++ b/src/main/java/nonapi/io/github/classgraph/scanspec/WhiteBlackList.java @@ -26,7 +26,7 @@ * 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; +package nonapi.io.github.classgraph.scanspec; import java.util.ArrayList; import java.util.Collection; From 2af330491ff4ce77fec9c802af50329140692760 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 7 May 2019 13:08:24 -0600 Subject: [PATCH 0155/1778] Don't write to temp file when preloading shutdown classes (#343) --- .../java/io/github/classgraph/ScanResult.java | 64 +------------------ 1 file changed, 2 insertions(+), 62 deletions(-) diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index 360540ee2..f5d121ee4 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -30,17 +30,11 @@ import java.io.Closeable; import java.io.File; -import java.io.IOException; -import java.io.PrintWriter; -import java.io.RandomAccessFile; import java.lang.ref.WeakReference; import java.net.MalformedURLException; import java.net.URI; -import java.net.URISyntaxException; import java.net.URL; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.nio.file.Files; +import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -221,61 +215,7 @@ public void run() { // fail, which will throw an exception and leave resources open (#331). // 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. - File tempFile = null; - boolean createdTempFile = false; - try { - tempFile = File.createTempFile("ClassGraph-", "-temp"); - tempFile.deleteOnExit(); - try (PrintWriter printWriter = new PrintWriter(tempFile)) { - printWriter.print("temp"); - createdTempFile = true; - } - } catch (final IOException e1) { - // Could not create temp file - } - if (!createdTempFile) { - // Could not create temp file (system does not have a writeable filesystem?). - // Instead, try opening items on java.class.path until one is found that can be opened. - final String classpath = System.getProperty("java.class.path"); - if (classpath == null) { - // Should not happen -- one of these options should work - throw new RuntimeException("Could not create temp file, and could not read java.class.path"); - } - boolean foundReadableClasspathEntry = false; - for (final String classpathEntry : JarUtils.smartPathSplit(classpath)) { - try { - tempFile = new File(new URI(classpathEntry)); - } catch (final URISyntaxException e2) { - continue; - } - if (FileUtils.canRead(tempFile)) { - // Found a readable file - foundReadableClasspathEntry = true; - break; - } - } - if (!foundReadableClasspathEntry) { - // Should not happen -- one of these options should work - throw new RuntimeException( - "Could not create temp file, and could not read a file from java.class.path"); - } - } - MappedByteBuffer buffer = null; - try (RandomAccessFile raf = new RandomAccessFile(tempFile, "r"); - FileChannel fileChannel = raf.getChannel()) { - buffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size()); - } catch (final IOException | OutOfMemoryError e) { - throw new RuntimeException("Could not open file", e); - } - // Preload classes needed by shutdown hook - FileUtils.closeDirectByteBuffer(buffer, /* log = */ null); - if (createdTempFile) { - try { - Files.delete(tempFile.toPath()); - } catch (final IOException | SecurityException e) { - // Failed - } - } + FileUtils.closeDirectByteBuffer(ByteBuffer.allocateDirect(32), /* log = */ null); } // ------------------------------------------------------------------------------------------------------------- From ab723d2238028a55e0741d64427ff3c9e4df0807 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 7 May 2019 14:01:26 -0600 Subject: [PATCH 0156/1778] Support downloading http(s) JAR URLs directly to ByteBuffer (#343) --- .../java/io/github/classgraph/ClassInfo.java | 4 +- .../classgraph/ClasspathElementZip.java | 7 +- .../java/io/github/classgraph/Resource.java | 4 +- .../fastzipfilereader/NestedJarHandler.java | 150 +++++++++++++----- .../fastzipfilereader/PhysicalZipFile.java | 8 +- .../fastzipfilereader/ZipFileSlice.java | 10 +- .../io/github/classgraph/utils/FileUtils.java | 22 ++- 7 files changed, 156 insertions(+), 49 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index 63b760182..1acb45729 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -2391,7 +2391,9 @@ public URL getClasspathElementURL() { * null if this class was found in a module. (See also {@link #getModuleRef}.) * * @return The {@link File} for the classpath element package root dir or jar that this class was found within, - * or null if this class was found in a module. (See also {@link #getModuleRef}.) + * or null if this class was found in a module (see {@link #getModuleRef}). May also return null if the + * classpath element was an http/https URL, and the jar was downloaded directly to RAM, rather than to a + * temp file on disk (e.g. if the temp dir is not writeable). */ public File getClasspathElementFile() { return classpathElement.getFile(); diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index 2d67c251b..5221f3b1a 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -499,7 +499,9 @@ void scanPaths(final LogNode log) { // Save the last modified time for the zipfile final File zipfile = getFile(); - fileToLastModified.put(zipfile, zipfile.lastModified()); + if (zipfile != null) { + fileToLastModified.put(zipfile, zipfile.lastModified()); + } finishScanPaths(subLog); } @@ -550,7 +552,8 @@ URI getURI() { /** * Get the {@link File} for the outermost zipfile of this classpath element. * - * @return The {@link File} for the outermost zipfile of this classpath element. + * @return The {@link File} for the outermost zipfile of this classpath element, or null if this file was + * downloaded from a URL directly to RAM. */ @Override File getFile() { diff --git a/src/main/java/io/github/classgraph/Resource.java b/src/main/java/io/github/classgraph/Resource.java index a38b48b0d..a666e6733 100644 --- a/src/main/java/io/github/classgraph/Resource.java +++ b/src/main/java/io/github/classgraph/Resource.java @@ -395,7 +395,9 @@ public URL getClasspathElementURL() { * * @return The {@link File} for the classpath element package root dir or jar that this {@link Resource} was * found within, or null if this {@link Resource} was found in a module backed by a "jrt:" URI, or a - * module with an unknown location. + * module with an unknown location. May also return null if the classpath element was an http/https URL, + * and the jar was downloaded directly to RAM, rather than to a temp file on disk (e.g. if the temp dir + * is not writeable). */ public File getClasspathElementFile() { return classpathElement.getFile(); 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 0232c47ca..06f6add5c 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java @@ -29,9 +29,11 @@ package nonapi.io.github.classgraph.fastzipfilereader; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; +import java.net.MalformedURLException; import java.net.URL; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; @@ -51,6 +53,7 @@ import io.github.classgraph.ScanResult; import nonapi.io.github.classgraph.concurrency.InterruptionChecker; import nonapi.io.github.classgraph.concurrency.SingletonMap; +import nonapi.io.github.classgraph.concurrency.SingletonMap.NullSingletonException; import nonapi.io.github.classgraph.recycler.Recycler; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.FastPathResolver; @@ -234,13 +237,28 @@ public Entry newInstance(final String nestedJarPathRaw, // If the path starts with "http(s)://", download the jar to a temp file final boolean isRemote = nestedJarPath.startsWith("http://") || nestedJarPath.startsWith("https://"); - File canonicalFile; + PhysicalZipFile physicalZipFile; if (isRemote) { // Jarfile is at http(s) URL if (scanSpec.enableRemoteJarScanning) { - canonicalFile = downloadTempFile(nestedJarPath, log); - if (canonicalFile == null) { - throw new IOException("Could not download jarfile " + nestedJarPath); + try { + final File canonicalFile = downloadJarFromURLToTempFile(nestedJarPath, log); + if (canonicalFile == null) { + throw new IOException("Could not download jarfile " + nestedJarPath); + } + physicalZipFile = canonicalFileToPhysicalZipFile(canonicalFile, log); + } catch (final IllegalArgumentException e) { + // Temp file could not be written, so this is a read-only filesystem, + // or temp dir does not exist, or temp dir has run out of space. + // Download the jar to a ByteBuffer in RAM instead. + if (log != null) { + log.log("Could not download jar to temp file, attempting to download " + + "to RAM instead: " + e); + } + final ByteBuffer byteBuffer = downloadJarFromURLToByteBuffer(nestedJarPath, + log); + physicalZipFile = new PhysicalZipFile(byteBuffer, /* outermostFile = */ null, + nestedJarPath, NestedJarHandler.this); } } else { throw new IOException( @@ -250,27 +268,13 @@ public Entry newInstance(final String nestedJarPathRaw, } else { // Jarfile should be local try { - canonicalFile = new File(nestedJarPath).getCanonicalFile(); + final File canonicalFile = new File(nestedJarPath).getCanonicalFile(); + physicalZipFile = canonicalFileToPhysicalZipFile(canonicalFile, log); } catch (final SecurityException e) { throw new IOException( "Path component " + nestedJarPath + " could not be canonicalized: " + e); } } - if (!FileUtils.canRead(canonicalFile)) { - throw new IOException("Path component " + nestedJarPath + " does not exist"); - } - if (!canonicalFile.isFile()) { - throw new IOException( - "Path component " + nestedJarPath + " is not a file (expected a jarfile)"); - } - - // Get or create a PhysicalZipFile instance for the canonical file - PhysicalZipFile physicalZipFile; - try { - physicalZipFile = canonicalFileToPhysicalZipFileMap.get(canonicalFile, log); - } catch (final NullSingletonException e) { - throw new IOException("Could not get physical zipfile " + canonicalFile + " : " + e); - } // Create a new logical slice of the whole physical zipfile final ZipFileSlice topLevelSlice = new ZipFileSlice(physicalZipFile); @@ -468,6 +472,37 @@ public NestedJarHandler(final ScanSpec scanSpec, final InterruptionChecker inter // ------------------------------------------------------------------------------------------------------------- + /** + * Get the {@link PhysicalZipFile} for a cononical {@link File}. + * + * @param canonicalFile + * the canonical file + * @param log + * the log + * @return the physical zip file + * @throws IOException + * If the {@link File} could not be read, or was not a file. + * @throws InterruptedException + * If the thread was interrupted. + */ + private PhysicalZipFile canonicalFileToPhysicalZipFile(final File canonicalFile, final LogNode log) + throws IOException, InterruptedException { + if (!FileUtils.canRead(canonicalFile)) { + throw new FileNotFoundException("Cannot read " + canonicalFile); + } + if (!canonicalFile.isFile()) { + throw new IOException("Not a file (expected a jarfile): " + canonicalFile); + } + try { + // Get or create a PhysicalZipFile instance for the canonical file + return canonicalFileToPhysicalZipFileMap.get(canonicalFile, log); + } catch (final NullSingletonException e) { + throw new IOException("Could not get physical zipfile " + canonicalFile + " : " + e); + } + } + + // ------------------------------------------------------------------------------------------------------------- + /** * Get the leafname of a path. * @@ -518,32 +553,75 @@ private File makeTempFile(final String filePath, final boolean onlyUseLeafname) * @param log * the log * @return the temporary file the jar was downloaded to + * @throws IOException + * If the jar could not be downloaded, or the jar URL is malformed. + * @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.) */ - private File downloadTempFile(final String jarURL, final LogNode log) { - final LogNode subLog = log == null ? null : log.log(jarURL, "Downloading URL " + jarURL); - File tempFile; + private File downloadJarFromURLToTempFile(final String jarURL, final LogNode log) throws IOException { + final LogNode subLog = log == null ? null : log.log(jarURL, "Downloading jar from URL " + jarURL); + final URL url; + try { + url = new URL(jarURL); + } catch (final MalformedURLException e) { + throw new IOException("Malformed URL: " + jarURL); + } + final InputStream inputStream = url.openStream(); // Will throw IOException if URL can't be opened + File tempFile = null; try { tempFile = makeTempFile(jarURL, /* onlyUseLeafname = */ true); - final URL url = new URL(jarURL); - try (InputStream inputStream = url.openStream()) { - Files.copy(inputStream, tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + Files.copy(inputStream, tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + } catch (final SecurityException | UnsupportedOperationException | IOException e) { + // Re-throw as IllegalArgumentException if the jar can't be copied to a temp file + throw new IllegalArgumentException( + "Could not download " + jarURL + (tempFile == null ? "" : " to file " + tempFile) + " : " + e); + } finally { + if (inputStream != null) { + inputStream.close(); } if (subLog != null) { subLog.addElapsedTime(); + subLog.log("Downloaded jar to temporary file " + tempFile); + subLog.log("***** Note that it is time-consuming to scan jars at http(s) addresses, " + + "they must be downloaded for every scan, and the same jars must also be " + + "separately downloaded by the ClassLoader *****"); } - } catch (final IOException | SecurityException e) { + } + return tempFile; + } + + /** + * Download a jar from a URL to a ByteBuffer. + * + * @param jarURL + * the jar URL + * @param log + * the log + * @return the {@link ByteBuffer} the jar was downloaded to + * @throws IOException + * If the jar could not be downloaded, or the jar URL is malformed. + */ + private ByteBuffer downloadJarFromURLToByteBuffer(final String jarURL, final LogNode log) throws IOException { + final LogNode subLog = log == null ? null + : log.log(jarURL, "Downloading jar from URL " + jarURL + " to ByteBuffer"); + final URL url; + try { + url = new URL(jarURL); + } catch (final MalformedURLException e) { + throw new IOException("Malformed URL: " + jarURL); + } + try (final InputStream inputStream = url.openStream()) { + return FileUtils.readAllBytesAsByteBuffer(inputStream, -1L); + } finally { if (subLog != null) { - subLog.log("Could not download " + jarURL, e); + subLog.addElapsedTime(); + subLog.log("***** Note that it is time-consuming to scan jars at http(s) addresses, " + + "they must be downloaded for every scan, and the same jars must also be " + + "separately downloaded by the ClassLoader *****"); } - return null; } - if (subLog != null) { - subLog.log("Downloaded to temporary file " + tempFile); - subLog.log("***** Note that it is time-consuming to scan jars at http(s) addresses, " - + "they must be downloaded for every scan, and the same jars must also be " - + "separately downloaded by the ClassLoader *****"); - } - return tempFile; } // ------------------------------------------------------------------------------------------------------------- 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 2a0adaf4a..8a91257bf 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/PhysicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/PhysicalZipFile.java @@ -36,6 +36,7 @@ import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; +import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; import nonapi.io.github.classgraph.concurrency.SingletonMap; @@ -220,7 +221,8 @@ ByteBuffer getByteBuffer(final int chunkIdx) throws IOException, InterruptedExce /** * Get the {@link File} for the outermost jar file of this PhysicalZipFile. * - * @return the {@link File} for the outermost jar file of this PhysicalZipFile. + * @return the {@link File} for the outermost jar file of this PhysicalZipFile, or null if this file was + * downloaded from a URL directly to RAM. */ public File getFile() { return file; @@ -242,7 +244,7 @@ public String getPath() { */ @Override public int hashCode() { - return file.hashCode(); + return file == null ? 0 : file.hashCode(); } /* (non-Javadoc) @@ -255,7 +257,7 @@ public boolean equals(final Object obj) { } else if (!(obj instanceof PhysicalZipFile)) { return false; } - return file.equals(((PhysicalZipFile) obj).file); + return Objects.equals(file, ((PhysicalZipFile) obj).file); } /* (non-Javadoc) diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSlice.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSlice.java index ea4238aa6..85aeab74b 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSlice.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSlice.java @@ -199,7 +199,8 @@ public String getPath() { /** * Get the physical {@link File} that this ZipFileSlice is a slice of. * - * @return the physical {@link File} that this ZipFileSlice is a slice of. + * @return the physical {@link File} that this ZipFileSlice is a slice of, or null if this file was downloaded + * from a URL directly to RAM. */ public File getPhysicalFile() { return physicalZipFile.getFile(); @@ -233,8 +234,11 @@ public boolean equals(final Object obj) { */ @Override public String toString() { - return (physicalZipFile.isDeflatedToRam ? "[ByteBuffer deflated to RAM from " + getPath() + "]" - : physicalZipFile.getFile()) + " [byte range " + startOffsetWithinPhysicalZipFile + ".." + return "[" + + (physicalZipFile.isDeflatedToRam ? "ByteBuffer deflated to RAM from " + getPath() + : physicalZipFile.getFile() == null ? "ByteBuffer downloaded to RAM from " + getPath() + : physicalZipFile.getFile()) + + " ; byte range: " + startOffsetWithinPhysicalZipFile + ".." + (startOffsetWithinPhysicalZipFile + len) + " / " + physicalZipFile.fileLen + "]"; } } \ 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 1ace95b3a..147552ec6 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -227,20 +227,36 @@ public static byte[] readAllBytesAsArray(final InputStream inputStream, final lo return (buf.length == bufBytesUsed) ? buf : Arrays.copyOf(buf, bufBytesUsed); } + /** + * Read all the bytes in an {@link InputStream} as a {@link ByteBuffer}. + * + * @param inputStream + * The {@link InputStream}. + * @param fileSizeHint + * The file size, if known, otherwise -1L. + * @return The contents of the {@link InputStream} as a {@link ByteBuffer}. + * @throws IOException + * If the contents could not be read. + */ + public static ByteBuffer readAllBytesAsByteBuffer(final InputStream inputStream, final long fileSizeHint) + throws IOException { + return ByteBuffer.wrap(readAllBytesAsArray(inputStream, fileSizeHint)); + } + /** * Read all the bytes in an {@link InputStream} as a String. * * @param inputStream * The {@link InputStream}. - * @param fileSize + * @param fileSizeHint * The file size, if known, otherwise -1L. * @return The contents of the {@link InputStream} as a String. * @throws IOException * If the contents could not be read. */ - public static String readAllBytesAsString(final InputStream inputStream, final long fileSize) + public static String readAllBytesAsString(final InputStream inputStream, final long fileSizeHint) throws IOException { - final SimpleEntry ent = readAllBytes(inputStream, fileSize); + final SimpleEntry ent = readAllBytes(inputStream, fileSizeHint); final byte[] buf = ent.getKey(); final int bufBytesUsed = ent.getValue(); return new String(buf, 0, bufBytesUsed, StandardCharsets.UTF_8); From 9201d8979e32d24eca4a3ce2bbc8dc5501453412 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 7 May 2019 14:03:33 -0600 Subject: [PATCH 0157/1778] [maven-release-plugin] prepare release classgraph-4.8.30 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 9e3add6f8..383a84ff4 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.30-SNAPSHOT + 4.8.30 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.30 From 3d4eed691ae9ae8ad4dce48aba6a7154de5ae30a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 7 May 2019 14:03:40 -0600 Subject: [PATCH 0158/1778] [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 383a84ff4..834bc3640 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.30 + 4.8.31-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.30 + HEAD From 85c1928745fd3308b6a1929a3ae9a6354afb83dc Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 7 May 2019 17:59:25 -0600 Subject: [PATCH 0159/1778] Refactoring and cleanup of changes in previous release --- .../fastzipfilereader/NestedJarHandler.java | 130 +++++++----------- 1 file changed, 49 insertions(+), 81 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 06f6add5c..55e5f8396 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java @@ -77,6 +77,12 @@ public PhysicalZipFile newInstance(final File canonicalFile, final LogNode log) throw ClassGraphException .newClassGraphException(NestedJarHandler.class.getSimpleName() + " already closed"); } + if (!FileUtils.canRead(canonicalFile)) { + throw new FileNotFoundException("Cannot read " + canonicalFile); + } + if (!canonicalFile.isFile()) { + throw new IOException("Not a file (expected a jarfile): " + canonicalFile); + } return new PhysicalZipFile(canonicalFile, NestedJarHandler.this); } }; @@ -126,12 +132,7 @@ public ZipFileSlice newInstance(final FastZipEntry childZipEntry, final LogNode } // Get or create a PhysicalZipFile instance for the new temp file - PhysicalZipFile physicalZipFile; - try { - physicalZipFile = canonicalFileToPhysicalZipFileMap.get(tempFile, log); - } catch (final NullSingletonException e) { - throw new IOException("Could not get physical zipfile " + tempFile + " : " + e); - } + final PhysicalZipFile physicalZipFile = canonicalFileToPhysicalZipFile(tempFile, log); additionalAllocatedPhysicalZipFiles.add(physicalZipFile); // Create a new logical slice of the whole physical zipfile @@ -234,39 +235,23 @@ public Entry newInstance(final String nestedJarPathRaw, // 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(s)://", download the jar to a temp file + // If the path starts with "http://" or "https://", download the jar to a temp file + // or to a ByteBuffer in RAM final boolean isRemote = nestedJarPath.startsWith("http://") || nestedJarPath.startsWith("https://"); PhysicalZipFile physicalZipFile; if (isRemote) { - // Jarfile is at http(s) URL + // Jarfile is at an http:// or https:// URL if (scanSpec.enableRemoteJarScanning) { - try { - final File canonicalFile = downloadJarFromURLToTempFile(nestedJarPath, log); - if (canonicalFile == null) { - throw new IOException("Could not download jarfile " + nestedJarPath); - } - physicalZipFile = canonicalFileToPhysicalZipFile(canonicalFile, log); - } catch (final IllegalArgumentException e) { - // Temp file could not be written, so this is a read-only filesystem, - // or temp dir does not exist, or temp dir has run out of space. - // Download the jar to a ByteBuffer in RAM instead. - if (log != null) { - log.log("Could not download jar to temp file, attempting to download " - + "to RAM instead: " + e); - } - final ByteBuffer byteBuffer = downloadJarFromURLToByteBuffer(nestedJarPath, - log); - physicalZipFile = new PhysicalZipFile(byteBuffer, /* outermostFile = */ null, - nestedJarPath, NestedJarHandler.this); - } + // Download jar to a temp file, or if not possible, to a ByteBuffer in RAM + physicalZipFile = downloadJarFromURL(nestedJarPath, log); } else { throw new IOException( "Remote jar scanning has not been enabled, cannot scan classpath element: " + nestedJarPath); } } else { - // Jarfile should be local + // Jarfile should be a local file try { final File canonicalFile = new File(nestedJarPath).getCanonicalFile(); physicalZipFile = canonicalFileToPhysicalZipFile(canonicalFile, log); @@ -487,12 +472,6 @@ public NestedJarHandler(final ScanSpec scanSpec, final InterruptionChecker inter */ private PhysicalZipFile canonicalFileToPhysicalZipFile(final File canonicalFile, final LogNode log) throws IOException, InterruptedException { - if (!FileUtils.canRead(canonicalFile)) { - throw new FileNotFoundException("Cannot read " + canonicalFile); - } - if (!canonicalFile.isFile()) { - throw new IOException("Not a file (expected a jarfile): " + canonicalFile); - } try { // Get or create a PhysicalZipFile instance for the canonical file return canonicalFileToPhysicalZipFileMap.get(canonicalFile, log); @@ -546,13 +525,15 @@ private File makeTempFile(final String filePath, final boolean onlyUseLeafname) } /** - * Download a jar from a URL to a temporary file. + * 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 * @param log * the log - * @return the temporary file the jar was downloaded to + * @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. * @throws IllegalArgumentException @@ -560,7 +541,8 @@ private File makeTempFile(final String filePath, final boolean onlyUseLeafname) * 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 File downloadJarFromURLToTempFile(final String jarURL, final LogNode log) throws IOException { + private PhysicalZipFile downloadJarFromURL(final String jarURL, final LogNode log) + throws IOException, InterruptedException { final LogNode subLog = log == null ? null : log.log(jarURL, "Downloading jar from URL " + jarURL); final URL url; try { @@ -568,52 +550,38 @@ private File downloadJarFromURLToTempFile(final String jarURL, final LogNode log } catch (final MalformedURLException e) { throw new IOException("Malformed URL: " + jarURL); } - final InputStream inputStream = url.openStream(); // Will throw IOException if URL can't be opened - File tempFile = null; - try { - tempFile = makeTempFile(jarURL, /* onlyUseLeafname = */ true); - Files.copy(inputStream, tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING); - } catch (final SecurityException | UnsupportedOperationException | IOException e) { - // Re-throw as IllegalArgumentException if the jar can't be copied to a temp file - throw new IllegalArgumentException( - "Could not download " + jarURL + (tempFile == null ? "" : " to file " + tempFile) + " : " + e); - } finally { - if (inputStream != null) { - inputStream.close(); + try (final InputStream inputStream = url.openStream()) { + PhysicalZipFile physicalZipFile = null; + try { + // Download jar from inputStream to a temporary file + final File tempFile = makeTempFile(jarURL, /* onlyUseLeafname = */ true); + Files.copy(inputStream, tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + if (subLog != null) { + subLog.log("Downloaded jar to temporary file " + tempFile); + } + // Wrap temp file in a PhysicalZipFile + physicalZipFile = canonicalFileToPhysicalZipFile(tempFile, log); + // Add temp file to queue of physical zipfiles that have to be unmapped on close + additionalAllocatedPhysicalZipFiles.add(physicalZipFile); + } catch (final SecurityException | UnsupportedOperationException | IOException e) { + // Temp file could not be written, so this is a read-only filesystem, or temp dir does not exist, + // or temp dir has run out of space. Download the jar to a ByteBuffer in RAM instead. + if (log != null) { + log.log("Could not download jar to temp file (" + e + "), downloading to ByteBuffer instead"); + } + // Read inputStream into a ByteBuffer + final ByteBuffer byteBuffer = FileUtils.readAllBytesAsByteBuffer(inputStream, -1L); + // Wrap ByteBuffer in a PhysicalZipFile. (No need to add to additionalAllocatedPhysicalZipFiles, + // since byteBuffer is not a DirectByteBuffer, it just wraps a standard Java byte array, so the + // DirectByteBuffer cleaner does not need to be called on close.) + physicalZipFile = new PhysicalZipFile(byteBuffer, /* outermostFile = */ null, jarURL, + NestedJarHandler.this); } - if (subLog != null) { - subLog.addElapsedTime(); - subLog.log("Downloaded jar to temporary file " + tempFile); - subLog.log("***** Note that it is time-consuming to scan jars at http(s) addresses, " - + "they must be downloaded for every scan, and the same jars must also be " - + "separately downloaded by the ClassLoader *****"); + if (physicalZipFile == null) { + // Should not happen + throw new RuntimeException("physicalZipFile should not be null"); } - } - return tempFile; - } - - /** - * Download a jar from a URL to a ByteBuffer. - * - * @param jarURL - * the jar URL - * @param log - * the log - * @return the {@link ByteBuffer} the jar was downloaded to - * @throws IOException - * If the jar could not be downloaded, or the jar URL is malformed. - */ - private ByteBuffer downloadJarFromURLToByteBuffer(final String jarURL, final LogNode log) throws IOException { - final LogNode subLog = log == null ? null - : log.log(jarURL, "Downloading jar from URL " + jarURL + " to ByteBuffer"); - final URL url; - try { - url = new URL(jarURL); - } catch (final MalformedURLException e) { - throw new IOException("Malformed URL: " + jarURL); - } - try (final InputStream inputStream = url.openStream()) { - return FileUtils.readAllBytesAsByteBuffer(inputStream, -1L); + return physicalZipFile; } finally { if (subLog != null) { subLog.addElapsedTime(); From 00e214b56c3bf25700deb796cab4e7979be8a2cf Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 7 May 2019 18:02:10 -0600 Subject: [PATCH 0160/1778] Remove duplicate checks --- .../classgraph/fastzipfilereader/NestedJarHandler.java | 7 ------- .../classgraph/fastzipfilereader/PhysicalZipFile.java | 5 +---- 2 files changed, 1 insertion(+), 11 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 55e5f8396..2c2aadb9c 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.File; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; @@ -77,12 +76,6 @@ public PhysicalZipFile newInstance(final File canonicalFile, final LogNode log) throw ClassGraphException .newClassGraphException(NestedJarHandler.class.getSimpleName() + " already closed"); } - if (!FileUtils.canRead(canonicalFile)) { - throw new FileNotFoundException("Cannot read " + canonicalFile); - } - if (!canonicalFile.isFile()) { - throw new IOException("Not a file (expected a jarfile): " + canonicalFile); - } return new PhysicalZipFile(canonicalFile, NestedJarHandler.this); } }; 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 8a91257bf..40e5b1ed8 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/PhysicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/PhysicalZipFile.java @@ -96,11 +96,8 @@ class PhysicalZipFile implements Closeable { path = FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, file.getPath()); - if (!file.exists()) { - throw new IOException("File does not exist: " + file); - } if (!FileUtils.canRead(file)) { - throw new IOException("Cannot read file: " + file); + throw new FileNotFoundException("File does not exist or cannot be read: " + file); } if (!file.isFile()) { throw new IOException("Is not a file: " + file); From ca97cef934661e3e36324d3eb9b9c95aac1ecbac Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 7 May 2019 18:19:48 -0600 Subject: [PATCH 0161/1778] Refactoring and cleanup --- .../classgraph/ClasspathElementDir.java | 6 +- .../classgraph/classpath/SystemJarFinder.java | 2 +- .../fastzipfilereader/PhysicalZipFile.java | 14 ++- .../io/github/classgraph/utils/FileUtils.java | 85 ++++++++++++++++++- 4 files changed, 93 insertions(+), 14 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementDir.java b/src/main/java/io/github/classgraph/ClasspathElementDir.java index ec4ad2115..22297e7bc 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementDir.java @@ -97,7 +97,7 @@ void open(final WorkQueue workQueue, final LogNode log) int childClasspathEntryIdx = 0; for (final String libDirPrefix : ClassLoaderHandlerRegistry.AUTOMATIC_LIB_DIR_PREFIXES) { final File libDir = new File(classpathEltDir, libDirPrefix); - if (libDir.exists() && libDir.isDirectory()) { + if (FileUtils.canReadAndIsDir(libDir)) { // Sort directory entries for consistency final File[] listFiles = libDir.listFiles(); if (listFiles != null) { @@ -120,7 +120,7 @@ void open(final WorkQueue workQueue, final LogNode log) } for (final String packageRootPrefix : ClassLoaderHandlerRegistry.AUTOMATIC_PACKAGE_ROOT_PREFIXES) { final File packageRootDir = new File(classpathEltDir, packageRootPrefix); - if (packageRootDir.exists() && packageRootDir.isDirectory()) { + if (FileUtils.canReadAndIsDir(packageRootDir)) { if (log != null) { log.log("Found package root: " + packageRootDir); } @@ -284,7 +284,7 @@ public synchronized void close() { @Override Resource getResource(final String relativePath) { final File resourceFile = new File(classpathEltDir, relativePath); - return resourceFile.canRead() && resourceFile.isFile() ? newResource(relativePath, resourceFile) : null; + return FileUtils.canReadAndIsFile(resourceFile) ? newResource(relativePath, resourceFile) : null; } /** 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 206737d94..0f44cb106 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/SystemJarFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/SystemJarFinder.java @@ -64,7 +64,7 @@ private SystemJarFinder() { * @return true if the directory was readable. */ private static boolean addJREPath(final File dir) { - if (dir != null && !dir.getPath().isEmpty() && FileUtils.canRead(dir) && dir.isDirectory()) { + if (dir != null && !dir.getPath().isEmpty() && FileUtils.canReadAndIsDir(dir)) { final File[] dirFiles = dir.listFiles(); if (dirFiles != null) { for (final File file : dirFiles) { 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 40e5b1ed8..f4100ee18 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/PhysicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/PhysicalZipFile.java @@ -91,18 +91,14 @@ class PhysicalZipFile implements Closeable { * if an I/O exception occurs. */ PhysicalZipFile(final File file, final NestedJarHandler nestedJarHandler) throws IOException { + // Make sure the File is readable and is a regular file + FileUtils.checkCanReadAndIsFile(file); + this.file = file; this.nestedJarHandler = nestedJarHandler; + this.path = FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, file.getPath()); - path = FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, file.getPath()); - - if (!FileUtils.canRead(file)) { - throw new FileNotFoundException("File does not exist or cannot be read: " + file); - } - if (!file.isFile()) { - throw new IOException("Is not a file: " + file); - } - + // Open the File as a RandomAccessFile, and open a FileChannel on the RandomAccessFile try { raf = new RandomAccessFile(file, "r"); fileLen = raf.length(); 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 147552ec6..b27b9e718 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -29,6 +29,7 @@ package nonapi.io.github.classgraph.utils; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Field; @@ -419,7 +420,7 @@ public static boolean isClassfile(final String path) { // ------------------------------------------------------------------------------------------------------------- /** - * Check if the file exists and can be read. + * Check if a {@link File} exists and can be read. * * @param file * A {@link File}. @@ -433,6 +434,88 @@ public static boolean canRead(final File file) { } } + /** + * Check if a {@link File} exists, is a regular file, and can be read. + * + * @param file + * A {@link File}. + * @return true if the file exists, is a regular file, and can be read. + */ + public static boolean canReadAndIsFile(final File file) { + try { + if (!file.canRead()) { + return false; + } + } catch (final SecurityException e) { + return false; + } + if (!file.isFile()) { + return false; + } + return true; + } + + /** + * Check if a {@link File} exists, is a regular file, and can be read. + * + * @param file + * A {@link File}. + * @throw IOException if the file does not exist, is not a regular file, or cannot be read. + */ + public static void checkCanReadAndIsFile(final File file) throws IOException { + try { + if (!file.canRead()) { + throw new FileNotFoundException("File does not exist or cannot be read: " + file); + } + } catch (final SecurityException e) { + throw new FileNotFoundException("File " + file + " cannot be accessed: " + e); + } + if (!file.isFile()) { + throw new IOException("Not a regular file: " + file); + } + } + + /** + * Check if a {@link File} exists, is a directory, and can be read. + * + * @param file + * A {@link File}. + * @return true if the file exists, is a directory, and can be read. + */ + public static boolean canReadAndIsDir(final File file) { + try { + if (!file.canRead()) { + return false; + } + } catch (final SecurityException e) { + return false; + } + if (!file.isDirectory()) { + return false; + } + return true; + } + + /** + * Check if a {@link File} exists, is a directory, and can be read. + * + * @param file + * A {@link File}. + * @throw IOException if the file does not exist, is not a directory, or cannot be read. + */ + public static void checkCanReadAndIsDir(final File file) throws IOException { + try { + if (!file.canRead()) { + throw new FileNotFoundException("Directory does not exist or cannot be read: " + file); + } + } catch (final SecurityException e) { + throw new FileNotFoundException("File " + file + " cannot be accessed: " + e); + } + if (!file.isDirectory()) { + throw new IOException("Not a directory: " + file); + } + } + // ------------------------------------------------------------------------------------------------------------- /** From 1e016de38b4f6c0cf00d7a426bcbc63aec880484 Mon Sep 17 00:00:00 2001 From: Andrew Shcheglov Date: Wed, 8 May 2019 06:23:35 +0300 Subject: [PATCH 0162/1778] Fixed exception during loading of method with array-type arguments --- .../classgraph/AnnotationEnumValue.java | 3 ++ .../github/classgraph/ArrayTypeSignature.java | 15 ++++++--- .../java/io/github/classgraph/MethodInfo.java | 2 +- .../github/classgraph/ScanResultObject.java | 7 +++- .../test/methodinfo/MethodInfoTest.java | 33 ++++++++++++++++--- 5 files changed, 50 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/github/classgraph/AnnotationEnumValue.java b/src/main/java/io/github/classgraph/AnnotationEnumValue.java index f9f42521e..5d88138c9 100644 --- a/src/main/java/io/github/classgraph/AnnotationEnumValue.java +++ b/src/main/java/io/github/classgraph/AnnotationEnumValue.java @@ -105,6 +105,9 @@ public String getName() { */ public Object loadClassAndReturnEnumValue(final boolean ignoreExceptions) throws IllegalArgumentException { final Class classRef = super.loadClass(ignoreExceptions); + if (classRef == null) { + return null; + } if (!classRef.isEnum()) { throw new IllegalArgumentException("Class " + className + " is not an enum"); } diff --git a/src/main/java/io/github/classgraph/ArrayTypeSignature.java b/src/main/java/io/github/classgraph/ArrayTypeSignature.java index bf7fce372..fab38a48d 100644 --- a/src/main/java/io/github/classgraph/ArrayTypeSignature.java +++ b/src/main/java/io/github/classgraph/ArrayTypeSignature.java @@ -40,6 +40,9 @@ public class ArrayTypeSignature extends ReferenceTypeSignature { /** The number of array dimensions. */ private final int numDims; + + /** Class name. */ + private final String className; // ------------------------------------------------------------------------------------------------------------- @@ -50,11 +53,14 @@ public class ArrayTypeSignature extends ReferenceTypeSignature { * The type signature of the array elements. * @param numDims * The number of array dimensions. + * @param className + * Raw name of array type (like "[[B") */ - ArrayTypeSignature(final TypeSignature elementTypeSignature, final int numDims) { + ArrayTypeSignature(final TypeSignature elementTypeSignature, final int numDims, final String className) { super(); this.elementTypeSignature = elementTypeSignature; this.numDims = numDims; + this.className = className; } /** @@ -82,8 +88,7 @@ public int getNumDimensions() { */ @Override protected String getClassName() { - // getClassInfo() is not valid for this type, so getClassName() does not need to be implemented - throw new IllegalArgumentException("getClassName() cannot be called here"); + return className; } /* (non-Javadoc) @@ -180,6 +185,7 @@ protected String toStringInternal(final boolean useSimpleNames) { */ static ArrayTypeSignature parse(final Parser parser, final String definingClassName) throws ParseException { int numArrayDims = 0; + int begin = parser.getPosition(); while (parser.peek() == '[') { numArrayDims++; parser.next(); @@ -189,7 +195,8 @@ static ArrayTypeSignature parse(final Parser parser, final String definingClassN if (elementTypeSignature == null) { throw new ParseException(parser, "elementTypeSignature == null"); } - return new ArrayTypeSignature(elementTypeSignature, numArrayDims); + CharSequence cn = parser.getSubsequence(begin, parser.getPosition()); + return new ArrayTypeSignature(elementTypeSignature, numArrayDims, cn.toString()); } else { return null; } diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index dd17eb507..b86c10603 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -842,7 +842,7 @@ public String toString() { "Got a zero-dimension array type for last parameter of varargs method " + name); } buf.append(new ArrayTypeSignature(arrayType.getElementTypeSignature(), - arrayType.getNumDimensions() - 1).toString()); + arrayType.getNumDimensions() - 1, arrayType.getClassName()).toString()); buf.append("..."); } else { buf.append(paramType.toString()); diff --git a/src/main/java/io/github/classgraph/ScanResultObject.java b/src/main/java/io/github/classgraph/ScanResultObject.java index a4ab82e4f..968a7e357 100644 --- a/src/main/java/io/github/classgraph/ScanResultObject.java +++ b/src/main/java/io/github/classgraph/ScanResultObject.java @@ -111,7 +111,12 @@ ClassInfo getClassInfo() { */ private String getClassInfoNameOrClassName() { String className; - ClassInfo ci = getClassInfo(); + ClassInfo ci = null; + try { + ci = getClassInfo(); + } catch(IllegalArgumentException e) { + // Just ignore wrong access to array classInfo + } if (ci == null) { ci = classInfo; } 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 0656e3c16..192369175 100644 --- a/src/test/java/io/github/classgraph/test/methodinfo/MethodInfoTest.java +++ b/src/test/java/io/github/classgraph/test/methodinfo/MethodInfoTest.java @@ -29,7 +29,9 @@ package io.github.classgraph.test.methodinfo; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import java.lang.reflect.Method; import java.util.List; import org.junit.Test; @@ -103,7 +105,7 @@ public void testGetMethodInfo() { @Override public boolean accept(final MethodInfo methodInfo) { // JDK 10 fix - return !methodInfo.getName().equals("$closeResource"); + return !methodInfo.getName().equals("$closeResource") && !methodInfo.getName().equals("lambda$0") && !methodInfo.isSynthetic(); } }).getAsStrings()).containsExactlyInAnyOrder( // "@" + ExternalAnnotation.class.getName() // @@ -118,7 +120,10 @@ public boolean accept(final MethodInfo methodInfo) { "@" + Test.class.getName() + "(expected=class org.junit.Test$None, timeout=0) " + "public void testGetConstructorInfo()", "@" + Test.class.getName() + "(expected=class org.junit.Test$None, timeout=0) " - + "public void testGetMethodInfoIgnoringVisibility()"); + + "public void testGetMethodInfoIgnoringVisibility()", + "@" + Test.class.getName() + "(expected=class org.junit.Test$None, timeout=0) " + + "public void testMethodInfoLoadMethodForArrayArg()" + ); } } @@ -146,7 +151,7 @@ public void testGetMethodInfoIgnoringVisibility() { @Override public boolean accept(final MethodInfo methodInfo) { // JDK 10 fix - return !methodInfo.getName().equals("$closeResource"); + return !methodInfo.getName().equals("$closeResource") && !methodInfo.getName().equals("lambda$0") && !methodInfo.isSynthetic(); } }).getAsStrings()).containsExactlyInAnyOrder( // "@" + ExternalAnnotation.class.getName() // @@ -162,7 +167,27 @@ public boolean accept(final MethodInfo methodInfo) { "@" + Test.class.getName() + "(expected=class org.junit.Test$None, timeout=0) " + "public void testGetConstructorInfo()", "@" + Test.class.getName() + "(expected=class org.junit.Test$None, timeout=0) " - + "public void testGetMethodInfoIgnoringVisibility()"); + + "public void testGetMethodInfoIgnoringVisibility()", + "@" + Test.class.getName() + "(expected=class org.junit.Test$None, timeout=0) " + + "public void testMethodInfoLoadMethodForArrayArg()" + ); + } + } + + /** + * MethodInfo.loadClassAndGetMethod for arrays argument + */ + @Test + public void testMethodInfoLoadMethodForArrayArg() { + try (ScanResult scanResult = new ClassGraph().whitelistPackages(MethodInfoTest.class.getPackage().getName()) + .enableClassInfo().enableMethodInfo().enableAnnotationInfo().scan()) { + MethodInfo mi = scanResult.getClassInfo(MethodInfoTest.class.getName()).getMethodInfo() + .getSingleMethod("publicMethodWithArgs"); + assertThat(mi).isNotNull(); + assertThatCode(() -> { + mi.loadClassAndGetMethod(); + }).doesNotThrowAnyException(); + assertThat(mi.loadClassAndGetMethod()).isNotNull(); } } } From 71653a3a05d4cd924cc61911864bef649634c00f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 8 May 2019 01:20:37 -0600 Subject: [PATCH 0163/1778] Add `ArrayTypeSignature#loadElementClass()` and `ArrayClassInfo` (#344) --- .../classgraph/AnnotationEnumValue.java | 10 +- .../io/github/classgraph/ArrayClassInfo.java | 223 ++++++++++++++++++ .../github/classgraph/ArrayTypeSignature.java | 159 +++++++++++-- .../java/io/github/classgraph/ClassInfo.java | 34 ++- .../java/io/github/classgraph/MethodInfo.java | 2 +- .../github/classgraph/ScanResultObject.java | 41 +++- .../test/methodinfo/MethodInfoTest.java | 82 +++++-- 7 files changed, 497 insertions(+), 54 deletions(-) create mode 100644 src/main/java/io/github/classgraph/ArrayClassInfo.java diff --git a/src/main/java/io/github/classgraph/AnnotationEnumValue.java b/src/main/java/io/github/classgraph/AnnotationEnumValue.java index 5d88138c9..90c6924c1 100644 --- a/src/main/java/io/github/classgraph/AnnotationEnumValue.java +++ b/src/main/java/io/github/classgraph/AnnotationEnumValue.java @@ -105,9 +105,13 @@ public String getName() { */ public Object loadClassAndReturnEnumValue(final boolean ignoreExceptions) throws IllegalArgumentException { final Class classRef = super.loadClass(ignoreExceptions); - if (classRef == null) { - return null; - } + if (classRef == null) { + if (ignoreExceptions) { + return null; + } else { + throw new IllegalArgumentException("Enum class " + className + " could not be loaded"); + } + } if (!classRef.isEnum()) { throw new IllegalArgumentException("Class " + className + " is not an enum"); } diff --git a/src/main/java/io/github/classgraph/ArrayClassInfo.java b/src/main/java/io/github/classgraph/ArrayClassInfo.java new file mode 100644 index 000000000..a50ea7203 --- /dev/null +++ b/src/main/java/io/github/classgraph/ArrayClassInfo.java @@ -0,0 +1,223 @@ +/* + * 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.util.Set; + +/** + * Holds metadata about an array class. This class extends {@link ClassInfo} with additional methods relevant to + * array classes, in particular {@link #getArrayTypeSignature()}, {@link #getTypeSignatureStr()}, + * {@link #getElementTypeSignature()}, {@link #getElementClassInfo()}, {@link #loadElementClass()}, and + * {@link #getNumDimensions()}. + * + *

+ * An {@link ArrayClassInfo} object will not have any methods, fields or annotations. + * {@link ClassInfo#isArrayClass()} will return true for this subclass of {@link ClassInfo}. + */ +public class ArrayClassInfo extends ClassInfo { + /** The array type signature. */ + private ArrayTypeSignature arrayTypeSignature; + + /** The element class info. */ + private ClassInfo elementClassInfo; + + /** Default constructor for deserialization. */ + ArrayClassInfo() { + super(); + } + + /** + * Constructor. + * + * @param arrayTypeSignature + * the array type signature + * @param scanResult + * the scan result + */ + ArrayClassInfo(final ArrayTypeSignature arrayTypeSignature, final ScanResult scanResult) { + super(arrayTypeSignature.getClassName(), /* modifiers = */ 0, /* resource = */ null); + this.arrayTypeSignature = arrayTypeSignature; + this.scanResult = scanResult; + // Pre-load fields from element type + getElementClassInfo(); + } + + // ------------------------------------------------------------------------------------------------------------- + + /** + * Get the raw type signature string of the array class, e.g. "[[I" for "int[][]". + * + * @return The raw type signature string of the array class. + */ + public String getTypeSignatureStr() { + return arrayTypeSignature.getTypeSignatureStr(); + } + + /** + * Returns null, because array classes do not have a ClassTypeSignature. Call {@link #getArrayTypeSignature()} + * instead. + * + * @return null (always). + */ + @Override + public ClassTypeSignature getTypeSignature() { + return null; + } + + /** + * Get the type signature of the class. + * + * @return The class type signature, if available, otherwise returns null. + */ + public ArrayTypeSignature getArrayTypeSignature() { + return arrayTypeSignature; + } + + /** + * Get the type signature of the array elements. + * + * @return The type signature of the array elements. + */ + public TypeSignature getElementTypeSignature() { + return arrayTypeSignature.getElementTypeSignature(); + } + + /** + * Get the number of dimensions of the array. + * + * @return The number of dimensions of the array. + */ + public int getNumDimensions() { + return arrayTypeSignature.getNumDimensions(); + } + + // ------------------------------------------------------------------------------------------------------------- + + /** + * Get the {@link ClassInfo} instance for the array element type. + * + * @return the {@link ClassInfo} instance for the array element type. Returns null if the element type was not + * found during the scan. In particular, will return null for + */ + public ClassInfo getElementClassInfo() { + if (elementClassInfo == null) { + final TypeSignature elementTypeSignature = arrayTypeSignature.getElementTypeSignature(); + if (!(elementTypeSignature instanceof BaseTypeSignature)) { + elementClassInfo = arrayTypeSignature.getElementTypeSignature().getClassInfo(); + if (elementClassInfo != null) { + // Copy over relevant fields from array element ClassInfo + this.classpathElement = elementClassInfo.classpathElement; + this.classfileResource = elementClassInfo.classfileResource; + this.classLoader = elementClassInfo.classLoader; + this.isScannedClass = elementClassInfo.isScannedClass; + this.isExternalClass = elementClassInfo.isExternalClass; + this.moduleInfo = elementClassInfo.moduleInfo; + this.packageInfo = elementClassInfo.packageInfo; + } + } + } + return elementClassInfo; + } + + // ------------------------------------------------------------------------------------------------------------- + + /** + * Get a {@code Class} reference for the array element type. Causes the ClassLoader to load the element + * class, if it is not already loaded. + * + * @param ignoreExceptions + * Whether or not to ignore exceptions. + * @return a {@code Class} reference for the array element type. Also works for arrays of primitive element + * type. + */ + public Class loadElementClass(final boolean ignoreExceptions) { + return arrayTypeSignature.loadElementClass(ignoreExceptions); + } + + /** + * Get a {@code Class} reference for the array element type. Causes the ClassLoader to load the element + * class, if it is not already loaded. + * + * @return a {@code Class} reference for the array element type. Also works for arrays of primitive element + * type. + */ + public Class loadElementClass() { + return arrayTypeSignature.loadElementClass(); + } + + /** + * Obtain a {@code Class} reference for the array class named by this {@link ArrayClassInfo} object. Causes + * the ClassLoader to load the element class, if it is not already loaded. + * + * @param ignoreExceptions + * Whether or not to ignore exceptions + * @return The class reference, or null, if ignoreExceptions is true and there was an exception or error loading + * the class. + * @throws IllegalArgumentException + * if ignoreExceptions is false and there were problems loading the class. + */ + @Override + public Class loadClass(final boolean ignoreExceptions) { + if (classRef == null) { + classRef = arrayTypeSignature.loadClass(ignoreExceptions); + } + return classRef; + } + + /** + * Obtain a {@code Class} reference for the array class named by this {@link ArrayClassInfo} object. Causes + * the ClassLoader to load the element class, if it is not already loaded. + * + * @return The class reference. + * @throws IllegalArgumentException + * if there were problems loading the class. + */ + @Override + public Class loadClass() { + if (classRef == null) { + classRef = arrayTypeSignature.loadClass(); + } + return classRef; + } + + // ------------------------------------------------------------------------------------------------------------- + + /** + * Get the names of any classes referenced in this class' type descriptor, or the type descriptors of fields, + * methods or annotations. + * + * @param referencedClassNames + * the referenced class names + */ + @Override + protected void findReferencedClassNames(final Set referencedClassNames) { + super.findReferencedClassNames(referencedClassNames); + arrayTypeSignature.findReferencedClassNames(referencedClassNames); + } +} diff --git a/src/main/java/io/github/classgraph/ArrayTypeSignature.java b/src/main/java/io/github/classgraph/ArrayTypeSignature.java index fab38a48d..df2d021cf 100644 --- a/src/main/java/io/github/classgraph/ArrayTypeSignature.java +++ b/src/main/java/io/github/classgraph/ArrayTypeSignature.java @@ -28,6 +28,7 @@ */ package io.github.classgraph; +import java.lang.reflect.Array; import java.util.Set; import nonapi.io.github.classgraph.types.ParseException; @@ -40,9 +41,18 @@ public class ArrayTypeSignature extends ReferenceTypeSignature { /** The number of array dimensions. */ private final int numDims; - - /** Class name. */ - private final String className; + + /** The raw type signature string for the array type. */ + private final String typeSignatureStr; + + /** Human-readable class name, e.g. "java.lang.String[]". */ + private String className; + + /** Array class info. */ + private ArrayClassInfo arrayClassInfo; + + /** The element class. */ + private Class elementClassRef; // ------------------------------------------------------------------------------------------------------------- @@ -53,18 +63,27 @@ public class ArrayTypeSignature extends ReferenceTypeSignature { * The type signature of the array elements. * @param numDims * The number of array dimensions. - * @param className - * Raw name of array type (like "[[B") + * @param typeSignatureStr + * Raw array type signature string (e.g. "[[I") */ - ArrayTypeSignature(final TypeSignature elementTypeSignature, final int numDims, final String className) { + ArrayTypeSignature(final TypeSignature elementTypeSignature, final int numDims, final String typeSignatureStr) { super(); this.elementTypeSignature = elementTypeSignature; this.numDims = numDims; - this.className = className; + this.typeSignatureStr = typeSignatureStr; } /** - * Get the element type signature. + * Get the raw array type signature string, e.g. "[[I". + * + * @return the raw array type signature string. + */ + public String getTypeSignatureStr() { + return typeSignatureStr; + } + + /** + * Get the type signature of the array elements. * * @return The type signature of the array elements. */ @@ -73,7 +92,7 @@ public TypeSignature getElementTypeSignature() { } /** - * Get the number of dimensions. + * Get the number of dimensions of the array. * * @return The number of dimensions of the array. */ @@ -88,7 +107,10 @@ public int getNumDimensions() { */ @Override protected String getClassName() { - return className; + if (className == null) { + className = toStringInternal(/* useSimpleNames = */ false); + } + return className; } /* (non-Javadoc) @@ -96,7 +118,19 @@ protected String getClassName() { */ @Override protected ClassInfo getClassInfo() { - throw new IllegalArgumentException("getClassInfo() cannot be called here"); + return getArrayClassInfo(); + } + + /** + * Return an {@link ArrayClassInfo} instance for the array class, cast to its superclass. + * + * @return the {@link ArrayClassInfo} instance. + */ + public ArrayClassInfo getArrayClassInfo() { + if (arrayClassInfo == null && scanResult != null) { + arrayClassInfo = new ArrayClassInfo(this, scanResult); + } + return arrayClassInfo; } /* (non-Javadoc) @@ -120,6 +154,103 @@ void findReferencedClassNames(final Set referencedClassNames) { // ------------------------------------------------------------------------------------------------------------- + /** + * Get a {@code Class} reference for the array element type. Causes the ClassLoader to load the element + * class, if it is not already loaded. + * + * @param ignoreExceptions + * Whether or not to ignore exceptions. + * @return a {@code Class} reference for the array element type. Also works for arrays of primitive element + * type. + */ + public Class loadElementClass(final boolean ignoreExceptions) { + if (elementClassRef == null) { + // Try resolving element type against base types (int, etc.) + if (elementTypeSignature instanceof BaseTypeSignature) { + elementClassRef = ((BaseTypeSignature) elementTypeSignature).getType(); + } else { + if (scanResult != null) { + elementClassRef = elementTypeSignature.loadClass(ignoreExceptions); + } else { + // Fallback, if scanResult is not set + final String elementTypeName = ((ClassRefTypeSignature) elementTypeSignature) + .getFullyQualifiedClassName(); + try { + elementClassRef = Class.forName(elementTypeName); + } catch (final Throwable t) { + if (!ignoreExceptions) { + throw new IllegalArgumentException( + "Could not load array element class " + elementTypeName, t); + } + } + } + } + } + return elementClassRef; + } + + /** + * Get a {@code Class} reference for the array element type. Causes the ClassLoader to load the element + * class, if it is not already loaded. + * + * @return a {@code Class} reference for the array element type. Also works for arrays of primitive element + * type. + */ + public Class loadElementClass() { + return loadElementClass(/* ignoreExceptions = */ false); + } + + /** + * Obtain a {@code Class} reference for the array class named by this {@link ArrayClassInfo} object. Causes + * the ClassLoader to load the element class, if it is not already loaded. + * + * @param ignoreExceptions + * Whether or not to ignore exceptions. + * @return The class reference, or null, if ignoreExceptions is true and there was an exception or error loading + * the class. + * @throws IllegalArgumentException + * if ignoreExceptions is false and there were problems loading the class. + */ + @Override + public Class loadClass(final boolean ignoreExceptions) { + if (classRef == null) { + // Get the element type + Class eltClassRef = null; + if (ignoreExceptions) { + try { + eltClassRef = loadElementClass(); + } catch (final IllegalArgumentException e) { + return null; + } + } else { + eltClassRef = loadElementClass(); + } + if (eltClassRef == null) { + throw new IllegalArgumentException("Could not load array element class " + elementTypeSignature); + } + // Create an array of the target number of dimensions, with size zero in each dimension + final Object eltArrayInstance = Array.newInstance(eltClassRef, new int[numDims]); + // Get the class reference from the array instance + classRef = eltArrayInstance.getClass(); + } + return classRef; + } + + /** + * Obtain a {@code Class} reference for the array class named by this {@link ArrayClassInfo} object. Causes + * the ClassLoader to load the element class, if it is not already loaded. + * + * @return The class reference. + * @throws IllegalArgumentException + * if there were problems loading the class. + */ + @Override + public Class loadClass() { + return loadClass(/* ignoreExceptions = */ false); + } + + // ------------------------------------------------------------------------------------------------------------- + /* (non-Javadoc) * @see java.lang.Object#hashCode() */ @@ -185,7 +316,7 @@ protected String toStringInternal(final boolean useSimpleNames) { */ static ArrayTypeSignature parse(final Parser parser, final String definingClassName) throws ParseException { int numArrayDims = 0; - int begin = parser.getPosition(); + final int begin = parser.getPosition(); while (parser.peek() == '[') { numArrayDims++; parser.next(); @@ -195,8 +326,8 @@ static ArrayTypeSignature parse(final Parser parser, final String definingClassN if (elementTypeSignature == null) { throw new ParseException(parser, "elementTypeSignature == null"); } - CharSequence cn = parser.getSubsequence(begin, parser.getPosition()); - return new ArrayTypeSignature(elementTypeSignature, numArrayDims, cn.toString()); + final String typeSignatureStr = parser.getSubsequence(begin, parser.getPosition()).toString(); + return new ArrayTypeSignature(elementTypeSignature, numArrayDims, typeSignatureStr); } else { return null; } diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index 1acb45729..a78ef4ef1 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -56,7 +56,8 @@ /** Holds metadata about a class encountered during a scan. */ public class ClassInfo extends ScanResultObject implements Comparable, HasName { /** The name of the class. */ - private @Id String name; + @Id + protected String name; /** Class modifier flags, e.g. Modifier.PUBLIC */ private int modifiers; @@ -74,7 +75,7 @@ public class ClassInfo extends ScanResultObject implements Comparable boolean isInherited; /** The class type signature string. */ - private String typeSignatureStr; + protected String typeSignatureStr; /** The class type signature, parsed. */ private transient ClassTypeSignature typeSignature; @@ -90,19 +91,19 @@ public class ClassInfo extends ScanResultObject implements Comparable * If false, this classfile was matched during scanning (i.e. its classfile contents read), i.e. this class is a * whitelisted (and non-blacklisted) class in a whitelisted (and non-blacklisted) package. */ - private boolean isExternalClass = true; + protected boolean isExternalClass = true; /** * Set to true when the class is actually scanned (as opposed to just referenced as a superclass, interface or * annotation of a scanned class). */ - private boolean isScannedClass; + protected boolean isScannedClass; /** The classpath element that this class was found within. */ transient ClasspathElement classpathElement; /** The {@link Resource} for the classfile of this class. */ - private transient Resource classfileResource; + protected transient Resource classfileResource; /** The classloader this class was obtained from. */ transient ClassLoader classLoader; @@ -176,7 +177,7 @@ public class ClassInfo extends ScanResultObject implements Comparable * @param classfileResource * the classfile resource */ - private ClassInfo(final String name, final int classModifiers, final Resource classfileResource) { + protected ClassInfo(final String name, final int classModifiers, final Resource classfileResource) { super(); this.name = name; if (name.endsWith(";")) { @@ -1076,6 +1077,16 @@ public boolean isStandardClass() { return !(isAnnotation || isInterface); } + /** + * Checks if this class is an array class. Returns false unless this {@link ClassInfo} is an instance of + * {@link ArrayClassInfo}. + * + * @return true if this is an array class. + */ + public boolean isArrayClass() { + return this instanceof ArrayClassInfo; + } + /** * Checks if this class extends the named superclass. * @@ -2379,6 +2390,9 @@ public ClassTypeSignature getTypeSignature() { * @return The {@link URL} of the classpath element that this class was found within. */ public URL getClasspathElementURL() { + if (classpathElement == null) { + throw new IllegalArgumentException("Classpath element is not known for this classpath element"); + } try { return classpathElement.getURI().toURL(); } catch (final MalformedURLException e) { @@ -2396,6 +2410,9 @@ public URL getClasspathElementURL() { * temp file on disk (e.g. if the temp dir is not writeable). */ public File getClasspathElementFile() { + if (classpathElement == null) { + throw new IllegalArgumentException("Classpath element is not known for this classpath element"); + } return classpathElement.getFile(); } @@ -2407,6 +2424,9 @@ public File getClasspathElementFile() { * in a directory or jar in the classpath. (See also {@link #getClasspathElementFile()}.) */ public ModuleRef getModuleRef() { + if (classpathElement == null) { + throw new IllegalArgumentException("Classpath element is not known for this classpath element"); + } return classpathElement instanceof ClasspathElementModule ? ((ClasspathElementModule) classpathElement).getModuleRef() : null; @@ -2701,7 +2721,7 @@ public int hashCode() { * if true, convert type name to string only. * @return the string */ - private String toString(final boolean typeNameOnly) { + protected String toString(final boolean typeNameOnly) { final ClassTypeSignature typeSig = getTypeSignature(); if (typeSig != null) { // Generic classes diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index b86c10603..997f97eb2 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -842,7 +842,7 @@ public String toString() { "Got a zero-dimension array type for last parameter of varargs method " + name); } buf.append(new ArrayTypeSignature(arrayType.getElementTypeSignature(), - arrayType.getNumDimensions() - 1, arrayType.getClassName()).toString()); + arrayType.getNumDimensions() - 1, /* unused */ null).toString()); buf.append("..."); } else { buf.append(paramType.toString()); diff --git a/src/main/java/io/github/classgraph/ScanResultObject.java b/src/main/java/io/github/classgraph/ScanResultObject.java index 968a7e357..4337b9353 100644 --- a/src/main/java/io/github/classgraph/ScanResultObject.java +++ b/src/main/java/io/github/classgraph/ScanResultObject.java @@ -43,7 +43,7 @@ abstract class ScanResultObject { private transient ClassInfo classInfo; /** The class ref, once the class is loaded. */ - private transient Class classRef; + protected transient Class classRef; /** * Set ScanResult backreferences in info objects after scan has completed. @@ -112,11 +112,11 @@ ClassInfo getClassInfo() { private String getClassInfoNameOrClassName() { String className; ClassInfo ci = null; - try { - ci = getClassInfo(); - } catch(IllegalArgumentException e) { - // Just ignore wrong access to array classInfo - } + try { + ci = getClassInfo(); + } catch (final IllegalArgumentException e) { + // Just ignore wrong access to array classInfo + } if (ci == null) { ci = classInfo; } @@ -151,8 +151,19 @@ private String getClassInfoNameOrClassName() { */ Class loadClass(final Class superclassOrInterfaceType, final boolean ignoreExceptions) { if (classRef == null) { - classRef = scanResult.loadClass(getClassInfoNameOrClassName(), superclassOrInterfaceType, - ignoreExceptions); + 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); + } + } + } } @SuppressWarnings("unchecked") final Class classT = (Class) classRef; @@ -190,7 +201,19 @@ Class loadClass(final Class superclassOrInterfaceType) { */ Class loadClass(final boolean ignoreExceptions) { if (classRef == null) { - classRef = scanResult.loadClass(getClassInfoNameOrClassName(), ignoreExceptions); + final String className = getClassInfoNameOrClassName(); + if (scanResult != null) { + classRef = scanResult.loadClass(className, 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); + } + } + } } return classRef; } 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 192369175..c30351393 100644 --- a/src/test/java/io/github/classgraph/test/methodinfo/MethodInfoTest.java +++ b/src/test/java/io/github/classgraph/test/methodinfo/MethodInfoTest.java @@ -31,21 +31,36 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; -import java.lang.reflect.Method; +import java.util.ArrayList; import java.util.List; import org.junit.Test; +import io.github.classgraph.ArrayClassInfo; +import io.github.classgraph.ArrayTypeSignature; import io.github.classgraph.ClassGraph; import io.github.classgraph.MethodInfo; import io.github.classgraph.MethodInfoList.MethodInfoFilter; +import io.github.classgraph.MethodParameterInfo; import io.github.classgraph.ScanResult; +import io.github.classgraph.TypeSignature; import io.github.classgraph.test.external.ExternalAnnotation; /** * MethodInfoTest. */ public class MethodInfoTest { + /** + * The Class X. + */ + public static class X { + /** + * Method. + */ + public void xMethod() { + } + } + /** * Public method with args. * @@ -67,7 +82,7 @@ public class MethodInfoTest { */ @ExternalAnnotation public final int publicMethodWithArgs(final String str, final char c, final long j, final float[] f, - final byte[][] b, final List l, final int[]... varargs) { + final byte[][] b, final List l, final X[][][] xArray, final String[]... varargs) { return 0; } @@ -105,13 +120,15 @@ public void testGetMethodInfo() { @Override public boolean accept(final MethodInfo methodInfo) { // JDK 10 fix - return !methodInfo.getName().equals("$closeResource") && !methodInfo.getName().equals("lambda$0") && !methodInfo.isSynthetic(); + return !methodInfo.getName().equals("$closeResource") + && !methodInfo.getName().equals("lambda$0") && !methodInfo.isSynthetic(); } }).getAsStrings()).containsExactlyInAnyOrder( // "@" + ExternalAnnotation.class.getName() // + " public final int publicMethodWithArgs" + "(java.lang.String, char, long, float[], byte[][], " - + "java.util.List, int[]...)", + + "java.util.List, " + X.class.getName() + + "[][][], java.lang.String[]...)", "@" + Test.class.getName() + "(expected=class java.lang.IllegalArgumentException, timeout=0) " + "public void methodInfoNotEnabled()", @@ -122,8 +139,7 @@ public boolean accept(final MethodInfo methodInfo) { "@" + Test.class.getName() + "(expected=class org.junit.Test$None, timeout=0) " + "public void testGetMethodInfoIgnoringVisibility()", "@" + Test.class.getName() + "(expected=class org.junit.Test$None, timeout=0) " - + "public void testMethodInfoLoadMethodForArrayArg()" - ); + + "public void testMethodInfoLoadMethodForArrayArg()"); } } @@ -151,13 +167,15 @@ public void testGetMethodInfoIgnoringVisibility() { @Override public boolean accept(final MethodInfo methodInfo) { // JDK 10 fix - return !methodInfo.getName().equals("$closeResource") && !methodInfo.getName().equals("lambda$0") && !methodInfo.isSynthetic(); + return !methodInfo.getName().equals("$closeResource") + && !methodInfo.getName().equals("lambda$0") && !methodInfo.isSynthetic(); } }).getAsStrings()).containsExactlyInAnyOrder( // "@" + ExternalAnnotation.class.getName() // + " public final int publicMethodWithArgs" + "(java.lang.String, char, long, float[], byte[][], " - + "java.util.List, int[]...)", + + "java.util.List, " + X.class.getName() + + "[][][], java.lang.String[]...)", "private static java.lang.String[] privateMethod()", "@" + Test.class.getName() + "(expected=class java.lang.IllegalArgumentException, timeout=0) " @@ -169,25 +187,49 @@ public boolean accept(final MethodInfo methodInfo) { "@" + Test.class.getName() + "(expected=class org.junit.Test$None, timeout=0) " + "public void testGetMethodInfoIgnoringVisibility()", "@" + Test.class.getName() + "(expected=class org.junit.Test$None, timeout=0) " - + "public void testMethodInfoLoadMethodForArrayArg()" - ); + + "public void testMethodInfoLoadMethodForArrayArg()"); } } - + /** - * MethodInfo.loadClassAndGetMethod for arrays argument + * MethodInfo.loadClassAndGetMethod for arrays argument (#344) */ @Test public void testMethodInfoLoadMethodForArrayArg() { - try (ScanResult scanResult = new ClassGraph().whitelistPackages(MethodInfoTest.class.getPackage().getName()) + try (ScanResult scanResult = new ClassGraph().whitelistPackages(MethodInfoTest.class.getPackage().getName()) .enableClassInfo().enableMethodInfo().enableAnnotationInfo().scan()) { - MethodInfo mi = scanResult.getClassInfo(MethodInfoTest.class.getName()).getMethodInfo() - .getSingleMethod("publicMethodWithArgs"); - assertThat(mi).isNotNull(); - assertThatCode(() -> { - mi.loadClassAndGetMethod(); - }).doesNotThrowAnyException(); - assertThat(mi.loadClassAndGetMethod()).isNotNull(); + final MethodInfo mi = scanResult.getClassInfo(MethodInfoTest.class.getName()).getMethodInfo() + .getSingleMethod("publicMethodWithArgs"); + assertThat(mi).isNotNull(); + assertThatCode(() -> { + mi.loadClassAndGetMethod(); + }).doesNotThrowAnyException(); + assertThat(mi.loadClassAndGetMethod()).isNotNull(); + + // Extract array-typed params from method params + final List arrayClassInfoList = new ArrayList<>(); + for (final MethodParameterInfo mpi : mi.getParameterInfo()) { + final TypeSignature paramTypeSig = mpi.getTypeSignatureOrTypeDescriptor(); + if (paramTypeSig instanceof ArrayTypeSignature) { + arrayClassInfoList.add(((ArrayTypeSignature) paramTypeSig).getArrayClassInfo()); + } + } + assertThat(arrayClassInfoList.toString()).isEqualTo("[class float[], class byte[][], " + + "class io.github.classgraph.test.methodinfo.MethodInfoTest$X[][][], " + + "class java.lang.String[][]]"); + final ArrayClassInfo p1 = arrayClassInfoList.get(1); + assertThat(p1.loadElementClass()).isEqualTo(byte.class); + assertThat(p1.getElementClassInfo()).isNull(); + assertThat(p1.getNumDimensions()).isEqualTo(2); + final ArrayClassInfo p2 = arrayClassInfoList.get(2); + assertThat(p2.loadElementClass()).isEqualTo(X.class); + assertThat(p2.getElementClassInfo().getName()).isEqualTo(X.class.getName()); + assertThat(p2.getElementClassInfo().getMethodInfo().get(0).getName()).isEqualTo("xMethod"); + assertThat(p2.getNumDimensions()).isEqualTo(3); + final ArrayClassInfo p3 = arrayClassInfoList.get(3); + assertThat(p3.loadElementClass()).isEqualTo(String.class); + assertThat(p3.getElementClassInfo()).isNull(); + assertThat(p3.getNumDimensions()).isEqualTo(2); } } } From ac7e7cb9175817d76caf4650ec8174558207735d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 8 May 2019 01:23:18 -0600 Subject: [PATCH 0164/1778] [maven-release-plugin] prepare release classgraph-4.8.31 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 834bc3640..58cecb5c5 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.31-SNAPSHOT + 4.8.31 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.31 From 159b326d072118a2fdee20d8f004f6e079024410 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 8 May 2019 01:23:24 -0600 Subject: [PATCH 0165/1778] [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 58cecb5c5..4ca332827 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.31 + 4.8.32-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.31 + HEAD From 01a6bf8858fdb05a9f601f407dfd6c32a6043c7f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 8 May 2019 01:27:08 -0600 Subject: [PATCH 0166/1778] Add some more tests --- .../io/github/classgraph/test/methodinfo/MethodInfoTest.java | 3 +++ 1 file changed, 3 insertions(+) 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 c30351393..07c0966ee 100644 --- a/src/test/java/io/github/classgraph/test/methodinfo/MethodInfoTest.java +++ b/src/test/java/io/github/classgraph/test/methodinfo/MethodInfoTest.java @@ -219,15 +219,18 @@ public void testMethodInfoLoadMethodForArrayArg() { + "class java.lang.String[][]]"); final ArrayClassInfo p1 = arrayClassInfoList.get(1); assertThat(p1.loadElementClass()).isEqualTo(byte.class); + assertThat(p1.loadClass()).isEqualTo(byte[][].class); assertThat(p1.getElementClassInfo()).isNull(); assertThat(p1.getNumDimensions()).isEqualTo(2); final ArrayClassInfo p2 = arrayClassInfoList.get(2); assertThat(p2.loadElementClass()).isEqualTo(X.class); assertThat(p2.getElementClassInfo().getName()).isEqualTo(X.class.getName()); + assertThat(p2.loadClass()).isEqualTo(X[][][].class); assertThat(p2.getElementClassInfo().getMethodInfo().get(0).getName()).isEqualTo("xMethod"); assertThat(p2.getNumDimensions()).isEqualTo(3); final ArrayClassInfo p3 = arrayClassInfoList.get(3); assertThat(p3.loadElementClass()).isEqualTo(String.class); + assertThat(p3.loadClass()).isEqualTo(String[][].class); assertThat(p3.getElementClassInfo()).isNull(); assertThat(p3.getNumDimensions()).isEqualTo(2); } From e0bb487f3a798752f61bf149d573d1bfda451e85 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 8 May 2019 03:08:45 -0600 Subject: [PATCH 0167/1778] Fix static analyzer warnings --- .../io/github/classgraph/ArrayClassInfo.java | 18 +++++++ .../classgraph/ObjectTypedValueWrapper.java | 48 ++++++++++--------- .../fastzipfilereader/NestedJarHandler.java | 2 +- .../classgraph/utils/VersionFinder.java | 4 +- 4 files changed, 46 insertions(+), 26 deletions(-) diff --git a/src/main/java/io/github/classgraph/ArrayClassInfo.java b/src/main/java/io/github/classgraph/ArrayClassInfo.java index a50ea7203..9cf1325f0 100644 --- a/src/main/java/io/github/classgraph/ArrayClassInfo.java +++ b/src/main/java/io/github/classgraph/ArrayClassInfo.java @@ -220,4 +220,22 @@ protected void findReferencedClassNames(final Set referencedClassNames) super.findReferencedClassNames(referencedClassNames); arrayTypeSignature.findReferencedClassNames(referencedClassNames); } + + // ------------------------------------------------------------------------------------------------------------- + + /* (non-Javadoc) + * @see io.github.classgraph.ClassInfo#equals(java.lang.Object) + */ + @Override + public boolean equals(final Object obj) { + return super.equals(obj); + } + + /* (non-Javadoc) + * @see io.github.classgraph.ClassInfo#hashCode() + */ + @Override + public int hashCode() { + return super.hashCode(); + } } diff --git a/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java b/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java index 804058dc2..5a088ff19 100644 --- a/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java +++ b/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java @@ -39,10 +39,10 @@ class ObjectTypedValueWrapper extends ScanResultObject { // works properly (can't properly serialize a field of Object type, since the concrete type is not /** Enum value. */ // stored in JSON). - private AnnotationEnumValue enumValue; + private AnnotationEnumValue annotationEnumValue; /** Class ref. */ - private AnnotationClassRef classRef; + private AnnotationClassRef annotationClassRef; /** AnnotationInfo. */ private AnnotationInfo annotationInfo; @@ -152,9 +152,9 @@ public ObjectTypedValueWrapper(final Object annotationParamValue) { } } } else if (annotationParamValue instanceof AnnotationEnumValue) { - enumValue = (AnnotationEnumValue) annotationParamValue; + annotationEnumValue = (AnnotationEnumValue) annotationParamValue; } else if (annotationParamValue instanceof AnnotationClassRef) { - classRef = (AnnotationClassRef) annotationParamValue; + annotationClassRef = (AnnotationClassRef) annotationParamValue; } else if (annotationParamValue instanceof AnnotationInfo) { annotationInfo = (AnnotationInfo) annotationParamValue; } else if (annotationParamValue instanceof String) { @@ -195,10 +195,10 @@ public ObjectTypedValueWrapper(final Object annotationParamValue) { */ Object instantiateOrGet(final ClassInfo annotationClassInfo, final String paramName) { final boolean instantiate = annotationClassInfo != null; - if (enumValue != null) { - return instantiate ? enumValue.loadClassAndReturnEnumValue() : enumValue; - } else if (classRef != null) { - return instantiate ? classRef.loadClass() : classRef; + if (annotationEnumValue != null) { + return instantiate ? annotationEnumValue.loadClassAndReturnEnumValue() : annotationEnumValue; + } else if (annotationClassRef != null) { + return instantiate ? annotationClassRef.loadClass() : annotationClassRef; } else if (annotationInfo != null) { return instantiate ? annotationInfo.loadClassAndInstantiate() : annotationInfo; } else if (stringValue != null) { @@ -517,10 +517,10 @@ protected ClassInfo getClassInfo() { @Override void setScanResult(final ScanResult scanResult) { super.setScanResult(scanResult); - if (enumValue != null) { - enumValue.setScanResult(scanResult); - } else if (classRef != null) { - classRef.setScanResult(scanResult); + if (annotationEnumValue != null) { + annotationEnumValue.setScanResult(scanResult); + } else if (annotationClassRef != null) { + annotationClassRef.setScanResult(scanResult); } else if (annotationInfo != null) { annotationInfo.setScanResult(scanResult); } else if (objectArrayValue != null) { @@ -540,10 +540,10 @@ void setScanResult(final ScanResult scanResult) { */ @Override void findReferencedClassNames(final Set referencedClassNames) { - if (enumValue != null) { - enumValue.findReferencedClassNames(referencedClassNames); - } else if (classRef != null) { - referencedClassNames.add(classRef.getClassName()); + if (annotationEnumValue != null) { + annotationEnumValue.findReferencedClassNames(referencedClassNames); + } else if (annotationClassRef != null) { + referencedClassNames.add(annotationClassRef.getClassName()); } else if (annotationInfo != null) { annotationInfo.findReferencedClassNames(referencedClassNames); } else if (objectArrayValue != null) { @@ -560,12 +560,13 @@ void findReferencedClassNames(final Set referencedClassNames) { */ @Override public int hashCode() { - return Objects.hash(enumValue, classRef, annotationInfo, stringValue, integerValue, longValue, shortValue, - booleanValue, characterValue, floatValue, doubleValue, byteValue, Arrays.hashCode(stringArrayValue), - Arrays.hashCode(intArrayValue), Arrays.hashCode(longArrayValue), Arrays.hashCode(shortArrayValue), - Arrays.hashCode(booleanArrayValue), Arrays.hashCode(charArrayValue), - Arrays.hashCode(floatArrayValue), Arrays.hashCode(doubleArrayValue), - Arrays.hashCode(byteArrayValue), Arrays.hashCode(objectArrayValue)); + return Objects.hash(annotationEnumValue, annotationClassRef, annotationInfo, stringValue, integerValue, + longValue, shortValue, booleanValue, characterValue, floatValue, doubleValue, byteValue, + Arrays.hashCode(stringArrayValue), Arrays.hashCode(intArrayValue), Arrays.hashCode(longArrayValue), + Arrays.hashCode(shortArrayValue), Arrays.hashCode(booleanArrayValue), + Arrays.hashCode(charArrayValue), Arrays.hashCode(floatArrayValue), + Arrays.hashCode(doubleArrayValue), Arrays.hashCode(byteArrayValue), + Arrays.hashCode(objectArrayValue)); } /* (non-Javadoc) @@ -579,7 +580,8 @@ public boolean equals(final Object other) { return false; } final ObjectTypedValueWrapper o = (ObjectTypedValueWrapper) other; - return Objects.equals(enumValue, o.enumValue) && Objects.equals(classRef, o.classRef) + return Objects.equals(annotationEnumValue, o.annotationEnumValue) + && Objects.equals(annotationClassRef, o.annotationClassRef) && Objects.equals(annotationInfo, o.annotationInfo) && Objects.equals(stringValue, o.stringValue) && Objects.equals(integerValue, o.integerValue) && Objects.equals(longValue, o.longValue) && Objects.equals(shortValue, o.shortValue) && Objects.equals(booleanValue, o.booleanValue) 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 2c2aadb9c..0be1d675c 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java @@ -572,7 +572,7 @@ private PhysicalZipFile downloadJarFromURL(final String jarURL, final LogNode lo } if (physicalZipFile == null) { // Should not happen - throw new RuntimeException("physicalZipFile should not be null"); + throw ClassGraphException.newClassGraphException("physicalZipFile should not be null"); } return physicalZipFile; } finally { 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 f552983a7..cef6e6959 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/VersionFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/VersionFinder.java @@ -87,11 +87,11 @@ public final class VersionFinder { // Skip } } - if (versionParts.size() > 0 && versionParts.get(0) == 1) { + if (!versionParts.isEmpty() && versionParts.get(0) == 1) { // 1.7 or 1.8 -> 7 or 8 versionParts.remove(0); } - if (versionParts.size() == 0) { + if (versionParts.isEmpty()) { throw new RuntimeException("Could not determine Java version: " + JAVA_VERSION); } javaMajorVersion = versionParts.get(0); From 46365bb4364c906bd2ad8a2f78a8d514c050818d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 9 May 2019 04:06:35 -0600 Subject: [PATCH 0168/1778] Add `ClassInfo#getClasspathElementURI()` --- .../java/io/github/classgraph/ClassInfo.java | 23 ++++++++++++++++++- .../java/io/github/classgraph/Resource.java | 2 +- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index a78ef4ef1..888dab2f8 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -33,6 +33,7 @@ import java.lang.annotation.Repeatable; import java.lang.reflect.Modifier; import java.net.MalformedURLException; +import java.net.URI; import java.net.URL; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; @@ -2385,9 +2386,29 @@ public ClassTypeSignature getTypeSignature() { // ------------------------------------------------------------------------------------------------------------- /** - * Get the {@link URL} of the classpath element that this class was found within. + * Get the {@link URI} of the classpath element that this class was found within. + * + * @return The {@link URI} of the classpath element that this class was found within. + * @throws IllegalArgumentException + * if the classpath element does not have a valid URI (e.g. for modules whose location URI is null). + */ + public URI getClasspathElementURI() { + if (classpathElement == null) { + throw new IllegalArgumentException("Classpath element is not known for this classpath element"); + } + return classpathElement.getURI(); + } + + /** + * Get the {@link URL} of the classpath element or module that this class was found within. Use + * {@link #getClasspathElementURI()} instead if the resource may have come from a system module, or if this is a + * jlink'd runtime image, since "jrt:" URI schemes used by system modules and jlink'd runtime images are not + * suppored by {@link URL}, and this will cause {@link IllegalArgumentException} to be thrown. * * @return The {@link URL} of the classpath element that this class was found within. + * @throws IllegalArgumentException + * if the classpath element URI cannot be converted to a {@link URL} (in particular, if the URI has + * a {@code jrt:/} scheme). */ public URL getClasspathElementURL() { if (classpathElement == null) { diff --git a/src/main/java/io/github/classgraph/Resource.java b/src/main/java/io/github/classgraph/Resource.java index a666e6733..2bb121593 100644 --- a/src/main/java/io/github/classgraph/Resource.java +++ b/src/main/java/io/github/classgraph/Resource.java @@ -369,7 +369,7 @@ public URL getURL() { * * @return The {@link URL} of the classpath element or module that this resource was found within. * @throws IllegalArgumentException - * if the resource was obtained from a module and the module's location URI is null. + * if the classpath element does not have a valid URI (e.g. for modules whose location URI is null). */ public URI getClasspathElementURI() { return classpathElement.getURI(); From 78f17e641d17c168647122791bceec4f9f1e0769 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 10 May 2019 01:38:23 -0600 Subject: [PATCH 0169/1778] Fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f9e9c96c1..c05e603da 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ClassGraph Logo       Duke Award Logo -ClassGraph (formerly **FastClasspathScanner**) is an uber-fast, ultra-lightweight, parallelized classpath and module for Java, Scala, Kotlin and other JVM languages. +ClassGraph (formerly **FastClasspathScanner**) is an uber-fast, ultra-lightweight, parallelized classpath 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. | |-----------------------------| From 59c85e79baae3f0ec4000c481c042549966aacb0 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 10 May 2019 01:39:27 -0600 Subject: [PATCH 0170/1778] Update docs --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c05e603da..d46b40283 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ClassGraph Logo       Duke Award Logo -ClassGraph (formerly **FastClasspathScanner**) is an uber-fast, ultra-lightweight, parallelized classpath and module scanner for Java, Scala, Kotlin and other JVM languages. +ClassGraph (formerly **FastClasspathScanner**) is an uber-fast, ultra-lightweight, 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. | |-----------------------------| From 5d7585036deb4906ea8596cfc03e4530b43f0a3e Mon Sep 17 00:00:00 2001 From: Christian Dietrich Date: Sat, 11 May 2019 21:47:45 +0200 Subject: [PATCH 0171/1778] Fixed classfileresource of classes that were created as superclass before they where scanned. Fixes https://github.com/classgraph/classgraph/issues/345 --- .../java/io/github/classgraph/ClassInfo.java | 2 + .../classgraph/issues/issue345/Issue345.java | 40 +++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 src/test/java/io/github/classgraph/issues/issue345/Issue345.java diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index 888dab2f8..72d1b1d41 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -596,6 +596,8 @@ static ClassInfo addScannedClass(final String className, final int classModifier + " should not have been encountered more than once due to classpath masking --" + " please report this bug at: https://github.com/classgraph/classgraph/issues"); } + // set the classfileResource for the placeholder class + classInfo.classfileResource = classfileResource; } // Mark the class as scanned diff --git a/src/test/java/io/github/classgraph/issues/issue345/Issue345.java b/src/test/java/io/github/classgraph/issues/issue345/Issue345.java new file mode 100644 index 000000000..8ef94f0eb --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue345/Issue345.java @@ -0,0 +1,40 @@ +package io.github.classgraph.issues.issue345; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.net.URL; +import java.net.URLClassLoader; +import java.util.AbstractQueue; + +import org.junit.Test; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ScanResult; + +/** + * Issue345. + */ +public class Issue345 { + + /** + * Issue 345. + * + * @throws Exception + * the exception + */ + @Test + public void issue345() { + try (ScanResult scanResult = new ClassGraph() + .enableClassInfo() + .enableSystemJarsAndModules() + .overrideClassLoaders( + new URLClassLoader(new URL[] { + Issue345.class.getResource("/java/util/ArrayBlockingQueue.class"), + Issue345.class.getResource("/java/util/AbstractQueue.class"), + })) + .scan()) { + assertThat(scanResult.getClassInfo(AbstractQueue.class.getName()).getResource()).isNotNull(); + } + } + +} From b9808d24b53b43a3eb15c1213fb10369e384c49f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 11 May 2019 14:23:15 -0600 Subject: [PATCH 0172/1778] Add any discovered modifier bits in `addScannedClass()` (#346) --- src/main/java/io/github/classgraph/ClassInfo.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index 72d1b1d41..cdbb86055 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -596,8 +596,12 @@ static ClassInfo addScannedClass(final String className, final int classModifier + " should not have been encountered more than once due to classpath masking --" + " please report this bug at: https://github.com/classgraph/classgraph/issues"); } - // set the classfileResource for the placeholder class + + // Set the classfileResource for the placeholder class classInfo.classfileResource = classfileResource; + + // Add any additional modifier bits + classInfo.modifiers |= classModifiers; } // Mark the class as scanned From 34f26895e0b6f148ee5d1dd785d4397aa01f5ac3 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 11 May 2019 14:25:30 -0600 Subject: [PATCH 0173/1778] Fix formatting --- .../java/io/github/classgraph/ClassInfo.java | 4 +-- .../classgraph/issues/issue345/Issue345.java | 25 ++++++------------- 2 files changed, 10 insertions(+), 19 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index cdbb86055..0670a2767 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -596,10 +596,10 @@ static ClassInfo addScannedClass(final String className, final int classModifier + " should not have been encountered more than once due to classpath masking --" + " please report this bug at: https://github.com/classgraph/classgraph/issues"); } - + // Set the classfileResource for the placeholder class classInfo.classfileResource = classfileResource; - + // Add any additional modifier bits classInfo.modifiers |= classModifiers; } diff --git a/src/test/java/io/github/classgraph/issues/issue345/Issue345.java b/src/test/java/io/github/classgraph/issues/issue345/Issue345.java index 8ef94f0eb..88e13277e 100644 --- a/src/test/java/io/github/classgraph/issues/issue345/Issue345.java +++ b/src/test/java/io/github/classgraph/issues/issue345/Issue345.java @@ -15,26 +15,17 @@ * Issue345. */ public class Issue345 { - /** * Issue 345. - * - * @throws Exception - * the exception */ - @Test - public void issue345() { - try (ScanResult scanResult = new ClassGraph() - .enableClassInfo() - .enableSystemJarsAndModules() - .overrideClassLoaders( - new URLClassLoader(new URL[] { - Issue345.class.getResource("/java/util/ArrayBlockingQueue.class"), - Issue345.class.getResource("/java/util/AbstractQueue.class"), - })) - .scan()) { + @Test + public void issue345() { + try (ScanResult scanResult = new ClassGraph().enableClassInfo().enableSystemJarsAndModules() + .overrideClassLoaders(new URLClassLoader( + new URL[] { Issue345.class.getResource("/java/util/ArrayBlockingQueue.class"), + Issue345.class.getResource("/java/util/AbstractQueue.class"), })) + .scan()) { assertThat(scanResult.getClassInfo(AbstractQueue.class.getName()).getResource()).isNotNull(); } - } - + } } From 307dadfc0f1892a4adb6c88c41035460dc62032e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 11 May 2019 14:26:09 -0600 Subject: [PATCH 0174/1778] [maven-release-plugin] prepare release classgraph-4.8.32 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 4ca332827..4fade34cf 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.32-SNAPSHOT + 4.8.32 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.32 From f50cc40f839f23427fe62e1b82175cc6b0666239 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 11 May 2019 14:26:15 -0600 Subject: [PATCH 0175/1778] [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 4fade34cf..050455bec 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.32 + 4.8.33-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.32 + HEAD From 612a72d351b049a03957d241e35e44e06e6e8b73 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 11 May 2019 14:39:40 -0600 Subject: [PATCH 0176/1778] Handle modifier bits for interface/annotation more uniformly (#345) --- .../java/io/github/classgraph/ClassInfo.java | 53 +++++++------------ 1 file changed, 20 insertions(+), 33 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index 0670a2767..78b1daed0 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -63,12 +63,6 @@ public class ClassInfo extends ScanResultObject implements Comparable /** Class modifier flags, e.g. Modifier.PUBLIC */ private int modifiers; - /** True if the classfile indicated this is an interface (or an annotation, which is an interface). */ - private boolean isInterface; - - /** True if the classfile indicated this is an annotation. */ - private boolean isAnnotation; - /** * This annotation has the {@link Inherited} meta-annotation, which means that any class that this annotation is * applied to also implicitly causes the annotation to annotate all subclasses too. @@ -186,12 +180,6 @@ protected ClassInfo(final String name, final int classModifiers, final Resource throw new IllegalArgumentException("Bad class name"); } setModifiers(classModifiers); - if ((classModifiers & ANNOTATION_CLASS_MODIFIER) != 0) { - isAnnotation = true; - } - if ((classModifiers & Modifier.INTERFACE) != 0) { - isInterface = true; - } this.classfileResource = classfileResource; this.relatedClasses = new EnumMap<>(RelType.class); } @@ -327,12 +315,6 @@ static ClassInfo getOrCreateClassInfo(final String className, final int classMod */ void setModifiers(final int modifiers) { this.modifiers |= modifiers; - if ((modifiers & ANNOTATION_CLASS_MODIFIER) != 0) { - this.isAnnotation = true; - } - if ((modifiers & Modifier.INTERFACE) != 0) { - this.isInterface = true; - } } /** @@ -342,7 +324,9 @@ void setModifiers(final int modifiers) { * true if this is an interface */ void setIsInterface(final boolean isInterface) { - this.isInterface |= isInterface; + if (isInterface) { + this.modifiers |= Modifier.INTERFACE; + } } /** @@ -352,7 +336,9 @@ void setIsInterface(final boolean isInterface) { * true if this is an annotation */ void setIsAnnotation(final boolean isAnnotation) { - this.isAnnotation |= isAnnotation; + if (isAnnotation) { + this.modifiers |= ANNOTATION_CLASS_MODIFIER; + } } // ------------------------------------------------------------------------------------------------------------- @@ -385,7 +371,7 @@ void addSuperclass(final String superclassName, final Map cla void addImplementedInterface(final String interfaceName, final Map classNameToClassInfo) { final ClassInfo interfaceClassInfo = getOrCreateClassInfo(interfaceName, /* classModifiers = */ Modifier.INTERFACE, classNameToClassInfo); - interfaceClassInfo.isInterface = true; + interfaceClassInfo.setIsInterface(true); interfaceClassInfo.modifiers |= Modifier.INTERFACE; this.addRelatedClass(RelType.IMPLEMENTED_INTERFACES, interfaceClassInfo); interfaceClassInfo.addRelatedClass(RelType.CLASSES_IMPLEMENTING, this); @@ -551,6 +537,7 @@ void setTypeSignature(final String typeSignatureStr) { * the default param names and values, if this is an annotation */ void addAnnotationParamDefaultValues(final AnnotationParameterValueList paramNamesAndValues) { + setIsAnnotation(true); if (this.annotationDefaultParamValues == null) { this.annotationDefaultParamValues = paramNamesAndValues; } else { @@ -1043,7 +1030,7 @@ public boolean isStatic() { * @return true if this class is an annotation class. */ public boolean isAnnotation() { - return isAnnotation; + return (modifiers & ANNOTATION_CLASS_MODIFIER) != 0; } /** @@ -1053,7 +1040,7 @@ public boolean isAnnotation() { * implemented). */ public boolean isInterface() { - return isInterface && !isAnnotation; + return isInterfaceOrAnnotation() && !isAnnotation(); } /** @@ -1063,7 +1050,7 @@ public boolean isInterface() { * implemented). */ public boolean isInterfaceOrAnnotation() { - return isInterface; + return (modifiers & Modifier.INTERFACE) != 0; } /** @@ -1081,7 +1068,7 @@ public boolean isEnum() { * @return true if this class is a standard class (i.e. is not an annotation or interface). */ public boolean isStandardClass() { - return !(isAnnotation || isInterface); + return !(isAnnotation() || isInterface()); } /** @@ -1147,7 +1134,7 @@ public boolean isAnonymousInnerClass() { * @return true if this class is an implemented interface. */ public boolean isImplementedInterface() { - return relatedClasses.get(RelType.CLASSES_IMPLEMENTING) != null || (isInterface && !isAnnotation); + return relatedClasses.get(RelType.CLASSES_IMPLEMENTING) != null || isInterface(); } /** @@ -1482,7 +1469,7 @@ public ClassInfoList getInterfaces() { * interface, otherwise returns the empty list. */ public ClassInfoList getClassesImplementing() { - if (!isInterface) { + if (!isInterface()) { throw new IllegalArgumentException("Class is not an interface: " + getName()); } // Subclasses of implementing classes also implement the interface @@ -1686,7 +1673,7 @@ public AnnotationParameterValueList getAnnotationDefaultParameterValues() { if (!scanResult.scanSpec.enableAnnotationInfo) { throw new IllegalArgumentException("Please call ClassGraph#enableAnnotationInfo() before #scan()"); } - if (!isAnnotation) { + if (!isAnnotation()) { throw new IllegalArgumentException("Class is not an annotation: " + getName()); } if (annotationDefaultParamValues == null) { @@ -1710,7 +1697,7 @@ public ClassInfoList getClassesWithAnnotation() { if (!scanResult.scanSpec.enableAnnotationInfo) { throw new IllegalArgumentException("Please call ClassGraph#enableAnnotationInfo() before #scan()"); } - if (!isAnnotation) { + if (!isAnnotation()) { throw new IllegalArgumentException("Class is not an annotation: " + getName()); } @@ -2752,7 +2739,7 @@ protected String toString(final boolean typeNameOnly) { final ClassTypeSignature typeSig = getTypeSignature(); if (typeSig != null) { // Generic classes - return typeSig.toString(name, typeNameOnly, modifiers, isAnnotation, isInterface); + return typeSig.toString(name, typeNameOnly, modifiers, isAnnotation(), isInterface()); } else { // Non-generic classes final StringBuilder buf = new StringBuilder(); @@ -2763,8 +2750,8 @@ protected String toString(final boolean typeNameOnly) { if (buf.length() > 0) { buf.append(' '); } - buf.append(isAnnotation ? "@interface " - : isInterface ? "interface " : (modifiers & 0x4000) != 0 ? "enum " : "class "); + buf.append(isAnnotation() ? "@interface " + : isInterface() ? "interface " : (modifiers & 0x4000) != 0 ? "enum " : "class "); buf.append(name); final ClassInfo superclass = getSuperclass(); if (superclass != null && !superclass.getName().equals("java.lang.Object")) { @@ -2773,7 +2760,7 @@ protected String toString(final boolean typeNameOnly) { final Set interfaces = this.filterClassInfo(RelType.IMPLEMENTED_INTERFACES, /* strictWhitelist = */ false).directlyRelatedClasses; if (!interfaces.isEmpty()) { - buf.append(isInterface ? " extends " : " implements "); + buf.append(isInterface() ? " extends " : " implements "); boolean first = true; for (final ClassInfo iface : interfaces) { if (first) { From 9fc972e0f925ca611e2253a08664eafc12e4f519 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 11 May 2019 15:13:55 -0600 Subject: [PATCH 0177/1778] Percent-encode URIs before creating them --- src/main/java/io/github/classgraph/Resource.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/classgraph/Resource.java b/src/main/java/io/github/classgraph/Resource.java index 2bb121593..759d02941 100644 --- a/src/main/java/io/github/classgraph/Resource.java +++ b/src/main/java/io/github/classgraph/Resource.java @@ -43,6 +43,7 @@ import nonapi.io.github.classgraph.utils.FileUtils; import nonapi.io.github.classgraph.utils.InputStreamOrByteBufferAdapter; import nonapi.io.github.classgraph.utils.LogNode; +import nonapi.io.github.classgraph.utils.URLPathEncoder; /** * A classpath or module path resource (i.e. file) that was found in a whitelisted/non-blacklisted package inside a @@ -341,8 +342,9 @@ public URI getURI() { // Check if this is a directory-based module (location URI will end in "/") final boolean isDir = locationURIStr.endsWith("/"); try { - return new URI((isDir || locationURIStr.startsWith("jar:") ? "" : "jar:") + locationURIStr - + (isDir ? "" : "!/") + resourcePath); + return new URI( + URLPathEncoder.normalizeURLPath((isDir || locationURIStr.startsWith("jar:") ? "" : "jar:") + + locationURIStr + (isDir ? "" : "!/") + resourcePath)); } catch (final URISyntaxException e) { throw new IllegalArgumentException("Could not form URL for classpath element: " + locationURIStr + " ; path: " + resourcePath + " : " + e); From 26795ba374e5b6b4455cf68e46f54ea8a73e8393 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 11 May 2019 15:18:20 -0600 Subject: [PATCH 0178/1778] Fix previous commit --- src/main/java/io/github/classgraph/Resource.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/github/classgraph/Resource.java b/src/main/java/io/github/classgraph/Resource.java index 759d02941..6fc01adb1 100644 --- a/src/main/java/io/github/classgraph/Resource.java +++ b/src/main/java/io/github/classgraph/Resource.java @@ -337,14 +337,13 @@ private static URL uriToURL(final URI uri) { */ public URI getURI() { final URI locationURI = getClasspathElementURI(); - final String resourcePath = getPathRelativeToClasspathElement(); final String locationURIStr = locationURI.toString(); + final String resourcePath = getPathRelativeToClasspathElement(); // Check if this is a directory-based module (location URI will end in "/") final boolean isDir = locationURIStr.endsWith("/"); try { - return new URI( - URLPathEncoder.normalizeURLPath((isDir || locationURIStr.startsWith("jar:") ? "" : "jar:") - + locationURIStr + (isDir ? "" : "!/") + resourcePath)); + return new URI((isDir || locationURIStr.startsWith("jar:") ? "" : "jar:") + locationURIStr + + (isDir ? "" : "!/") + URLPathEncoder.normalizeURLPath(resourcePath)); } catch (final URISyntaxException e) { throw new IllegalArgumentException("Could not form URL for classpath element: " + locationURIStr + " ; path: " + resourcePath + " : " + e); From 407294768143367bd9328cfb60538a6f1edb8706 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 11 May 2019 15:40:11 -0600 Subject: [PATCH 0179/1778] Fix URI path encoding --- src/main/java/io/github/classgraph/Resource.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/Resource.java b/src/main/java/io/github/classgraph/Resource.java index 6fc01adb1..acb21266e 100644 --- a/src/main/java/io/github/classgraph/Resource.java +++ b/src/main/java/io/github/classgraph/Resource.java @@ -343,7 +343,7 @@ public URI getURI() { final boolean isDir = locationURIStr.endsWith("/"); try { return new URI((isDir || locationURIStr.startsWith("jar:") ? "" : "jar:") + locationURIStr - + (isDir ? "" : "!/") + URLPathEncoder.normalizeURLPath(resourcePath)); + + (isDir ? "" : "!/") + URLPathEncoder.encodePath(resourcePath)); } catch (final URISyntaxException e) { throw new IllegalArgumentException("Could not form URL for classpath element: " + locationURIStr + " ; path: " + resourcePath + " : " + e); From 4aeae5dd35126462b58ea1a00a071efbd8d5d4d4 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 11 May 2019 15:42:23 -0600 Subject: [PATCH 0180/1778] Add `.getURIs()` --- .../java/io/github/classgraph/ResourceList.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/main/java/io/github/classgraph/ResourceList.java b/src/main/java/io/github/classgraph/ResourceList.java index b21f00a20..07f8a34f6 100644 --- a/src/main/java/io/github/classgraph/ResourceList.java +++ b/src/main/java/io/github/classgraph/ResourceList.java @@ -30,6 +30,7 @@ import java.io.IOException; import java.io.InputStream; +import java.net.URI; import java.net.URL; import java.nio.ByteBuffer; import java.util.AbstractMap.SimpleEntry; @@ -160,6 +161,9 @@ public List getPathsRelativeToClasspathElement() { /** * Get the URLs of all resources in this list, by calling {@link Resource#getURL()} for each item in the list. + * Note that any resource with a {@code jrt:} URI (e.g. a system resource, or a resource from a jlink'd image) + * will cause {@link IllegalArgumentException} to be thrown, since {@link URL} does not support this scheme, so + * {@link #getURIs()} is strongly preferred over {@link #getURLs()}. * * @return The URLs of all resources in this list. */ @@ -171,6 +175,19 @@ public List getURLs() { return resourceURLs; } + /** + * Get the URIs of all resources in this list, by calling {@link Resource#getURI()} for each item in the list. + * + * @return The URIs of all resources in this list. + */ + public List getURIs() { + final List resourceURLs = new ArrayList<>(this.size()); + for (final Resource resource : this) { + resourceURLs.add(resource.getURI()); + } + return resourceURLs; + } + // ------------------------------------------------------------------------------------------------------------- /** Returns true if a Resource has a path ending in ".class". */ From 12ce8b3187047e076b6bb906c588a54b24c7ba8f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 12 May 2019 01:31:23 -0600 Subject: [PATCH 0181/1778] Improve logging --- .../java/io/github/classgraph/Classfile.java | 57 ++++++++++--------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index 92b1eceeb..131388dd3 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -130,9 +130,6 @@ class Classfile { /** The scan spec. */ private final ScanSpec scanSpec; - /** The log. */ - private final LogNode log; - // ------------------------------------------------------------------------------------------------------------- /** The number of constant pool entries plus one. */ @@ -227,8 +224,11 @@ public synchronized Throwable fillInStackTrace() { * the class name * @param relationship * the relationship type + * @param log + * the log */ - private void scheduleScanningIfExternalClass(final String className, final String relationship) { + private void scheduleScanningIfExternalClass(final String className, final String relationship, + final LogNode log) { // Don't scan Object if (className != null && !className.equals("java.lang.Object") // Only schedule each external class once for scanning, across all threads @@ -278,22 +278,25 @@ private void scheduleScanningIfExternalClass(final String className, final Strin /** * Check if scanning needs to be extended upwards to an external superclass, interface or annotation. + * + * @param log + * the log */ - private void extendScanningUpwards() { + private void extendScanningUpwards(final LogNode log) { // Check superclass if (superclassName != null) { - scheduleScanningIfExternalClass(superclassName, "superclass"); + scheduleScanningIfExternalClass(superclassName, "superclass", log); } // Check implemented interfaces if (implementedInterfaces != null) { for (final String interfaceName : implementedInterfaces) { - scheduleScanningIfExternalClass(interfaceName, "interface"); + scheduleScanningIfExternalClass(interfaceName, "interface", log); } } // Check class annotations if (classAnnotations != null) { for (final AnnotationInfo annotationInfo : classAnnotations) { - scheduleScanningIfExternalClass(annotationInfo.getName(), "class annotation"); + scheduleScanningIfExternalClass(annotationInfo.getName(), "class annotation", log); } } // Check method annotations and method parameter annotations @@ -301,7 +304,7 @@ private void extendScanningUpwards() { for (final MethodInfo methodInfo : methodInfoList) { if (methodInfo.annotationInfo != null) { for (final AnnotationInfo methodAnnotationInfo : methodInfo.annotationInfo) { - scheduleScanningIfExternalClass(methodAnnotationInfo.getName(), "method annotation"); + scheduleScanningIfExternalClass(methodAnnotationInfo.getName(), "method annotation", log); } if (methodInfo.parameterAnnotationInfo != null && methodInfo.parameterAnnotationInfo.length > 0) { @@ -309,7 +312,7 @@ private void extendScanningUpwards() { if (paramAnns != null && paramAnns.length > 0) { for (final AnnotationInfo paramAnn : paramAnns) { scheduleScanningIfExternalClass(paramAnn.getName(), - "method parameter annotation"); + "method parameter annotation", log); } } } @@ -322,7 +325,7 @@ private void extendScanningUpwards() { for (final FieldInfo fieldInfo : fieldInfoList) { if (fieldInfo.annotationInfo != null) { for (final AnnotationInfo fieldAnnotationInfo : fieldInfo.annotationInfo) { - scheduleScanningIfExternalClass(fieldAnnotationInfo.getName(), "field annotation"); + scheduleScanningIfExternalClass(fieldAnnotationInfo.getName(), "field annotation", log); } } } @@ -1481,7 +1484,6 @@ private void readClassAttributes() throws IOException, ClassfileFormatException this.isExternalClass = isExternalClass; this.stringInternMap = stringInternMap; this.scanSpec = scanSpec; - this.log = log; try { // Open classfile as a ByteBuffer or InputStream @@ -1522,23 +1524,12 @@ private void readClassAttributes() throws IOException, ClassfileFormatException inputStreamOrByteBuffer = null; } - // Check if any superclasses, interfaces or annotations are external (non-whitelisted) classes - // that need to be scheduled for scanning, so that all of the "upwards" direction of the class - // graph is scanned for any whitelisted class, even if the superclasses / interfaces / annotations - // are not themselves whitelisted. - if (scanSpec.extendScanningUpwardsToExternalClasses) { - extendScanningUpwards(); - // If any external classes were found, schedule them for scanning - if (additionalWorkUnits != null) { - workQueue.addWorkUnits(additionalWorkUnits); - } - } - // Write class info to log + final LogNode subLog = log == null ? null + : log.log("Found " // + + (isAnnotation ? "annotation class" : isInterface ? "interface class" : "class") // + + " " + className); if (log != null) { - final LogNode subLog = log.log("Found " // - + (isAnnotation ? "annotation class" : isInterface ? "interface class" : "class") // - + " " + className); if (superclassName != null) { subLog.log( "Super" + (isInterface && !isAnnotation ? "interface" : "class") + ": " + superclassName); @@ -1581,5 +1572,17 @@ private void readClassAttributes() throws IOException, ClassfileFormatException subLog.log("Referenced class names: " + Join.join(", ", refdClassNamesSorted)); } } + + // Check if any superclasses, interfaces or annotations are external (non-whitelisted) classes + // that need to be scheduled for scanning, so that all of the "upwards" direction of the class + // graph is scanned for any whitelisted class, even if the superclasses / interfaces / annotations + // are not themselves whitelisted. + if (scanSpec.extendScanningUpwardsToExternalClasses) { + extendScanningUpwards(subLog); + // If any external classes were found, schedule them for scanning + if (additionalWorkUnits != null) { + workQueue.addWorkUnits(additionalWorkUnits); + } + } } } From 5eb94225a1db2a2c44c82cd8d77509b8a7eaf7bd Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 12 May 2019 02:31:53 -0600 Subject: [PATCH 0182/1778] Fix method visibility --- .../java/nonapi/io/github/classgraph/utils/URLPathEncoder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 a1502528f..28fb06bd2 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/URLPathEncoder.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/URLPathEncoder.java @@ -75,7 +75,7 @@ private URLPathEncoder() { * The path to encode. * @return The encoded path. */ - private static String encodePath(final String path) { + public static String encodePath(final String path) { // Accept ':' if it is part of a scheme prefix int validColonPrefixLen = 0; for (final String scheme : SCHEME_PREFIXES) { From 87ceab3cb2e07b8cba2df8647fccc05f3d7af135 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 12 May 2019 02:32:18 -0600 Subject: [PATCH 0183/1778] Fix race condition when extending scanning upwards to superclasses etc. --- .../java/io/github/classgraph/ClassInfo.java | 6 +-- .../java/io/github/classgraph/Classfile.java | 38 ++++++++++++------ .../java/io/github/classgraph/Scanner.java | 39 ++++++++++++------- 3 files changed, 53 insertions(+), 30 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index 78b1daed0..3d275c9b9 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -2449,9 +2449,9 @@ public ModuleRef getModuleRef() { /** * The {@link Resource} for the classfile of this class. * - * @return The {@link Resource} for the classfile of this class, or null if this is an "external" class (a - * blacklisted class, or a class in a blacklisted package, or a class that was referenced as a - * superclass, interface or annotation, but that wasn't in the scanned path). + * @return The {@link Resource} for the classfile of this class. Returns null if the classfile for this class + * was not actually read during the scan, because this class was not in the whitelist, but was + * referenced by a whitelisted class. */ public Resource getResource() { return classfileResource; diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index 131388dd3..998066ca9 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -118,11 +118,14 @@ class Classfile { /** The type signature. */ private String typeSignature; + /** The names of whitelisted classes found in the classpath while scanning paths within classpath elements. */ + private final Set whitelistedClassNamesFound; + /** - * Class names already scheduled for scanning. If a class name is not in this list, the class is external, and - * has not yet been scheduled for scanning. + * The names of external (non-whitelisted) classes scheduled for extended scanning (where scanning is extended + * upwards to superclasses, interfaces and annotations). */ - private final Set classNamesScheduledForScanning; + private final Set classNamesScheduledForExtendedScanning; /** Any additional work units scheduled for scanning. */ private List additionalWorkUnits; @@ -231,8 +234,10 @@ private void scheduleScanningIfExternalClass(final String className, final Strin final LogNode log) { // Don't scan Object if (className != null && !className.equals("java.lang.Object") - // Only schedule each external class once for scanning, across all threads - && classNamesScheduledForScanning.add(className)) { + // Don't schedule a class for scanning that was already found to be whitelisted + && !whitelistedClassNamesFound.contains(className) + // Only schedule each external class once for scanning, across all threads + && classNamesScheduledForExtendedScanning.add(className)) { // Search for the named class' classfile among classpath elements, in classpath order (this is O(N) // for each class, but there shouldn't be too many cases of extending scanning upwards) final String classfilePath = JarUtils.classNameToClassfilePath(className); @@ -257,9 +262,13 @@ private void scheduleScanningIfExternalClass(final String className, final Strin if (classResource != null) { // Found class resource if (log != null) { - log.log("Scheduling external class for scanning: " + relationship + " " + className - + (foundInClasspathElt == classpathElement ? "" + // Log the extended scan as a child LogNode of the current class' scan log, since the + // external class is not scanned at the regular place in the classpath element hierarchy + // traversal + classResource.scanLog = log.log("Extending scanning to external (non-whitelisted) " + + relationship + " " + className + (foundInClasspathElt == classpathElement ? "" : " -- found in classpath element " + foundInClasspathElt)); + } if (additionalWorkUnits == null) { additionalWorkUnits = new ArrayList<>(); @@ -1446,8 +1455,12 @@ private void readClassAttributes() throws IOException, ClassfileFormatException * the classpath element * @param classpathOrder * the classpath order - * @param classNamesScheduledForScanning - * the class names scheduled for scanning + * @param whitelistedClassNamesFound + * the names of whitelisted classes found in the classpath while scanning paths within classpath + * elements. + * @param classNamesScheduledForExtendedScanning + * the names of external (non-whitelisted) classes scheduled for extended scanning (where scanning is + * extended upwards to superclasses, interfaces and annotations). * @param relativePath * the relative path * @param classfileResource @@ -1471,15 +1484,16 @@ private void readClassAttributes() throws IOException, ClassfileFormatException * false) */ Classfile(final ClasspathElement classpathElement, final List classpathOrder, - final Set classNamesScheduledForScanning, final String relativePath, - final Resource classfileResource, final boolean isExternalClass, + final Set whitelistedClassNamesFound, final Set classNamesScheduledForExtendedScanning, + final String relativePath, final Resource classfileResource, final boolean isExternalClass, final ConcurrentHashMap stringInternMap, final WorkQueue workQueue, final ScanSpec scanSpec, final LogNode log) throws IOException, ClassfileFormatException, SkipClassException { this.classpathElement = classpathElement; this.classpathOrder = classpathOrder; this.relativePath = relativePath; - this.classNamesScheduledForScanning = classNamesScheduledForScanning; + this.whitelistedClassNamesFound = whitelistedClassNamesFound; + this.classNamesScheduledForExtendedScanning = classNamesScheduledForExtendedScanning; this.classfileResource = classfileResource; this.isExternalClass = isExternalClass; this.stringInternMap = stringInternMap; diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 00b7a49d0..6b3804937 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -551,8 +551,16 @@ private static class ClassfileScannerWorkUnitProcessor implements WorkUnitProces /** The classpath order. */ private final List classpathOrder; - /** The class names scheduled for scanning. */ - private final Set classNamesScheduledForScanning; + /** + * The names of whitelisted classes found in the classpath while scanning paths within classpath elements. + */ + private final Set whitelistedClassNamesFound; + + /** + * The names of external (non-whitelisted) classes scheduled for extended scanning (where scanning is + * extended upwards to superclasses, interfaces and annotations). + */ + private final Set classNamesScheduledForExtendedScanning = new HashSet<>(); /** The valid {@link Classfile} objects created by scanning classfiles. */ private final Queue scannedClassfiles; @@ -567,17 +575,18 @@ private static class ClassfileScannerWorkUnitProcessor implements WorkUnitProces * the scan spec * @param classpathOrder * the classpath order - * @param classNamesScheduledForScanning - * the class names scheduled for scanning + * @param whitelistedClassNamesFound + * the names of whitelisted classes found in the classpath while scanning paths within classpath + * elements. * @param scannedClassfiles * the {@link Classfile} objects created by scanning classfiles */ public ClassfileScannerWorkUnitProcessor(final ScanSpec scanSpec, - final List classpathOrder, final Set classNamesScheduledForScanning, + final List classpathOrder, final Set whitelistedClassNamesFound, final Queue scannedClassfiles) { this.scanSpec = scanSpec; this.classpathOrder = classpathOrder; - this.classNamesScheduledForScanning = classNamesScheduledForScanning; + this.whitelistedClassNamesFound = whitelistedClassNamesFound; this.scannedClassfiles = scannedClassfiles; } @@ -598,9 +607,9 @@ public void processWorkUnit(final ClassfileScanWorkUnit workUnit, try { // Parse classfile binary format, creating a Classfile object final Classfile classfile = new Classfile(workUnit.classpathElement, classpathOrder, - classNamesScheduledForScanning, workUnit.classfileResource.getPath(), - workUnit.classfileResource, workUnit.isExternalClass, stringInternMap, workQueue, scanSpec, - subLog); + whitelistedClassNamesFound, classNamesScheduledForExtendedScanning, + workUnit.classfileResource.getPath(), workUnit.classfileResource, workUnit.isExternalClass, + stringInternMap, workQueue, scanSpec, subLog); // Enqueue the classfile for linking scannedClassfiles.add(classfile); @@ -807,16 +816,15 @@ private ScanResult performScan(final List finalClasspathEltOrd if (scanSpec.enableClassInfo) { // Get whitelisted classfile order final List classfileScanWorkItems = new ArrayList<>(); - final Set classNamesScheduledForScanning = Collections + final Set whitelistedClassNamesFound = Collections .newSetFromMap(new ConcurrentHashMap()); for (final ClasspathElement classpathElement : finalClasspathEltOrder) { // Get classfile scan order across all classpath elements for (final Resource resource : classpathElement.whitelistedClassfileResources) { classfileScanWorkItems .add(new ClassfileScanWorkUnit(classpathElement, resource, /* isExternal = */ false)); - // Pre-seed scanned class names with all whitelisted classes (since these will - // be scanned for sure) - classNamesScheduledForScanning.add(JarUtils.classfilePathToClassName(resource.getPath())); + // Create a set of all names of classes found to be whitelisted among classpath element paths + whitelistedClassNamesFound.add(JarUtils.classfilePathToClassName(resource.getPath())); } } @@ -825,11 +833,12 @@ private ScanResult performScan(final List finalClasspathEltOrd processWorkUnits(classfileScanWorkItems, topLevelLog == null ? null : topLevelLog.log("Scanning classfiles"), new ClassfileScannerWorkUnitProcessor(scanSpec, finalClasspathEltOrder, - classNamesScheduledForScanning, scannedClassfiles)); + whitelistedClassNamesFound, scannedClassfiles)); // Link the Classfile objects to produce ClassInfo objects. This needs to be done from a single thread. final LogNode linkLog = topLevelLog == null ? null : topLevelLog.log("Linking related classfiles"); - for (final Classfile c : scannedClassfiles) { + while (!scannedClassfiles.isEmpty()) { + final Classfile c = scannedClassfiles.remove(); c.link(classNameToClassInfo, packageNameToPackageInfo, moduleNameToModuleInfo); } From 90c913c3e740cad37f33108d3e6e87f555710add Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 12 May 2019 03:05:15 -0600 Subject: [PATCH 0184/1778] More fixes for upwards extension of scanning --- .../java/io/github/classgraph/Classfile.java | 1 - .../java/io/github/classgraph/Scanner.java | 29 ++++++++++++++----- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index 998066ca9..0de03ad9c 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -268,7 +268,6 @@ private void scheduleScanningIfExternalClass(final String className, final Strin classResource.scanLog = log.log("Extending scanning to external (non-whitelisted) " + relationship + " " + className + (foundInClasspathElt == classpathElement ? "" : " -- found in classpath element " + foundInClasspathElt)); - } if (additionalWorkUnits == null) { additionalWorkUnits = new ArrayList<>(); diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 6b3804937..e1229f2ec 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -560,7 +560,8 @@ private static class ClassfileScannerWorkUnitProcessor implements WorkUnitProces * The names of external (non-whitelisted) classes scheduled for extended scanning (where scanning is * extended upwards to superclasses, interfaces and annotations). */ - private final Set classNamesScheduledForExtendedScanning = new HashSet<>(); + private final Set classNamesScheduledForExtendedScanning = Collections + .newSetFromMap(new ConcurrentHashMap()); /** The valid {@link Classfile} objects created by scanning classfiles. */ private final Queue scannedClassfiles; @@ -816,24 +817,36 @@ private ScanResult performScan(final List finalClasspathEltOrd if (scanSpec.enableClassInfo) { // Get whitelisted classfile order final List classfileScanWorkItems = new ArrayList<>(); - final Set whitelistedClassNamesFound = Collections - .newSetFromMap(new ConcurrentHashMap()); + final Set whitelistedClassNamesFound = new HashSet(); for (final ClasspathElement classpathElement : finalClasspathEltOrder) { // Get classfile scan order across all classpath elements for (final Resource resource : classpathElement.whitelistedClassfileResources) { + // Create a set of names of all whitelisted classes found in classpath element paths, + // and double-check that a class is not going to be scanned twice + final String className = JarUtils.classfilePathToClassName(resource.getPath()); + if (!whitelistedClassNamesFound.add(className) && !className.equals("module-info") + && !className.equals("package-info")) { + // The class should not be scheduled more than once for scanning, since classpath + // masking was already applied + throw new IllegalArgumentException("Class " + className + + " should not have been scheduled more than once for scanning due to classpath" + + " masking -- please report this bug at:" + + " https://github.com/classgraph/classgraph/issues"); + } + // Schedule class for scanning classfileScanWorkItems .add(new ClassfileScanWorkUnit(classpathElement, resource, /* isExternal = */ false)); - // Create a set of all names of classes found to be whitelisted among classpath element paths - whitelistedClassNamesFound.add(JarUtils.classfilePathToClassName(resource.getPath())); } } - // Scan classfiles in parallel. + // Scan classfiles in parallel final Queue scannedClassfiles = new ConcurrentLinkedQueue<>(); + final ClassfileScannerWorkUnitProcessor classfileWorkUnitProcessor = // + new ClassfileScannerWorkUnitProcessor(scanSpec, finalClasspathEltOrder, + Collections.unmodifiableSet(whitelistedClassNamesFound), scannedClassfiles); processWorkUnits(classfileScanWorkItems, topLevelLog == null ? null : topLevelLog.log("Scanning classfiles"), - new ClassfileScannerWorkUnitProcessor(scanSpec, finalClasspathEltOrder, - whitelistedClassNamesFound, scannedClassfiles)); + classfileWorkUnitProcessor); // Link the Classfile objects to produce ClassInfo objects. This needs to be done from a single thread. final LogNode linkLog = topLevelLog == null ? null : topLevelLog.log("Linking related classfiles"); From a0768ae8170d43f4d64cc0d56dbda5ff8a012501 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 12 May 2019 03:10:30 -0600 Subject: [PATCH 0185/1778] Extend scanning "upwards" to outer classes --- src/main/java/io/github/classgraph/Classfile.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index 0de03ad9c..7128686a6 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -100,7 +100,7 @@ class Classfile { /** The fully qualified name of the defining method. */ private String fullyQualifiedDefiningMethodName; - /** Class containment entries. */ + /** Class containment entries -- tuples of (inner class name, outer class name). */ private List> classContainmentEntries; /** Annotation default parameter values. */ @@ -338,6 +338,14 @@ private void extendScanningUpwards(final LogNode log) { } } } + // Check if this class is an inner class, and if so, extend scanning to outer class + if (classContainmentEntries != null) { + for (final SimpleEntry classContainmentEntry : classContainmentEntries) { + if (classContainmentEntry.getKey().equals(className)) { + scheduleScanningIfExternalClass(classContainmentEntry.getValue(), "outer class", log); + } + } + } } // ------------------------------------------------------------------------------------------------------------- From db2e8ab7c02c939b316fc9fb22848956875d2155 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 12 May 2019 03:13:45 -0600 Subject: [PATCH 0186/1778] Check blacklist before extending scanning upwards --- .../java/io/github/classgraph/Classfile.java | 81 ++++++++++--------- 1 file changed, 44 insertions(+), 37 deletions(-) diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index 7128686a6..c4c3d0e30 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -238,47 +238,54 @@ private void scheduleScanningIfExternalClass(final String className, final Strin && !whitelistedClassNamesFound.contains(className) // Only schedule each external class once for scanning, across all threads && classNamesScheduledForExtendedScanning.add(className)) { - // Search for the named class' classfile among classpath elements, in classpath order (this is O(N) - // for each class, but there shouldn't be too many cases of extending scanning upwards) - final String classfilePath = JarUtils.classNameToClassfilePath(className); - // First check current classpath element, to avoid iterating through other classpath elements - Resource classResource = classpathElement.getResource(classfilePath); - ClasspathElement foundInClasspathElt = null; - if (classResource != null) { - // Found the classfile in the current classpath element - foundInClasspathElt = classpathElement; + if (scanSpec.classWhiteBlackList.isBlacklisted(className)) { + if (log != null) { + log.log("Cannot extend scanning upwards to external " + relationship + " " + className + + ", since it is blacklisted"); + } } else { - // Didn't find the classfile in the current classpath element -- iterate through other elements - for (final ClasspathElement classpathOrderElt : classpathOrder) { - if (classpathOrderElt != classpathElement) { - classResource = classpathOrderElt.getResource(classfilePath); - if (classResource != null) { - foundInClasspathElt = classpathOrderElt; - break; + // Search for the named class' classfile among classpath elements, in classpath order (this is O(N) + // for each class, but there shouldn't be too many cases of extending scanning upwards) + final String classfilePath = JarUtils.classNameToClassfilePath(className); + // First check current classpath element, to avoid iterating through other classpath elements + Resource classResource = classpathElement.getResource(classfilePath); + ClasspathElement foundInClasspathElt = null; + if (classResource != null) { + // Found the classfile in the current classpath element + foundInClasspathElt = classpathElement; + } else { + // Didn't find the classfile in the current classpath element -- iterate through other elements + for (final ClasspathElement classpathOrderElt : classpathOrder) { + if (classpathOrderElt != classpathElement) { + classResource = classpathOrderElt.getResource(classfilePath); + if (classResource != null) { + foundInClasspathElt = classpathOrderElt; + break; + } } } } - } - if (classResource != null) { - // Found class resource - if (log != null) { - // Log the extended scan as a child LogNode of the current class' scan log, since the - // external class is not scanned at the regular place in the classpath element hierarchy - // traversal - classResource.scanLog = log.log("Extending scanning to external (non-whitelisted) " - + relationship + " " + className + (foundInClasspathElt == classpathElement ? "" - : " -- found in classpath element " + foundInClasspathElt)); - } - if (additionalWorkUnits == null) { - additionalWorkUnits = new ArrayList<>(); - } - // Schedule class resource for scanning - additionalWorkUnits.add(new ClassfileScanWorkUnit(foundInClasspathElt, classResource, - /* isExternalClass = */ true)); - } else { - if (log != null) { - log.log("External " + relationship + " " + className + " was not found in " - + "non-blacklisted packages -- cannot extend scanning to this class"); + if (classResource != null) { + // Found class resource + if (log != null) { + // Log the extended scan as a child LogNode of the current class' scan log, since the + // external class is not scanned at the regular place in the classpath element hierarchy + // traversal + classResource.scanLog = log.log("Extending scanning to external (non-whitelisted) " + + relationship + " " + className + (foundInClasspathElt == classpathElement ? "" + : " -- found in classpath element " + foundInClasspathElt)); + } + if (additionalWorkUnits == null) { + additionalWorkUnits = new ArrayList<>(); + } + // Schedule class resource for scanning + additionalWorkUnits.add(new ClassfileScanWorkUnit(foundInClasspathElt, classResource, + /* isExternalClass = */ true)); + } else { + if (log != null) { + log.log("External " + relationship + " " + className + " was not found in " + + "non-blacklisted packages -- cannot extend scanning to this class"); + } } } } From a70791e80788f1e712b57dc83edf724f27e1841e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 12 May 2019 03:47:27 -0600 Subject: [PATCH 0187/1778] Improve logging --- src/main/java/io/github/classgraph/Classfile.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index c4c3d0e30..b4bf4b73e 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -271,9 +271,11 @@ private void scheduleScanningIfExternalClass(final String className, final Strin // Log the extended scan as a child LogNode of the current class' scan log, since the // external class is not scanned at the regular place in the classpath element hierarchy // traversal - classResource.scanLog = log.log("Extending scanning to external (non-whitelisted) " - + relationship + " " + className + (foundInClasspathElt == classpathElement ? "" - : " -- found in classpath element " + foundInClasspathElt)); + classResource.scanLog = log + .log("Extending scanning to external " + relationship + + (foundInClasspathElt == classpathElement ? " in same classpath element" + : " in classpath element " + foundInClasspathElt) + + ": " + className); } if (additionalWorkUnits == null) { additionalWorkUnits = new ArrayList<>(); From a1d9e9d3b3ad02908e293ad9cb63008e94042ad2 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 12 May 2019 04:19:06 -0600 Subject: [PATCH 0188/1778] Search superclasses of ClassLoaders for ClassLoaderHandler match --- .../AntClassLoaderHandler.java | 8 ++++---- .../ClassLoaderHandlerRegistry.java | 4 ++-- .../EquinoxClassLoaderHandler.java | 8 ++++---- ...quinoxContextFinderClassLoaderHandler.java | 8 ++++---- .../FallbackClassLoaderHandler.java | 6 +++--- .../FelixClassLoaderHandler.java | 10 +++++----- .../JBossClassLoaderHandler.java | 8 ++++---- .../JPMSClassLoaderHandler.java | 10 +++++----- .../OSGiDefaultClassLoaderHandler.java | 8 ++++---- ...DelegationOrderTestClassLoaderHandler.java | 9 ++++----- ...assWorldsClassRealmClassLoaderHandler.java | 8 ++++---- .../SpringBootRestartClassLoaderHandler.java | 8 ++++---- .../TomcatWebappClassLoaderBaseHandler.java | 8 ++++---- .../URLClassLoaderHandler.java | 8 ++++---- .../WeblogicClassLoaderHandler.java | 16 ++++++++-------- .../WebsphereLibertyClassLoaderHandler.java | 10 +++++----- ...ebsphereTraditionalClassLoaderHandler.java | 12 ++++++------ .../classpath/ClassLoaderOrder.java | 19 ++++++++++++++----- .../classgraph/classpath/ClasspathFinder.java | 12 +++++++++++- 19 files changed, 99 insertions(+), 81 deletions(-) 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 8cac4c0d1..33b80e0b6 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/AntClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/AntClassLoaderHandler.java @@ -43,12 +43,12 @@ private AntClassLoaderHandler() { /** * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. * - * @param classLoader - * the {@link ClassLoader}. + * @param classLoaderClass + * the {@link ClassLoader} class or one of its superclasses. * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ - public static boolean canHandle(final ClassLoader classLoader) { - return "org.apache.tools.ant.AntClassLoader".equals(classLoader.getClass().getName()); + public static boolean canHandle(final Class classLoaderClass) { + return "org.apache.tools.ant.AntClassLoader".equals(classLoaderClass.getName()); } /** 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 69cff6f00..87a839636 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java @@ -141,7 +141,7 @@ public static class ClassLoaderHandlerRegistryEntry { private ClassLoaderHandlerRegistryEntry(final Class classLoaderHandlerClass) { this.classLoaderHandlerClass = classLoaderHandlerClass; try { - canHandleMethod = classLoaderHandlerClass.getDeclaredMethod("canHandle", ClassLoader.class); + canHandleMethod = classLoaderHandlerClass.getDeclaredMethod("canHandle", Class.class); } catch (final Exception e) { throw new RuntimeException( "Could not find canHandle method for " + classLoaderHandlerClass.getName(), e); @@ -169,7 +169,7 @@ private ClassLoaderHandlerRegistryEntry(final Class classLoader) { try { return (boolean) canHandleMethod.invoke(null, classLoader); } catch (final Throwable e) { 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 37a9bbced..b34f3a940 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxClassLoaderHandler.java @@ -62,12 +62,12 @@ private EquinoxClassLoaderHandler() { /** * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. * - * @param classLoader - * the {@link ClassLoader}. + * @param classLoaderClass + * the {@link ClassLoader} class or one of its superclasses. * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ - public static boolean canHandle(final ClassLoader classLoader) { - return "org.eclipse.osgi.internal.loader.EquinoxClassLoader".equals(classLoader.getClass().getName()); + public static boolean canHandle(final Class classLoaderClass) { + return "org.eclipse.osgi.internal.loader.EquinoxClassLoader".equals(classLoaderClass.getName()); } /** 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 165a8aad7..8d204f3ad 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxContextFinderClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxContextFinderClassLoaderHandler.java @@ -43,12 +43,12 @@ private EquinoxContextFinderClassLoaderHandler() { /** * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. * - * @param classLoader - * the {@link ClassLoader}. + * @param classLoaderClass + * the {@link ClassLoader} class or one of its superclasses. * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ - public static boolean canHandle(final ClassLoader classLoader) { - return "org.eclipse.osgi.internal.framework.ContextFinder".equals(classLoader.getClass().getName()); + public static boolean canHandle(final Class classLoaderClass) { + return "org.eclipse.osgi.internal.framework.ContextFinder".equals(classLoaderClass.getName()); } /** 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 0e1f68c28..38155d80f 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FallbackClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FallbackClassLoaderHandler.java @@ -45,11 +45,11 @@ private FallbackClassLoaderHandler() { /** * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. * - * @param classLoader - * the {@link ClassLoader}. + * @param classLoaderClass + * the {@link ClassLoader} class or one of its superclasses. * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ - public static boolean canHandle(final ClassLoader classLoader) { + public static boolean canHandle(final Class classLoaderClass) { // This is the fallback handler, it handles anything return true; } 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 477acf61b..24b40c31f 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FelixClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FelixClassLoaderHandler.java @@ -55,15 +55,15 @@ private FelixClassLoaderHandler() { /** * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. * - * @param classLoader - * the {@link ClassLoader}. + * @param classLoaderClass + * the {@link ClassLoader} class or one of its superclasses. * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ - public static boolean canHandle(final ClassLoader classLoader) { + public static boolean canHandle(final Class classLoaderClass) { return "org.apache.felix.framework.BundleWiringImpl$BundleClassLoaderJava5" - .equals(classLoader.getClass().getName()) + .equals(classLoaderClass.getName()) || "org.apache.felix.framework.BundleWiringImpl$BundleClassLoader" - .equals(classLoader.getClass().getName()); + .equals(classLoaderClass.getName()); } /** 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 df148c81f..e186bbd5a 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java @@ -58,12 +58,12 @@ private JBossClassLoaderHandler() { /** * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. * - * @param classLoader - * the {@link ClassLoader}. + * @param classLoaderClass + * the {@link ClassLoader} class or one of its superclasses. * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ - public static boolean canHandle(final ClassLoader classLoader) { - return "org.jboss.modules.ModuleClassLoader".equals(classLoader.getClass().getName()); + public static boolean canHandle(final Class classLoaderClass) { + return "org.jboss.modules.ModuleClassLoader".equals(classLoaderClass.getName()); } /** 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 2dc092abf..f60e2c4f1 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JPMSClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JPMSClassLoaderHandler.java @@ -45,13 +45,13 @@ private JPMSClassLoaderHandler() { /** * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. * - * @param classLoader - * the {@link ClassLoader}. + * @param classLoaderClass + * the {@link ClassLoader} class or one of its superclasses. * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ - public static boolean canHandle(final ClassLoader classLoader) { - return "jdk.internal.loader.ClassLoaders$AppClassLoader".equals(classLoader.getClass().getName()) - || "jdk.internal.loader.BuiltinClassLoader".equals(classLoader.getClass().getName()); + public static boolean canHandle(final Class classLoaderClass) { + return "jdk.internal.loader.ClassLoaders$AppClassLoader".equals(classLoaderClass.getName()) + || "jdk.internal.loader.BuiltinClassLoader".equals(classLoaderClass.getName()); } /** 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 60bc6e138..69df3b4a2 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/OSGiDefaultClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/OSGiDefaultClassLoaderHandler.java @@ -49,12 +49,12 @@ private OSGiDefaultClassLoaderHandler() { /** * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. * - * @param classLoader - * the {@link ClassLoader}. + * @param classLoaderClass + * the {@link ClassLoader} class or one of its superclasses. * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ - public static boolean canHandle(final ClassLoader classLoader) { - return "org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader".equals(classLoader.getClass().getName()); + public static boolean canHandle(final Class classLoaderClass) { + return "org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader".equals(classLoaderClass.getName()); } /** 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 c3df996a6..053bcf2b5 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ParentLastDelegationOrderTestClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ParentLastDelegationOrderTestClassLoaderHandler.java @@ -43,13 +43,12 @@ private ParentLastDelegationOrderTestClassLoaderHandler() { /** * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. * - * @param classLoader - * the {@link ClassLoader}. + * @param classLoaderClass + * the {@link ClassLoader} class or one of its superclasses. * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ - public static boolean canHandle(final ClassLoader classLoader) { - return "io.github.classgraph.issues.issue267.FakeRestartClassLoader" - .equals(classLoader.getClass().getName()); + public static boolean canHandle(final Class classLoaderClass) { + return "io.github.classgraph.issues.issue267.FakeRestartClassLoader".equals(classLoaderClass.getName()); } /** 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 59a481ed3..df42dd3b7 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/PlexusClassWorldsClassRealmClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/PlexusClassWorldsClassRealmClassLoaderHandler.java @@ -49,12 +49,12 @@ private PlexusClassWorldsClassRealmClassLoaderHandler() { /** * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. * - * @param classLoader - * the {@link ClassLoader}. + * @param classLoaderClass + * the {@link ClassLoader} class or one of its superclasses. * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ - public static boolean canHandle(final ClassLoader classLoader) { - return "org.codehaus.plexus.classworlds.realm.ClassRealm".equals(classLoader.getClass().getName()); + public static boolean canHandle(final Class classLoaderClass) { + return "org.codehaus.plexus.classworlds.realm.ClassRealm".equals(classLoaderClass.getName()); } /** 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 b00d1e406..be69d8ae2 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/SpringBootRestartClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/SpringBootRestartClassLoaderHandler.java @@ -49,13 +49,13 @@ private SpringBootRestartClassLoaderHandler() { /** * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. * - * @param classLoader - * the {@link ClassLoader}. + * @param classLoaderClass + * the {@link ClassLoader} class or one of its superclasses. * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ - public static boolean canHandle(final ClassLoader classLoader) { + public static boolean canHandle(final Class classLoaderClass) { return "org.springframework.boot.devtools.restart.classloader.RestartClassLoader" - .equals(classLoader.getClass().getName()); + .equals(classLoaderClass.getName()); } /** 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 3cb73057f..5c23581ed 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java @@ -46,12 +46,12 @@ private TomcatWebappClassLoaderBaseHandler() { /** * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. * - * @param classLoader - * the {@link ClassLoader}. + * @param classLoaderClass + * the {@link ClassLoader} class or one of its superclasses. * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ - public static boolean canHandle(final ClassLoader classLoader) { - return "org.apache.catalina.loader.WebappClassLoaderBase".equals(classLoader.getClass().getName()); + public static boolean canHandle(final Class classLoaderClass) { + return "org.apache.catalina.loader.WebappClassLoaderBase".equals(classLoaderClass.getName()); } /** 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 d3c4bb954..0eccd9ef8 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/URLClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/URLClassLoaderHandler.java @@ -45,12 +45,12 @@ private URLClassLoaderHandler() { /** * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. * - * @param classLoader - * the {@link ClassLoader}. + * @param classLoaderClass + * the {@link ClassLoader} class or one of its superclasses. * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ - public static boolean canHandle(final ClassLoader classLoader) { - return "java.net.URLClassLoader".equals(classLoader.getClass().getName()); + public static boolean canHandle(final Class classLoaderClass) { + return "java.net.URLClassLoader".equals(classLoaderClass.getName()); } /** 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 c5c8d2bc1..b98e61c55 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WeblogicClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WeblogicClassLoaderHandler.java @@ -43,18 +43,18 @@ private WeblogicClassLoaderHandler() { /** * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. * - * @param classLoader - * the {@link ClassLoader}. + * @param classLoaderClass + * the {@link ClassLoader} class or one of its superclasses. * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ - public static boolean canHandle(final ClassLoader classLoader) { - return "weblogic.utils.classloaders.ChangeAwareClassLoader".equals(classLoader.getClass().getName()) - || "weblogic.utils.classloaders.GenericClassLoader".equals(classLoader.getClass().getName()) - || "weblogic.utils.classloaders.FilteringClassLoader".equals(classLoader.getClass().getName()) + public static boolean canHandle(final Class classLoaderClass) { + return "weblogic.utils.classloaders.ChangeAwareClassLoader".equals(classLoaderClass.getName()) + || "weblogic.utils.classloaders.GenericClassLoader".equals(classLoaderClass.getName()) + || "weblogic.utils.classloaders.FilteringClassLoader".equals(classLoaderClass.getName()) // 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(classLoader.getClass().getName()) - || "weblogic.servlet.jsp.TagFileClassLoader".equals(classLoader.getClass().getName()); + || "weblogic.servlet.jsp.JspClassLoader".equals(classLoaderClass.getName()) + || "weblogic.servlet.jsp.TagFileClassLoader".equals(classLoaderClass.getName()); } /** 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 997024728..2a0bd02ce 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java @@ -62,13 +62,13 @@ private WebsphereLibertyClassLoaderHandler() { /** * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. * - * @param classLoader - * the {@link ClassLoader}. + * @param classLoaderClass + * the {@link ClassLoader} class or one of its superclasses. * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ - public static boolean canHandle(final ClassLoader classLoader) { - return IBM_APP_CLASS_LOADER.equals(classLoader.getClass().getName()) - || IBM_THREAD_CONTEXT_CLASS_LOADER.equals(classLoader.getClass().getName()); + public static boolean canHandle(final Class classLoaderClass) { + return IBM_APP_CLASS_LOADER.equals(classLoaderClass.getName()) + || IBM_THREAD_CONTEXT_CLASS_LOADER.equals(classLoaderClass.getName()); } /** 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 678eaf0ec..386e6f7cb 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereTraditionalClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereTraditionalClassLoaderHandler.java @@ -47,14 +47,14 @@ private WebsphereTraditionalClassLoaderHandler() { /** * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. * - * @param classLoader - * the {@link ClassLoader}. + * @param classLoaderClass + * the {@link ClassLoader} class or one of its superclasses. * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ - public static boolean canHandle(final ClassLoader classLoader) { - return "com.ibm.ws.classloader.CompoundClassLoader".equals(classLoader.getClass().getName()) - || "com.ibm.ws.classloader.ProtectionClassLoader".equals(classLoader.getClass().getName()) - || "com.ibm.ws.bootstrap.ExtClassLoader".equals(classLoader.getClass().getName()); + public static boolean canHandle(final Class classLoaderClass) { + 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()); } /** 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 b6e5c49c7..febc64105 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderOrder.java @@ -98,11 +98,20 @@ public Set getAllParentClassLoaders() { private ClassLoaderHandlerRegistryEntry getRegistryEntry(final ClassLoader classLoader) { ClassLoaderHandlerRegistryEntry entry = classLoaderToClassLoaderHandlerRegistryEntry.get(classLoader); if (entry == null) { - // Find a ClassLoaderHandler that can handle the ClassLoader - for (final ClassLoaderHandlerRegistryEntry handler : ClassLoaderHandlerRegistry.CLASS_LOADER_HANDLERS) { - if (handler.canHandle(classLoader)) { - // This ClassLoaderHandler can handle the ClassLoader - entry = handler; + // 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)) { + // 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; } } 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 5399bb80e..a09a0533b 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java @@ -100,7 +100,6 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { // If system jars are not blacklisted, add JRE rt.jar to the beginning of the classpath final String jreRtJar = SystemJarFinder.getJreRtJarPath(); final boolean scanAllLibOrExtJars = !scanSpec.libOrExtJarWhiteBlackList.whitelistAndBlacklistAreEmpty(); - final LogNode systemJarsLog = classpathFinderLog == null ? null : classpathFinderLog.log("System jars:"); classLoaderAndModuleFinder = new ClassLoaderAndModuleFinder(scanSpec, classpathFinderLog); @@ -130,6 +129,8 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { } else { // Add rt.jar and/or lib/ext jars to beginning of classpath, if enabled + final LogNode systemJarsLog = classpathFinderLog == null ? null + : classpathFinderLog.log("System jars:"); if (jreRtJar != null) { if (scanSpec.enableSystemJarsAndModules) { classpathOrder.addSystemClasspathEntry(jreRtJar, defaultClassLoader); @@ -170,6 +171,15 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { } } + if (classpathFinderLog != null) { + final LogNode classloaderOrderLog = classpathFinderLog.log("ClassLoader delegation order:"); + for (final Entry cl : classLoaderOrder + .getClassLoaderOrder()) { + classloaderOrderLog.log(cl.getKey().toString()) + .log("Handled by " + cl.getValue().classLoaderHandlerClass.getName()); + } + } + // Get all parent classloaders final Set allParentClassLoaders = classLoaderOrder.getAllParentClassLoaders(); From 0fea1defcb7977edc15930fa6e3bb48e25ee3527 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 12 May 2019 04:52:59 -0600 Subject: [PATCH 0189/1778] Don't get irrelevant URLs if overrideClassLoaders() called --- .../java/io/github/classgraph/Scanner.java | 16 ++-- .../classpath/ClassLoaderAndModuleFinder.java | 75 ++++++++++--------- .../classpath/ClassLoaderOrder.java | 2 + .../classgraph/classpath/ClasspathFinder.java | 29 +++---- 4 files changed, 67 insertions(+), 55 deletions(-) diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index e1229f2ec..5d1f134eb 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -104,7 +104,7 @@ class Scanner implements Callable { private final ClassLoaderAndModuleFinder classLoaderAndModuleFinder; /** The module order. */ - private final List moduleClasspathEltOrder; + private final List moduleOrder; /** The environment classloader order, respecting parent-first or parent-last delegation order. */ private final ClassLoader[] classLoaderOrderRespectingParentDelegation; @@ -158,7 +158,7 @@ class Scanner implements Callable { this.classLoaderAndModuleFinder = classpathFinder.getClassLoaderAndModuleFinder(); this.classLoaderOrderRespectingParentDelegation = classpathFinder .getClassLoaderOrderRespectingParentDelegation(); - this.moduleClasspathEltOrder = getModuleOrder(classpathFinderLog); + this.moduleOrder = getModuleOrder(classpathFinderLog); } // ------------------------------------------------------------------------------------------------------------- @@ -173,7 +173,7 @@ class Scanner implements Callable { * if interrupted */ private List getModuleOrder(final LogNode log) throws InterruptedException { - final List moduleCpEltOrder = new ArrayList<>(); + final List moduleOrder = new ArrayList<>(); if (scanSpec.overrideClasspath == null && scanSpec.overrideClassLoaders == null && scanSpec.scanModules) { // Add modules to start of classpath order, before traditional classpath final List systemModuleRefs = classLoaderAndModuleFinder.getSystemModuleRefs(); @@ -195,7 +195,7 @@ private List getModuleOrder(final LogNode log) throws In // Create a new ClasspathElementModule final ClasspathElementModule classpathElementModule = new ClasspathElementModule( systemModuleRef, defaultClassLoader, nestedJarHandler, scanSpec); - moduleCpEltOrder.add(classpathElementModule); + moduleOrder.add(classpathElementModule); // Open the ClasspathElementModule classpathElementModule.open(/* ignored */ null, log); } else { @@ -216,7 +216,7 @@ private List getModuleOrder(final LogNode log) throws In // Create a new ClasspathElementModule final ClasspathElementModule classpathElementModule = new ClasspathElementModule( nonSystemModuleRef, defaultClassLoader, nestedJarHandler, scanSpec); - moduleCpEltOrder.add(classpathElementModule); + moduleOrder.add(classpathElementModule); // Open the ClasspathElementModule classpathElementModule.open(/* ignored */ null, log); } else { @@ -227,7 +227,7 @@ private List getModuleOrder(final LogNode log) throws In } } } - return moduleCpEltOrder; + return moduleOrder; } // ------------------------------------------------------------------------------------------------------------- @@ -937,10 +937,10 @@ private ScanResult openClasspathElementsThenScan() throws InterruptedException, // Order modules before classpath elements from traditional classpath final LogNode classpathOrderLog = topLevelLog == null ? null : topLevelLog.log("Final classpath element order:"); - final int numElts = moduleClasspathEltOrder.size() + classpathEltOrder.size(); + final int numElts = moduleOrder.size() + classpathEltOrder.size(); final List finalClasspathEltOrder = new ArrayList<>(numElts); final List finalClasspathEltOrderStrs = new ArrayList<>(numElts); - for (final ClasspathElementModule classpathElt : moduleClasspathEltOrder) { + for (final ClasspathElementModule classpathElt : moduleOrder) { finalClasspathEltOrder.add(classpathElt); finalClasspathEltOrderStrs.add(classpathElt.toString()); if (classpathOrderLog != null) { diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderAndModuleFinder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderAndModuleFinder.java index a52563170..fb59fbb1b 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderAndModuleFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderAndModuleFinder.java @@ -255,6 +255,7 @@ private static List findModuleRefsFromCallstack(final Class[] call * The log. */ ClassLoaderAndModuleFinder(final ScanSpec scanSpec, final LogNode log) { + final boolean disableModules = scanSpec.overrideClassLoaders != null || scanSpec.overrideClasspath != null; LinkedHashSet classLoadersUnique; LogNode classLoadersFoundLog; if (scanSpec.overrideClassLoaders == null) { @@ -310,30 +311,32 @@ private static List findModuleRefsFromCallstack(final Class[] call } } - // Get the module resolution order - List allModuleRefsList = null; - if (scanSpec.overrideModuleLayers == null) { - // Find module references for classes on callstack, and from system (for JDK9+) - allModuleRefsList = findModuleRefsFromCallstack(callStack, scanSpec, log); - } else { - if (log != null) { - final LogNode subLog = log.log("Overriding module layers"); - for (final Object moduleLayer : scanSpec.overrideModuleLayers) { - subLog.log(moduleLayer.toString()); + if (!disableModules) { + // Get the module resolution order + List allModuleRefsList = null; + if (scanSpec.overrideModuleLayers == null) { + // Find module references for classes on callstack, and from system (for JDK9+) + allModuleRefsList = findModuleRefsFromCallstack(callStack, scanSpec, 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); } - 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.isSystemModule()) { - systemModuleRefs.add(moduleRef); - } else { - nonSystemModuleRefs.add(moduleRef); + 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.isSystemModule()) { + systemModuleRefs.add(moduleRef); + } else { + nonSystemModuleRefs.add(moduleRef); + } } } } @@ -359,21 +362,25 @@ private static List findModuleRefsFromCallstack(final Class[] call // Log any identified modules if (log != null) { - final LogNode sysSubLog = log.log("Found system modules:"); - if (systemModuleRefs != null && !systemModuleRefs.isEmpty()) { - for (final ModuleRef moduleRef : systemModuleRefs) { - sysSubLog.log(moduleRef.toString()); + if (!disableModules) { + final LogNode sysSubLog = log.log("Found system modules:"); + if (systemModuleRefs != null && !systemModuleRefs.isEmpty()) { + for (final ModuleRef moduleRef : systemModuleRefs) { + sysSubLog.log(moduleRef.toString()); + } + } else { + sysSubLog.log("[None]"); } - } else { - sysSubLog.log("[None]"); - } - final LogNode nonSysSubLog = log.log("Found non-system modules:"); - if (nonSystemModuleRefs != null && !nonSystemModuleRefs.isEmpty()) { - for (final ModuleRef moduleRef : nonSystemModuleRefs) { - nonSysSubLog.log(moduleRef.toString()); + final LogNode nonSysSubLog = log.log("Found non-system modules:"); + if (nonSystemModuleRefs != null && !nonSystemModuleRefs.isEmpty()) { + for (final ModuleRef moduleRef : nonSystemModuleRefs) { + nonSysSubLog.log(moduleRef.toString()); + } + } else { + nonSysSubLog.log("[None]"); } } else { - nonSysSubLog.log("[None]"); + log.log("Module scanning is disabled, because classloaders or classpath was overridden"); } } 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 febc64105..b80263cce 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderOrder.java @@ -147,6 +147,8 @@ public void add(final ClassLoader classLoader) { * * @param classLoader * the class loader + * @param isParent + * true if this is a parent of another classloader */ public void delegateTo(final ClassLoader classLoader, final boolean isParent) { if (classLoader == null) { 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 a09a0533b..7a5ea35ad 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java @@ -165,25 +165,21 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { // Find all unique classloaders, in delegation order final ClassLoaderOrder classLoaderOrder = new ClassLoaderOrder(); - if (contextClassLoaders != null) { - for (final ClassLoader classLoader : contextClassLoaders) { + final ClassLoader[] origClassLoaderOrder = scanSpec.overrideClassLoaders != null + ? scanSpec.overrideClassLoaders.toArray(new ClassLoader[0]) + : contextClassLoaders; + if (origClassLoaderOrder != null) { + for (final ClassLoader classLoader : origClassLoaderOrder) { classLoaderOrder.delegateTo(classLoader, /* isParent = */ false); } } - if (classpathFinderLog != null) { - final LogNode classloaderOrderLog = classpathFinderLog.log("ClassLoader delegation order:"); - for (final Entry cl : classLoaderOrder - .getClassLoaderOrder()) { - classloaderOrderLog.log(cl.getKey().toString()) - .log("Handled by " + cl.getValue().classLoaderHandlerClass.getName()); - } - } - // Get all parent classloaders final Set allParentClassLoaders = classLoaderOrder.getAllParentClassLoaders(); // Get the classpath URLs from each ClassLoader + final LogNode classloaderOrderLog = classpathFinderLog == null ? null + : classpathFinderLog.log("Obtaining URLs from classloaders in delegation order:"); final List finalClassLoaderOrder = new ArrayList<>(); for (final Entry ent : classLoaderOrder .getClassLoaderOrder()) { @@ -193,13 +189,20 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { if (scanSpec.ignoreParentClassLoaders && allParentClassLoaders.contains(classLoader)) { // If this is a parent and parent classloaders are being ignored, add classpath entries // to ignoredClasspathOrder + final LogNode classloaderURLLog = classloaderOrderLog == null ? null + : classloaderOrderLog + .log("Ignoring parent classloader " + classLoader + ", normally handled by " + + classLoaderHandlerRegistryEntry.classLoaderHandlerClass.getName()); classLoaderHandlerRegistryEntry.findClasspathOrder(classLoader, ignoredClasspathOrder, scanSpec, - classpathFinderLog); + classloaderURLLog); } else { // Otherwise add classpath entries to classpathOrder, and add the classloader to the // final classloader ordering + final LogNode classloaderURLLog = classloaderOrderLog == null ? null + : classloaderOrderLog.log("Classloader " + classLoader + " is handled by " + + classLoaderHandlerRegistryEntry.classLoaderHandlerClass.getName()); classLoaderHandlerRegistryEntry.findClasspathOrder(classLoader, classpathOrder, scanSpec, - classpathFinderLog); + classloaderURLLog); finalClassLoaderOrder.add(classLoader); } } From af1da04c9eda8766796a43834cd6410802bbd969 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 12 May 2019 05:07:40 -0600 Subject: [PATCH 0190/1778] Refactoring --- .../java/io/github/classgraph/Scanner.java | 12 +- .../classpath/ClassLoaderFinder.java | 150 +++++++++++++++++ .../classgraph/classpath/ClasspathFinder.java | 16 +- ...AndModuleFinder.java => ModuleFinder.java} | 156 ++++-------------- 4 files changed, 200 insertions(+), 134 deletions(-) create mode 100644 src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderFinder.java rename src/main/java/nonapi/io/github/classgraph/classpath/{ClassLoaderAndModuleFinder.java => ModuleFinder.java} (65%) diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 5d1f134eb..9863e763b 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -54,8 +54,8 @@ import io.github.classgraph.ClassGraph.ScanResultProcessor; import io.github.classgraph.Classfile.ClassfileFormatException; import io.github.classgraph.Classfile.SkipClassException; -import nonapi.io.github.classgraph.classpath.ClassLoaderAndModuleFinder; import nonapi.io.github.classgraph.classpath.ClasspathFinder; +import nonapi.io.github.classgraph.classpath.ModuleFinder; import nonapi.io.github.classgraph.concurrency.AutoCloseableExecutorService; import nonapi.io.github.classgraph.concurrency.InterruptionChecker; import nonapi.io.github.classgraph.concurrency.SingletonMap; @@ -100,8 +100,8 @@ class Scanner implements Callable { /** The classpath finder. */ private final ClasspathFinder classpathFinder; - /** The classloader and module finder. */ - private final ClassLoaderAndModuleFinder classLoaderAndModuleFinder; + /** The module finder. */ + private final ModuleFinder moduleFinder; /** The module order. */ private final List moduleOrder; @@ -155,7 +155,7 @@ class Scanner implements Callable { final LogNode classpathFinderLog = topLevelLog == null ? null : topLevelLog.log("Finding classpath"); this.classpathFinder = new ClasspathFinder(scanSpec, classpathFinderLog); - this.classLoaderAndModuleFinder = classpathFinder.getClassLoaderAndModuleFinder(); + this.moduleFinder = new ModuleFinder(classpathFinder.getCallStack(), scanSpec, classpathFinderLog); this.classLoaderOrderRespectingParentDelegation = classpathFinder .getClassLoaderOrderRespectingParentDelegation(); this.moduleOrder = getModuleOrder(classpathFinderLog); @@ -176,7 +176,7 @@ private List getModuleOrder(final LogNode log) throws In final List moduleOrder = new ArrayList<>(); if (scanSpec.overrideClasspath == null && scanSpec.overrideClassLoaders == null && scanSpec.scanModules) { // Add modules to start of classpath order, before traditional classpath - final List systemModuleRefs = classLoaderAndModuleFinder.getSystemModuleRefs(); + final List systemModuleRefs = moduleFinder.getSystemModuleRefs(); final ClassLoader defaultClassLoader = classLoaderOrderRespectingParentDelegation != null && classLoaderOrderRespectingParentDelegation.length != 0 ? classLoaderOrderRespectingParentDelegation[0] @@ -205,7 +205,7 @@ private List getModuleOrder(final LogNode log) throws In } } } - final List nonSystemModuleRefs = classLoaderAndModuleFinder.getNonSystemModuleRefs(); + final List nonSystemModuleRefs = moduleFinder.getNonSystemModuleRefs(); if (nonSystemModuleRefs != null) { for (final ModuleRef nonSystemModuleRef : nonSystemModuleRefs) { String moduleName = nonSystemModuleRef.getName(); diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderFinder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderFinder.java new file mode 100644 index 000000000..51f3aa6d5 --- /dev/null +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderFinder.java @@ -0,0 +1,150 @@ +/* + * 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.classpath; + +import java.util.LinkedHashSet; + +import nonapi.io.github.classgraph.scanspec.ScanSpec; +import nonapi.io.github.classgraph.utils.LogNode; + +/** A class to find the unique ordered classpath elements. */ +public class ClassLoaderFinder { + /** The context class loaders. */ + private final ClassLoader[] contextClassLoaders; + + /** The callstack. */ + private Class[] callStack; + + // ------------------------------------------------------------------------------------------------------------- + + /** + * Get the context class loaders. + * + * @return The context classloader, and any other classloader that is not an ancestor of context classloader. + */ + public ClassLoader[] getContextClassLoaders() { + return contextClassLoaders; + } + + /** + * Get classes in the callstack. + * + * @return classes in the callstack. + */ + public Class[] getCallStack() { + return callStack; + } + + // ------------------------------------------------------------------------------------------------------------- + + /** + * A class to find the unique ordered classpath elements. + * + * @param scanSpec + * The scan spec, or null if none available. + * @param log + * The log. + */ + ClassLoaderFinder(final ScanSpec scanSpec, final LogNode log) { + LinkedHashSet classLoadersUnique; + LogNode classLoadersFoundLog; + if (scanSpec.overrideClassLoaders == null) { + // ClassLoaders were not overridden + + // There's some advice here about choosing the best or the right classloader, but it is not complete + // (e.g. it doesn't cover parent delegation modes): + // http://www.javaworld.com/article/2077344/core-java/find-a-way-out-of-the-classloader-maze.html?page=2 + + // Get thread context classloader (this is the first classloader to try, since a context classloader + // can be set as an override on a per-thread basis) + classLoadersUnique = new LinkedHashSet<>(); + final ClassLoader threadClassLoader = Thread.currentThread().getContextClassLoader(); + if (threadClassLoader != null) { + classLoadersUnique.add(threadClassLoader); + } + + // Get classloader for this class, which will generally be the classloader of the class that + // called ClassGraph (the classloader of the caller is used by Class.forName(className), when + // no classloader is provided) + final ClassLoader currClassClassLoader = getClass().getClassLoader(); + if (currClassClassLoader != null) { + classLoadersUnique.add(currClassClassLoader); + } + + // Get system classloader (this is a fallback if one of the above do not work) + final ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); + if (systemClassLoader != null) { + classLoadersUnique.add(systemClassLoader); + } + + // There is one more classloader in JDK9+, the platform classloader (used for handling extensions), + // see: http://openjdk.java.net/jeps/261#Class-loaders + // The method call to get it is ClassLoader.getPlatformClassLoader() + // However, since it's not possible to get URLs from this classloader, and it is the parent of + // the application classloader returned by ClassLoader.getSystemClassLoader() (so is delegated to + // by the application classloader), there is no point adding it here. Modules are scanned + // directly anyway, so we don't need to get module path entries from the platform classloader. + + // Find classloaders for classes on callstack, in case any were missed + try { + callStack = CallStackReader.getClassContext(log); + for (int i = callStack.length - 1; i >= 0; --i) { + final ClassLoader callerClassLoader = callStack[i].getClassLoader(); + if (callerClassLoader != null) { + classLoadersUnique.add(callerClassLoader); + } + } + } catch (final IllegalArgumentException e) { + if (log != null) { + log.log("Could not get call stack", e); + } + } + + // Add any custom-added classloaders after system/context/module classloaders + if (scanSpec.addedClassLoaders != null) { + classLoadersUnique.addAll(scanSpec.addedClassLoaders); + } + classLoadersFoundLog = log == null ? null : log.log("Found ClassLoaders:"); + + } else { + // ClassLoaders were overridden + classLoadersUnique = new LinkedHashSet<>(scanSpec.overrideClassLoaders); + classLoadersFoundLog = log == null ? null : log.log("Override ClassLoaders:"); + } + + // Log all identified ClassLoaders + if (classLoadersFoundLog != null) { + for (final ClassLoader classLoader : classLoadersUnique) { + classLoadersFoundLog.log(classLoader.getClass().getName()); + } + } + + this.contextClassLoaders = classLoadersUnique.toArray(new ClassLoader[0]); + } +} 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 7a5ea35ad..65ef8060b 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java @@ -46,8 +46,8 @@ public class ClasspathFinder { /** The classpath order. */ private final ClasspathOrder classpathOrder; - /** The classloader and module finder. */ - private final ClassLoaderAndModuleFinder classLoaderAndModuleFinder; + /** The classloader finder. */ + private final ClassLoaderFinder classLoaderFinder; /** * The default order in which ClassLoaders are called to load classes, respecting parent-first/parent-last @@ -67,12 +67,12 @@ public ClasspathOrder getClasspathOrder() { } /** - * Get the classloader and module finder. + * Get the callstack. * - * @return The {@link ClassLoaderAndModuleFinder}. + * @return The callstack. */ - public ClassLoaderAndModuleFinder getClassLoaderAndModuleFinder() { - return classLoaderAndModuleFinder; + public Class[] getCallStack() { + return classLoaderFinder.getCallStack(); } /** @@ -101,12 +101,12 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { final String jreRtJar = SystemJarFinder.getJreRtJarPath(); final boolean scanAllLibOrExtJars = !scanSpec.libOrExtJarWhiteBlackList.whitelistAndBlacklistAreEmpty(); - classLoaderAndModuleFinder = new ClassLoaderAndModuleFinder(scanSpec, classpathFinderLog); + classLoaderFinder = new ClassLoaderFinder(scanSpec, classpathFinderLog); classpathOrder = new ClasspathOrder(scanSpec); final ClasspathOrder ignoredClasspathOrder = new ClasspathOrder(scanSpec); - final ClassLoader[] contextClassLoaders = classLoaderAndModuleFinder.getContextClassLoaders(); + final ClassLoader[] contextClassLoaders = classLoaderFinder.getContextClassLoaders(); final ClassLoader defaultClassLoader = contextClassLoaders != null && contextClassLoaders.length > 0 ? contextClassLoaders[0] : null; diff --git a/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderAndModuleFinder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ModuleFinder.java similarity index 65% rename from src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderAndModuleFinder.java rename to src/main/java/nonapi/io/github/classgraph/classpath/ModuleFinder.java index fb59fbb1b..8812f46e2 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderAndModuleFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ModuleFinder.java @@ -43,11 +43,8 @@ import nonapi.io.github.classgraph.utils.LogNode; import nonapi.io.github.classgraph.utils.ReflectionUtils; -/** A class to find the unique ordered classpath elements. */ -public class ClassLoaderAndModuleFinder { - /** The context class loaders. */ - private final ClassLoader[] contextClassLoaders; - +/** A class to find the visible modules. */ +public class ModuleFinder { /** The system module refs. */ private List systemModuleRefs; @@ -56,15 +53,6 @@ public class ClassLoaderAndModuleFinder { // ------------------------------------------------------------------------------------------------------------- - /** - * Get the context class loaders. - * - * @return The context classloader, and any other classloader that is not an ancestor of context classloader. - */ - public ClassLoader[] getContextClassLoaders() { - return contextClassLoaders; - } - /** * Get the system modules as {@link ModuleRef} wrappers. * @@ -247,122 +235,50 @@ private static List findModuleRefsFromCallstack(final Class[] call // ------------------------------------------------------------------------------------------------------------- /** - * A class to find the unique ordered classpath elements. - * + * A class to find the visible modules. + * + * @param callStack + * the callstack. * @param scanSpec - * The scan spec, or null if none available. + * The scan spec. * @param log * The log. */ - ClassLoaderAndModuleFinder(final ScanSpec scanSpec, final LogNode log) { + public ModuleFinder(final Class[] callStack, final ScanSpec scanSpec, final LogNode log) { final boolean disableModules = scanSpec.overrideClassLoaders != null || scanSpec.overrideClasspath != null; - LinkedHashSet classLoadersUnique; - LogNode classLoadersFoundLog; - if (scanSpec.overrideClassLoaders == null) { - // ClassLoaders were not overridden - // There's some advice here about choosing the best or the right classloader, but it is not complete - // (e.g. it doesn't cover parent delegation modes): - // http://www.javaworld.com/article/2077344/core-java/find-a-way-out-of-the-classloader-maze.html?page=2 - - // Get thread context classloader (this is the first classloader to try, since a context classloader - // can be set as an override on a per-thread basis) - classLoadersUnique = new LinkedHashSet<>(); - final ClassLoader threadClassLoader = Thread.currentThread().getContextClassLoader(); - if (threadClassLoader != null) { - classLoadersUnique.add(threadClassLoader); - } - - // Get classloader for this class, which will generally be the classloader of the class that - // called ClassGraph (the classloader of the caller is used by Class.forName(className), when - // no classloader is provided) - final ClassLoader currClassClassLoader = getClass().getClassLoader(); - if (currClassClassLoader != null) { - classLoadersUnique.add(currClassClassLoader); - } - - // Get system classloader (this is a fallback if one of the above do not work) - final ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); - if (systemClassLoader != null) { - classLoadersUnique.add(systemClassLoader); - } - - // There is one more classloader in JDK9+, the platform classloader (used for handling extensions), - // see: http://openjdk.java.net/jeps/261#Class-loaders - // The method call to get it is ClassLoader.getPlatformClassLoader() - // However, since it's not possible to get URLs from this classloader, and it is the parent of - // the application classloader returned by ClassLoader.getSystemClassLoader() (so is delegated to - // by the application classloader), there is no point adding it here. Modules are scanned - // directly anyway, so we don't need to get module path entries from the platform classloader. - - // Find classloaders for classes on callstack, in case any were missed - Class[] callStack = null; - try { - callStack = CallStackReader.getClassContext(log); - for (int i = callStack.length - 1; i >= 0; --i) { - final ClassLoader callerClassLoader = callStack[i].getClassLoader(); - if (callerClassLoader != null) { - classLoadersUnique.add(callerClassLoader); - } + if (!disableModules) { + // 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); } - } catch (final IllegalArgumentException e) { + } else { if (log != null) { - log.log("Could not get call stack", e); - } - } - - if (!disableModules) { - // Get the module resolution order - List allModuleRefsList = null; - if (scanSpec.overrideModuleLayers == null) { - // Find module references for classes on callstack, and from system (for JDK9+) - allModuleRefsList = findModuleRefsFromCallstack(callStack, scanSpec, log); - } else { - if (log != null) { - final LogNode subLog = log.log("Overriding module layers"); - for (final Object moduleLayer : scanSpec.overrideModuleLayers) { - subLog.log(moduleLayer.toString()); - } + 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.isSystemModule()) { - systemModuleRefs.add(moduleRef); - } else { - 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.isSystemModule()) { + systemModuleRefs.add(moduleRef); + } else { + nonSystemModuleRefs.add(moduleRef); } } } - - // Add any custom-added classloaders after system/context/module classloaders - if (scanSpec.addedClassLoaders != null) { - classLoadersUnique.addAll(scanSpec.addedClassLoaders); - } - classLoadersFoundLog = log == null ? null : log.log("Found ClassLoaders:"); - - } else { - // ClassLoaders were overridden - classLoadersUnique = new LinkedHashSet<>(scanSpec.overrideClassLoaders); - classLoadersFoundLog = log == null ? null : log.log("Override ClassLoaders:"); - } - - // Log all identified ClassLoaders - if (classLoadersFoundLog != null) { - for (final ClassLoader classLoader : classLoadersUnique) { - classLoadersFoundLog.log(classLoader.getClass().getName()); - } - } - - // Log any identified modules - if (log != null) { - if (!disableModules) { + // Log any identified modules + if (log != null) { final LogNode sysSubLog = log.log("Found system modules:"); if (systemModuleRefs != null && !systemModuleRefs.isEmpty()) { for (final ModuleRef moduleRef : systemModuleRefs) { @@ -379,11 +295,11 @@ private static List findModuleRefsFromCallstack(final Class[] call } else { nonSysSubLog.log("[None]"); } - } else { + } + } else { + if (log != null) { log.log("Module scanning is disabled, because classloaders or classpath was overridden"); } } - - this.contextClassLoaders = classLoadersUnique.toArray(new ClassLoader[0]); } } From 1076d6adfe5da62e0e13556d46584c3b582c46fd Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 12 May 2019 05:08:28 -0600 Subject: [PATCH 0191/1778] Improve unit tests (#345) --- .../classgraph/issues/issue345/Issue345.java | 108 ++++++++++++++++-- 1 file changed, 100 insertions(+), 8 deletions(-) diff --git a/src/test/java/io/github/classgraph/issues/issue345/Issue345.java b/src/test/java/io/github/classgraph/issues/issue345/Issue345.java index 88e13277e..645186944 100644 --- a/src/test/java/io/github/classgraph/issues/issue345/Issue345.java +++ b/src/test/java/io/github/classgraph/issues/issue345/Issue345.java @@ -4,11 +4,12 @@ import java.net.URL; import java.net.URLClassLoader; -import java.util.AbstractQueue; import org.junit.Test; import io.github.classgraph.ClassGraph; +import io.github.classgraph.ClassInfo; +import io.github.classgraph.Resource; import io.github.classgraph.ScanResult; /** @@ -16,16 +17,107 @@ */ public class Issue345 { /** - * Issue 345. + * Superclass. + */ + private static class Super { + } + + /** + * Subclass. + */ + public static class Sub extends Super { + } + + /** + * Test that private superclasses have their {@link Resource} reference set with .ignoreClassVisibility(). + */ + @Test + public void withIgnoreClassVisibility() { + try (ScanResult scanResult = new ClassGraph().whitelistClasses(Super.class.getName(), Sub.class.getName()) + .ignoreClassVisibility().scan()) { + final ClassInfo subClassInfo = scanResult.getClassInfo(Sub.class.getName()); + assertThat(subClassInfo).isNotNull(); + assertThat(subClassInfo.getResource()).isNotNull(); + final ClassInfo superClassInfo = scanResult.getClassInfo(Super.class.getName()); + assertThat(superClassInfo).isNotNull(); + assertThat(superClassInfo.getResource()).isNotNull(); + } + } + + /** + * Test that private superclasses do not have their {@link Resource} reference set without + * .ignoreClassVisibility(). + */ + @Test + public void withoutIgnoreClassVisibility() { + try (ScanResult scanResult = new ClassGraph().whitelistClasses(Super.class.getName(), Sub.class.getName()) + .scan()) { + final ClassInfo subClassInfo = scanResult.getClassInfo(Sub.class.getName()); + assertThat(subClassInfo).isNotNull(); + assertThat(subClassInfo.getResource()).isNotNull(); + final ClassInfo superClassInfo = scanResult.getClassInfo(Super.class.getName()); + assertThat(superClassInfo).isNotNull(); + assertThat(superClassInfo.getResource()).isNull(); + } + } + + /** + * Test that extending scanning to superclasses causes the {@link Resource} reference to be set. */ @Test - public void issue345() { - try (ScanResult scanResult = new ClassGraph().enableClassInfo().enableSystemJarsAndModules() - .overrideClassLoaders(new URLClassLoader( - new URL[] { Issue345.class.getResource("/java/util/ArrayBlockingQueue.class"), - Issue345.class.getResource("/java/util/AbstractQueue.class"), })) + public void testExtensionToParent() { + try (ScanResult scanResult = new ClassGraph().whitelistClasses(Sub.class.getName()).ignoreClassVisibility() .scan()) { - assertThat(scanResult.getClassInfo(AbstractQueue.class.getName()).getResource()).isNotNull(); + final ClassInfo superClassInfo = scanResult.getClassInfo(Super.class.getName()); + assertThat(superClassInfo).isNotNull(); + assertThat(superClassInfo.getResource()).isNotNull(); + } + } + + /** + * Test that extending scanning to outer class causes the {@link Resource} reference to be set. + */ + @Test + public void testExtensionToOuterClass() { + try (ScanResult scanResult = new ClassGraph().whitelistClasses(Super.class.getName()) + .ignoreClassVisibility().scan()) { + final ClassInfo outerClassInfo = scanResult.getClassInfo(Issue345.class.getName()); + assertThat(outerClassInfo).isNotNull(); + assertThat(outerClassInfo.getResource()).isNotNull(); + } + } + + /** + * Test that scanning is not extended to inner class, because the {@link Resource} reference is not set. + */ + @Test + public void testNonExtensionToInnerClass() { + try (ScanResult scanResult = new ClassGraph().whitelistClasses(Issue345.class.getName()) + .ignoreClassVisibility().scan()) { + final ClassInfo innerClassInfo = scanResult.getClassInfo(Super.class.getName()); + assertThat(innerClassInfo).isNotNull(); + assertThat(innerClassInfo.getResource()).isNull(); + } + } + + /** + * Test that overriding classloaders does not allow other classloaders to be scanned. + */ + @Test + public void issue345b() throws Exception { + // Find URL of this class' classpath element + URL classpathURL; + try (ScanResult scanResult = new ClassGraph().whitelistClasses(Issue345.class.getName()).scan()) { + classpathURL = scanResult.getClassInfo(Issue345.class.getName()).getClasspathElementURL(); + } + // Use this to create an override URLClassLoader + try (ScanResult scanResult = new ClassGraph().enableClassInfo() + .overrideClassLoaders(new URLClassLoader(new URL[] { classpathURL })).ignoreParentClassLoaders() + .verbose().scan()) { + // Assert that this class is found in its own classloader + assertThat(scanResult.getClassInfo(Issue345.class.getName())).isNotNull(); + // But that other classpath elements on the classpath are not found + assertThat(scanResult.getClassInfo(Test.class.getName())).isNull(); } } } From 76f7cf5b3b2da445b000051beb9708bc7954734e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 12 May 2019 05:12:00 -0600 Subject: [PATCH 0192/1778] [maven-release-plugin] prepare release classgraph-4.8.33 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 050455bec..291e33e0d 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.33-SNAPSHOT + 4.8.33 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.33 From 0062d163220d0b83c8ab31625699fdeb1a456959 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 12 May 2019 05:12:09 -0600 Subject: [PATCH 0193/1778] [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 291e33e0d..6ac80ac42 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.33 + 4.8.34-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.33 + HEAD From 60b270536323c9ba739ed82624541a894863d3f7 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 12 May 2019 05:24:19 -0600 Subject: [PATCH 0194/1778] Javadoc changes --- src/main/java/io/github/classgraph/ClassGraph.java | 4 +++- src/main/java/io/github/classgraph/ClassInfo.java | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index a806bc9c7..8e8061a35 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -470,7 +470,9 @@ public ClassGraph addClassLoader(final ClassLoader classLoader) { /** * Completely override (and ignore) system ClassLoaders and the java.class.path system property. Also causes - * modules not to be scanned. + * modules not to be scanned. Note that you may want to use this together with + * {@link #ignoreParentClassLoaders()} to extract classpath URLs from only the classloaders you specified in the + * parameter to `overrideClassLoaders`, and not their parent classloaders. * *

* This call is ignored if {@link #overrideClasspath(String)} is called. diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index 3d275c9b9..4cfb75c6d 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -2450,7 +2450,7 @@ public ModuleRef getModuleRef() { * The {@link Resource} for the classfile of this class. * * @return The {@link Resource} for the classfile of this class. Returns null if the classfile for this class - * was not actually read during the scan, because this class was not in the whitelist, but was + * was not actually read during the scan, e.g. because this class was not itself whitelisted, but was * referenced by a whitelisted class. */ public Resource getResource() { From a1e1d2f9bda1cb39442872da396edab46ee4ba86 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 12 May 2019 05:29:28 -0600 Subject: [PATCH 0195/1778] Refactoring --- .../java/io/github/classgraph/Scanner.java | 30 +++++-------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 9863e763b..2382e6cbe 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -158,22 +158,8 @@ class Scanner implements Callable { this.moduleFinder = new ModuleFinder(classpathFinder.getCallStack(), scanSpec, classpathFinderLog); this.classLoaderOrderRespectingParentDelegation = classpathFinder .getClassLoaderOrderRespectingParentDelegation(); - this.moduleOrder = getModuleOrder(classpathFinderLog); - } - - // ------------------------------------------------------------------------------------------------------------- - /** - * Get the module order. - * - * @param log - * the log - * @return the module order - * @throws InterruptedException - * if interrupted - */ - private List getModuleOrder(final LogNode log) throws InterruptedException { - final List moduleOrder = new ArrayList<>(); + this.moduleOrder = new ArrayList<>(); if (scanSpec.overrideClasspath == null && scanSpec.overrideClassLoaders == null && scanSpec.scanModules) { // Add modules to start of classpath order, before traditional classpath final List systemModuleRefs = moduleFinder.getSystemModuleRefs(); @@ -197,10 +183,11 @@ private List getModuleOrder(final LogNode log) throws In systemModuleRef, defaultClassLoader, nestedJarHandler, scanSpec); moduleOrder.add(classpathElementModule); // Open the ClasspathElementModule - classpathElementModule.open(/* ignored */ null, log); + classpathElementModule.open(/* ignored */ null, classpathFinderLog); } else { - if (log != null) { - log.log("Skipping non-whitelisted or blacklisted system module: " + moduleName); + if (classpathFinderLog != null) { + classpathFinderLog + .log("Skipping non-whitelisted or blacklisted system module: " + moduleName); } } } @@ -218,16 +205,15 @@ private List getModuleOrder(final LogNode log) throws In nonSystemModuleRef, defaultClassLoader, nestedJarHandler, scanSpec); moduleOrder.add(classpathElementModule); // Open the ClasspathElementModule - classpathElementModule.open(/* ignored */ null, log); + classpathElementModule.open(/* ignored */ null, classpathFinderLog); } else { - if (log != null) { - log.log("Skipping non-whitelisted or blacklisted module: " + moduleName); + if (classpathFinderLog != null) { + classpathFinderLog.log("Skipping non-whitelisted or blacklisted module: " + moduleName); } } } } } - return moduleOrder; } // ------------------------------------------------------------------------------------------------------------- From 486fb17195cf04c38db717f5b172fd36d14deab6 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 12 May 2019 06:09:33 -0600 Subject: [PATCH 0196/1778] Get modifiers for inner classes (#345). --- .../java/io/github/classgraph/ClassInfo.java | 13 ++-- .../java/io/github/classgraph/Classfile.java | 60 +++++++++++++++---- .../classgraph/issues/issue345/Issue345.java | 39 +++++++++++- 3 files changed, 93 insertions(+), 19 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index 4cfb75c6d..8d4dd308b 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -48,6 +48,7 @@ import java.util.Map.Entry; import java.util.Set; +import io.github.classgraph.Classfile.ClassContainment; import nonapi.io.github.classgraph.json.Id; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.types.ParseException; @@ -385,14 +386,12 @@ void addImplementedInterface(final String interfaceName, final Map> classContainmentEntries, + static void addClassContainment(final List classContainmentEntries, final Map classNameToClassInfo) { - for (final SimpleEntry ent : classContainmentEntries) { - final String innerClassName = ent.getKey(); - final ClassInfo innerClassInfo = ClassInfo.getOrCreateClassInfo(innerClassName, - /* classModifiers = */ 0, classNameToClassInfo); - final String outerClassName = ent.getValue(); - final ClassInfo outerClassInfo = ClassInfo.getOrCreateClassInfo(outerClassName, + for (final ClassContainment classContainment : classContainmentEntries) { + final ClassInfo innerClassInfo = ClassInfo.getOrCreateClassInfo(classContainment.innerClassName, + /* classModifiers = */ classContainment.innerClassModifierBits, classNameToClassInfo); + final ClassInfo outerClassInfo = ClassInfo.getOrCreateClassInfo(classContainment.outerClassName, /* classModifiers = */ 0, classNameToClassInfo); innerClassInfo.addRelatedClass(RelType.CONTAINED_WITHIN_OUTER_CLASS, outerClassInfo); outerClassInfo.addRelatedClass(RelType.CONTAINS_INNER_CLASS, innerClassInfo); diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index b4bf4b73e..1f545cb07 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -30,7 +30,6 @@ import java.io.IOException; import java.lang.reflect.Modifier; -import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; @@ -100,8 +99,39 @@ class Classfile { /** The fully qualified name of the defining method. */ private String fullyQualifiedDefiningMethodName; - /** Class containment entries -- tuples of (inner class name, outer class name). */ - private List> classContainmentEntries; + /** + * Class containment. + */ + static class ClassContainment { + /** The inner class name. */ + public final String innerClassName; + + /** The inner class modifier bits. */ + public final int innerClassModifierBits; + + /** The outer class name. */ + public final String outerClassName; + + /** + * Constructor. + * + * @param innerClassName + * the inner class name. + * @param innerClassModifierBits + * the inner class modifier bits. + * @param outerClassName + * the outer class name. + */ + public ClassContainment(final String innerClassName, final int innerClassModifierBits, + final String outerClassName) { + this.innerClassName = innerClassName; + this.innerClassModifierBits = innerClassModifierBits; + this.outerClassName = outerClassName; + } + } + + /** Class containment entries. */ + private List classContainmentEntries; /** Annotation default parameter values. */ private AnnotationParameterValueList annotationParamDefaultValues; @@ -349,9 +379,9 @@ private void extendScanningUpwards(final LogNode log) { } // Check if this class is an inner class, and if so, extend scanning to outer class if (classContainmentEntries != null) { - for (final SimpleEntry classContainmentEntry : classContainmentEntries) { - if (classContainmentEntry.getKey().equals(className)) { - scheduleScanningIfExternalClass(classContainmentEntry.getValue(), "outer class", log); + for (final ClassContainment classContainmentEntry : classContainmentEntries) { + if (classContainmentEntry.innerClassName.equals(className)) { + scheduleScanningIfExternalClass(classContainmentEntry.outerClassName, "outer class", log); } } } @@ -470,6 +500,10 @@ void link(final Map classNameToClassInfo, /** * Intern a string. + * + * @param str + * the str + * @return the string */ private String intern(final String str) { if (str == null) { @@ -1084,6 +1118,7 @@ private void readConstantPoolEntries() throws IOException { private void readBasicClassInfo() throws IOException, ClassfileFormatException, SkipClassException { // Modifier flags classModifiers = inputStreamOrByteBuffer.readUnsignedShort(); + isInterface = (classModifiers & 0x0200) != 0; isAnnotation = (classModifiers & 0x2000) != 0; @@ -1414,15 +1449,17 @@ private void readClassAttributes() throws IOException, ClassfileFormatException for (int j = 0; j < numInnerClasses; j++) { final int innerClassInfoCpIdx = inputStreamOrByteBuffer.readUnsignedShort(); final int outerClassInfoCpIdx = inputStreamOrByteBuffer.readUnsignedShort(); + inputStreamOrByteBuffer.skip(2); // inner_name_idx + final int innerClassAccessFlags = inputStreamOrByteBuffer.readUnsignedShort(); if (innerClassInfoCpIdx != 0 && outerClassInfoCpIdx != 0) { + final String innerClassName = getConstantPoolClassName(innerClassInfoCpIdx); + final String outerClassName = getConstantPoolClassName(outerClassInfoCpIdx); if (classContainmentEntries == null) { classContainmentEntries = new ArrayList<>(); } - classContainmentEntries.add(new SimpleEntry<>(getConstantPoolClassName(innerClassInfoCpIdx), - getConstantPoolClassName(outerClassInfoCpIdx))); + classContainmentEntries + .add(new ClassContainment(innerClassName, innerClassAccessFlags, outerClassName)); } - inputStreamOrByteBuffer.skip(2); // inner_name_idx - inputStreamOrByteBuffer.skip(2); // inner_class_access_flags } } else if (constantPoolStringEquals(attributeNameCpIdx, "Signature")) { // Get class type signature, including type variables @@ -1444,7 +1481,8 @@ private void readClassAttributes() throws IOException, ClassfileFormatException if (classContainmentEntries == null) { classContainmentEntries = new ArrayList<>(); } - classContainmentEntries.add(new SimpleEntry<>(className, innermostEnclosingClassName)); + classContainmentEntries + .add(new ClassContainment(className, classModifiers, innermostEnclosingClassName)); // Also store the fully-qualified name of the enclosing method, to mark this as an anonymous inner // class this.fullyQualifiedDefiningMethodName = innermostEnclosingClassName + "." + definingMethodName; diff --git a/src/test/java/io/github/classgraph/issues/issue345/Issue345.java b/src/test/java/io/github/classgraph/issues/issue345/Issue345.java index 645186944..c5e905ff6 100644 --- a/src/test/java/io/github/classgraph/issues/issue345/Issue345.java +++ b/src/test/java/io/github/classgraph/issues/issue345/Issue345.java @@ -102,6 +102,9 @@ public void testNonExtensionToInnerClass() { /** * Test that overriding classloaders does not allow other classloaders to be scanned. + * + * @throws Exception + * the exception */ @Test public void issue345b() throws Exception { @@ -113,11 +116,45 @@ public void issue345b() throws Exception { // Use this to create an override URLClassLoader try (ScanResult scanResult = new ClassGraph().enableClassInfo() .overrideClassLoaders(new URLClassLoader(new URL[] { classpathURL })).ignoreParentClassLoaders() - .verbose().scan()) { + .scan()) { // Assert that this class is found in its own classloader assertThat(scanResult.getClassInfo(Issue345.class.getName())).isNotNull(); // But that other classpath elements on the classpath are not found assertThat(scanResult.getClassInfo(Test.class.getName())).isNull(); } } + + /** + * A. + */ + private static class A { + } + + /** + * B. + */ + abstract static class B extends A { + } + + /** + * C. + */ + public static class C extends B { + } + + /** + * Test inner class modifiers are picked up from the InnerClasses attribute of classfiles. + */ + @Test + public void issue345c() { + try (ScanResult scanResult = new ClassGraph().enableClassInfo() + .whitelistPackages(Issue345.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()); + assertThat(ciB.getModifiersStr()).isEqualTo("abstract static"); + final ClassInfo ciC = scanResult.getClassInfo(C.class.getName()); + assertThat(ciC.getModifiersStr()).isEqualTo("public static"); + } + } } From 1aa078a6936fbb65b01d1d8b1eaf99b6b2a74839 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 12 May 2019 06:10:06 -0600 Subject: [PATCH 0197/1778] [maven-release-plugin] prepare release classgraph-4.8.34 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 6ac80ac42..491d69ab8 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.34-SNAPSHOT + 4.8.34 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.34 From 622a0894724b1bc7044916f5a27f3bd7bbac2835 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 12 May 2019 06:10:16 -0600 Subject: [PATCH 0198/1778] [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 491d69ab8..dc69968dd 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.34 + 4.8.35-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.34 + HEAD From a873e317452d711c04855905e527b07923108dbe Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 13 May 2019 03:25:09 -0600 Subject: [PATCH 0199/1778] Fix PMD static analyzer warnings --- .../java/io/github/classgraph/Classfile.java | 68 ++++++++++--------- .../java/io/github/classgraph/Scanner.java | 6 +- .../EquinoxClassLoaderHandler.java | 12 ++-- .../io/github/classgraph/utils/FileUtils.java | 10 +-- 4 files changed, 45 insertions(+), 51 deletions(-) diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index 1f545cb07..5ae54e878 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -99,37 +99,6 @@ class Classfile { /** The fully qualified name of the defining method. */ private String fullyQualifiedDefiningMethodName; - /** - * Class containment. - */ - static class ClassContainment { - /** The inner class name. */ - public final String innerClassName; - - /** The inner class modifier bits. */ - public final int innerClassModifierBits; - - /** The outer class name. */ - public final String outerClassName; - - /** - * Constructor. - * - * @param innerClassName - * the inner class name. - * @param innerClassModifierBits - * the inner class modifier bits. - * @param outerClassName - * the outer class name. - */ - public ClassContainment(final String innerClassName, final int innerClassModifierBits, - final String outerClassName) { - this.innerClassName = innerClassName; - this.innerClassModifierBits = innerClassModifierBits; - this.outerClassName = outerClassName; - } - } - /** Class containment entries. */ private List classContainmentEntries; @@ -184,8 +153,41 @@ public ClassContainment(final String innerClassName, final int innerClassModifie // ------------------------------------------------------------------------------------------------------------- + /** + * Class containment. + */ + static class ClassContainment { + /** The inner class name. */ + public final String innerClassName; + + /** The inner class modifier bits. */ + public final int innerClassModifierBits; + + /** The outer class name. */ + public final String outerClassName; + + /** + * Constructor. + * + * @param innerClassName + * the inner class name. + * @param innerClassModifierBits + * the inner class modifier bits. + * @param outerClassName + * the outer class name. + */ + public ClassContainment(final String innerClassName, final int innerClassModifierBits, + final String outerClassName) { + this.innerClassName = innerClassName; + this.innerClassModifierBits = innerClassModifierBits; + this.outerClassName = outerClassName; + } + } + + // ------------------------------------------------------------------------------------------------------------- + /** Thrown when a classfile's contents are not in the correct format. */ - class ClassfileFormatException extends IOException { + static class ClassfileFormatException extends IOException { /** serialVersionUID. */ static final long serialVersionUID = 1L; @@ -223,7 +225,7 @@ public synchronized Throwable fillInStackTrace() { } /** Thrown when a classfile needs to be skipped. */ - class SkipClassException extends IOException { + static class SkipClassException extends IOException { /** serialVersionUID. */ static final long serialVersionUID = 1L; diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 2382e6cbe..0a116d978 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -100,9 +100,6 @@ class Scanner implements Callable { /** The classpath finder. */ private final ClasspathFinder classpathFinder; - /** The module finder. */ - private final ModuleFinder moduleFinder; - /** The module order. */ private final List moduleOrder; @@ -155,9 +152,10 @@ class Scanner implements Callable { final LogNode classpathFinderLog = topLevelLog == null ? null : topLevelLog.log("Finding classpath"); this.classpathFinder = new ClasspathFinder(scanSpec, classpathFinderLog); - this.moduleFinder = new ModuleFinder(classpathFinder.getCallStack(), scanSpec, classpathFinderLog); this.classLoaderOrderRespectingParentDelegation = classpathFinder .getClassLoaderOrderRespectingParentDelegation(); + final ModuleFinder moduleFinder = new ModuleFinder(classpathFinder.getCallStack(), scanSpec, + classpathFinderLog); this.moduleOrder = new ArrayList<>(); if (scanSpec.overrideClasspath == null && scanSpec.overrideClassLoaders == null && scanSpec.scanModules) { 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 b34f3a940..78b86b3ad 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxClassLoaderHandler.java @@ -45,6 +45,12 @@ * Extract classpath entries from the Eclipse Equinox ClassLoader. */ class EquinoxClassLoaderHandler implements ClassLoaderHandler { + /** + * True if system bundles have been read. We assume there is only one system bundle on the classpath, so this is + * static. + */ + private static boolean alreadyReadSystemBundles; + /** Field names. */ private static final List FIELD_NAMES = Collections .unmodifiableList(Arrays.asList("cp", "nestedDirName")); @@ -53,12 +59,6 @@ class EquinoxClassLoaderHandler implements ClassLoaderHandler { private EquinoxClassLoaderHandler() { } - /** - * True if system bundles have been read. We assume there is only one system bundle on the classpath, so this is - * static. - */ - private static boolean alreadyReadSystemBundles; - /** * Check whether this {@link ClassLoaderHandler} can handle a given {@link ClassLoader}. * 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 b27b9e718..039ff153a 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -449,10 +449,7 @@ public static boolean canReadAndIsFile(final File file) { } catch (final SecurityException e) { return false; } - if (!file.isFile()) { - return false; - } - return true; + return file.isFile(); } /** @@ -490,10 +487,7 @@ public static boolean canReadAndIsDir(final File file) { } catch (final SecurityException e) { return false; } - if (!file.isDirectory()) { - return false; - } - return true; + return file.isDirectory(); } /** From 9aff0d75d12a904bbfa50c346d476802f340bf23 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 14 May 2019 01:59:06 -0600 Subject: [PATCH 0200/1778] Handle the same `package-info` being present multiple times in cp (#347) --- .../java/io/github/classgraph/Classfile.java | 5 ++-- .../github/classgraph/ClasspathElement.java | 6 +++-- .../java/io/github/classgraph/ModuleInfo.java | 26 ++++++++++++++----- .../io/github/classgraph/PackageInfo.java | 26 ++++++++++++++----- .../java/io/github/classgraph/Scanner.java | 2 +- 5 files changed, 47 insertions(+), 18 deletions(-) diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index 5ae54e878..757fde166 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -460,8 +460,8 @@ void link(final Map classNameToClassInfo, PackageInfo packageInfo = null; if (!isModuleDescriptor) { // Get package for this class or package descriptor - packageInfo = PackageInfo.getOrCreatePackage(PackageInfo.getParentPackageName(className), - packageNameToPackageInfo); + final String packageName = PackageInfo.getParentPackageName(className); + packageInfo = PackageInfo.getOrCreatePackage(packageName, packageNameToPackageInfo); if (isPackageDescriptor) { // Add any class annotations on the package-info.class file to the ModuleInfo packageInfo.addAnnotations(classAnnotations); @@ -495,7 +495,6 @@ void link(final Map classNameToClassInfo, moduleInfo.addPackageInfo(packageInfo); } } - } // ------------------------------------------------------------------------------------------------------------- diff --git a/src/main/java/io/github/classgraph/ClasspathElement.java b/src/main/java/io/github/classgraph/ClasspathElement.java index 64d092b01..828bb3043 100644 --- a/src/main/java/io/github/classgraph/ClasspathElement.java +++ b/src/main/java/io/github/classgraph/ClasspathElement.java @@ -198,9 +198,11 @@ void maskClassfiles(final int classpathIdx, final Set classpathRelativeP boolean foundMasked = false; for (final Resource res : whitelistedClassfileResources) { final String pathRelativeToPackageRoot = res.getPath(); - // Don't mask module-info.class or package-info.class, these are read for every module/package + // Don't mask module-info.class or package-info.class, these are read for every module/package, + // and they don't result in a ClassInfo object, so there will be no duplicate ClassInfo objects + // created, even if they are encountered multiple times. Instead, any annotations on modules or + // packages are merged into the appropriate ModuleInfo / PackageInfo object. if (!pathRelativeToPackageRoot.equals("module-info.class") - && !pathRelativeToPackageRoot.endsWith("/module-info.class") && !pathRelativeToPackageRoot.equals("package-info.class") && !pathRelativeToPackageRoot.endsWith("/package-info.class") // Check if pathRelativeToPackageRoot has been seen before diff --git a/src/main/java/io/github/classgraph/ModuleInfo.java b/src/main/java/io/github/classgraph/ModuleInfo.java index 203268e5e..272e7c65b 100644 --- a/src/main/java/io/github/classgraph/ModuleInfo.java +++ b/src/main/java/io/github/classgraph/ModuleInfo.java @@ -30,6 +30,7 @@ import java.net.URI; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.Set; import nonapi.io.github.classgraph.utils.CollectionUtils; @@ -48,7 +49,13 @@ public class ModuleInfo implements Comparable, HasName { /** The location of the module as a URI. */ private transient URI locationURI; - /** {@link AnnotationInfo} objects for any annotations on the package-info.class file, if present, else null. */ + /** + * Unique {@link AnnotationInfo} objects for any annotations on the module-info.class file, if present, else + * null. + */ + private Set annotationInfoSet; + + /** {@link AnnotationInfo} objects for any annotations on the module-info.class file, if present, else null. */ private AnnotationInfoList annotationInfo; /** {@link PackageInfo} objects for packages found within the class, if any, else null. */ @@ -217,11 +224,10 @@ public PackageInfoList getPackageInfo() { void addAnnotations(final AnnotationInfoList moduleAnnotations) { // Currently only class annotations are used in the module-info.class file if (moduleAnnotations != null && !moduleAnnotations.isEmpty()) { - if (this.annotationInfo == null) { - this.annotationInfo = new AnnotationInfoList(moduleAnnotations); - } else { - this.annotationInfo.addAll(moduleAnnotations); + if (annotationInfoSet == null) { + annotationInfoSet = new LinkedHashSet<>(); } + annotationInfoSet.addAll(moduleAnnotations); } } @@ -243,7 +249,15 @@ public AnnotationInfo getAnnotationInfo(final String annotationName) { * @return the list of {@link AnnotationInfo} objects for annotations on the {@code package-info.class} file. */ public AnnotationInfoList getAnnotationInfo() { - return annotationInfo == null ? AnnotationInfoList.EMPTY_LIST : annotationInfo; + if (annotationInfo == null) { + if (annotationInfoSet == null) { + annotationInfo = AnnotationInfoList.EMPTY_LIST; + } else { + annotationInfo = new AnnotationInfoList(); + annotationInfo.addAll(annotationInfoSet); + } + } + return annotationInfo; } /** diff --git a/src/main/java/io/github/classgraph/PackageInfo.java b/src/main/java/io/github/classgraph/PackageInfo.java index 7c422f1e7..17a57f799 100644 --- a/src/main/java/io/github/classgraph/PackageInfo.java +++ b/src/main/java/io/github/classgraph/PackageInfo.java @@ -31,6 +31,7 @@ import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; @@ -41,6 +42,12 @@ public class PackageInfo implements Comparable, HasName { /** Name of the package. */ private String name; + /** + * Unique {@link AnnotationInfo} objects for any annotations on the package-info.class file, if present, else + * null. + */ + private Set annotationInfoSet; + /** {@link AnnotationInfo} for any annotations on the package-info.class file, if present, else null. */ private AnnotationInfoList annotationInfo; @@ -89,13 +96,12 @@ public String getName() { * the package annotations */ void addAnnotations(final AnnotationInfoList packageAnnotations) { - // Currently only class annotations are used in the package-info.class file + // Add class annotations from the package-info.class file if (packageAnnotations != null && !packageAnnotations.isEmpty()) { - if (this.annotationInfo == null) { - this.annotationInfo = new AnnotationInfoList(packageAnnotations); - } else { - this.annotationInfo.addAll(packageAnnotations); + if (annotationInfoSet == null) { + annotationInfoSet = new LinkedHashSet<>(); } + annotationInfoSet.addAll(packageAnnotations); } } @@ -133,7 +139,15 @@ public AnnotationInfo getAnnotationInfo(final String annotationName) { * @return the annotations on the {@code package-info.class} file. */ public AnnotationInfoList getAnnotationInfo() { - return annotationInfo == null ? AnnotationInfoList.EMPTY_LIST : annotationInfo; + if (annotationInfo == null) { + if (annotationInfoSet == null) { + annotationInfo = AnnotationInfoList.EMPTY_LIST; + } else { + annotationInfo = new AnnotationInfoList(); + annotationInfo.addAll(annotationInfoSet); + } + } + return annotationInfo; } /** diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 0a116d978..ea12bb1ee 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -809,7 +809,7 @@ private ScanResult performScan(final List finalClasspathEltOrd // and double-check that a class is not going to be scanned twice final String className = JarUtils.classfilePathToClassName(resource.getPath()); if (!whitelistedClassNamesFound.add(className) && !className.equals("module-info") - && !className.equals("package-info")) { + && !className.equals("package-info") && !className.endsWith(".package-info")) { // The class should not be scheduled more than once for scanning, since classpath // masking was already applied throw new IllegalArgumentException("Class " + className From b370c7ae67d9aaba2de8f263029696d22bcdb7f9 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 14 May 2019 02:14:07 -0600 Subject: [PATCH 0201/1778] Ignore invalid multi-release versioned paths --- .../io/github/classgraph/ClasspathElementDir.java | 12 ++++++++++++ .../github/classgraph/ClasspathElementModule.java | 14 ++++++++++++++ .../io/github/classgraph/ClasspathElementZip.java | 12 ++++++++++++ .../fastzipfilereader/LogicalZipFile.java | 2 +- 4 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementDir.java b/src/main/java/io/github/classgraph/ClasspathElementDir.java index 22297e7bc..e71f68e6a 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementDir.java @@ -46,6 +46,7 @@ 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.scanspec.ScanSpec; import nonapi.io.github.classgraph.scanspec.ScanSpec.ScanSpecPathMatch; import nonapi.io.github.classgraph.utils.FileUtils; @@ -329,6 +330,17 @@ private void scanDirRecursively(final File dir, final LogNode log) { 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; + } + // Whitelist/blacklist classpath elements based on dir resource paths checkResourcePathWhiteBlackList(dirRelativePath, log); if (skipClasspathElement) { diff --git a/src/main/java/io/github/classgraph/ClasspathElementModule.java b/src/main/java/io/github/classgraph/ClasspathElementModule.java index 9b2f2cdbf..42217f6fc 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementModule.java +++ b/src/main/java/io/github/classgraph/ClasspathElementModule.java @@ -40,6 +40,7 @@ import io.github.classgraph.Scanner.ClasspathEntryWorkUnit; import nonapi.io.github.classgraph.concurrency.SingletonMap.NullSingletonException; 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.recycler.RecycleOnClose; import nonapi.io.github.classgraph.recycler.Recycler; @@ -274,6 +275,19 @@ void scanPaths(final LogNode log) { continue; } + // Paths in modules should never start with "META-INF/versions/{version}/", because the module + // system should already strip these prefixes away. If they are found, then the jarfile must + // 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 (subLog != null) { + subLog.log( + "Found unexpected nested versioned entry in module -- skipping: " + relativePath); + } + continue; + } + // Whitelist/blacklist classpath elements based on file resource paths checkResourcePathWhiteBlackList(relativePath, log); if (skipClasspathElement) { diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index 5221f3b1a..1ab78e395 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -414,6 +414,18 @@ void scanPaths(final LogNode log) { for (final FastZipEntry zipEntry : logicalZipFile.entries) { String relativePath = zipEntry.entryNameUnversioned; + // Paths should never start with "META-INF/versions/{version}/", because either this is a versioned + // 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 (subLog != null) { + subLog.log("Found unexpected versioned entry in jar (the jar's manifest file may be missing " + + "the \"Multi-Release\" key) -- skipping: " + relativePath); + } + continue; + } + // Check if the relative path is within a nested classpath root if (nestedClasspathRootPrefixes != null) { // This is O(mn), which is inefficient, but the number of nested classpath roots should be small 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 60de3b655..938528a27 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java @@ -92,7 +92,7 @@ public class LogicalZipFile extends ZipFileSlice implements AutoCloseable { private static final String MANIFEST_PATH = META_INF_PATH_PREFIX + "MANIFEST.MF"; /** {@code "META-INF/versions/"}. */ - static final String MULTI_RELEASE_PATH_PREFIX = META_INF_PATH_PREFIX + "versions/"; + public static final String MULTI_RELEASE_PATH_PREFIX = META_INF_PATH_PREFIX + "versions/"; /** The {@code "Implementation-Title"} manifest key. */ private static final byte[] IMPLEMENTATION_TITLE_KEY = manifestKeyToBytes("Implementation-Title"); From 52029209f807e586ae1bf77886953f1705923814 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 14 May 2019 02:15:12 -0600 Subject: [PATCH 0202/1778] [maven-release-plugin] prepare release classgraph-4.8.35 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index dc69968dd..34384b81d 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.35-SNAPSHOT + 4.8.35 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.35 From 8bed7c887d888dfb3b7c3486d4c444947994d4aa Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 14 May 2019 02:15:19 -0600 Subject: [PATCH 0203/1778] [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 34384b81d..bba754664 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.35 + 4.8.36-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.35 + HEAD From 14b995998f0377cd1c2b0bef9e0ffb6676778592 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 14 May 2019 20:25:57 -0600 Subject: [PATCH 0204/1778] Add downloading info --- README.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/README.md b/README.md index d46b40283..ca3844068 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,39 @@ ClassGraph provides a number of important capabilities to the JVM ecosystem: Class graph visualization

+## Downloading + +### Maven dependency + +```xml + + io.github.classgraph + classgraph + LATEST + +``` + +### 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). + +### Building from source + +ClassGraph must be built on JDK 8 or newer (due to the presence of `@FunctionalInterface` annotations on some interfaces), but is built using `-target 1.7` for backwards compatibility with JRE 7. + +The following commands will build the most recent version of ClassGraph from git master. The compiled package will then be in the "classgraph/target" directory. + +``` +git clone https://github.com/classgraph/classgraph.git +cd classgraph +export JAVA_HOME=/usr/java/default # Or similar -- Maven needs JAVA_HOME +mvn -Dmaven.test.skip=true package +``` + +This will allow you to build a local SNAPSHOT jar in `target/`. Alternatively, use `mvn -Dmaven.test.skip=true install` to build a SNAPSHOT jar and then copy it into your local repository, so that you can use it in your Maven projects. Note that may need to do `mvn dependency:resolve` in your project if you overwrite an older snapshot with a newer one. + +`mvn -U` updates from remote repositories an may overwrite your local artifact. But you can always change the `artifactId` or the `groupId` of your local ClassGraph build to place your local build artifact in another location within your local repository. + ## Documentation [See the wiki for complete documentation and usage information.](https://github.com/classgraph/classgraph/wiki) From 037a989ded4f0c6e723a39d88e8fc8ab760fd62a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 16 May 2019 03:21:04 -0600 Subject: [PATCH 0205/1778] Fix wildcarded black/whitelisting (#348) --- .../classgraph/scanspec/WhiteBlackList.java | 15 ++++-- .../classgraph/issues/issue348/Issue348.java | 53 +++++++++++++++++++ 2 files changed, 63 insertions(+), 5 deletions(-) create mode 100644 src/test/java/io/github/classgraph/issues/issue348/Issue348.java diff --git a/src/main/java/nonapi/io/github/classgraph/scanspec/WhiteBlackList.java b/src/main/java/nonapi/io/github/classgraph/scanspec/WhiteBlackList.java index f47227247..0e30fea3f 100644 --- a/src/main/java/nonapi/io/github/classgraph/scanspec/WhiteBlackList.java +++ b/src/main/java/nonapi/io/github/classgraph/scanspec/WhiteBlackList.java @@ -264,11 +264,16 @@ public void addToWhitelist(final String str) { final String separator = Character.toString(separatorChar); String prefix = str; if (prefix.contains("*")) { - // Stop performing prefix search at first '*' - prefix = prefix.substring(prefix.indexOf('*')); - if (!prefix.isEmpty() && !prefix.endsWith(separator)) { - // /path/to/wildcard* -> /path/to - prefix = prefix.substring(prefix.lastIndexOf(separatorChar)); + // Stop performing prefix search at first '*' -- this means prefix matching will + // break if there is more than one '*' in the path + prefix = prefix.substring(0, prefix.indexOf('*')); + // /path/to/wildcard*.jar -> /path/to + // /path/to/*.jar -> /path/to + final int sepIdx = prefix.lastIndexOf(separatorChar); + if (sepIdx < 0) { + prefix = ""; + } else { + prefix = prefix.substring(0, prefix.lastIndexOf(separatorChar)); } } // Strip off any final separator diff --git a/src/test/java/io/github/classgraph/issues/issue348/Issue348.java b/src/test/java/io/github/classgraph/issues/issue348/Issue348.java new file mode 100644 index 000000000..87c5b6454 --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue348/Issue348.java @@ -0,0 +1,53 @@ +package io.github.classgraph.issues.issue348; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.net.URI; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import org.junit.Test; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ScanResult; + +/** + * Issue345. + */ +public class Issue348 { + /** Test for wildcarded jars. */ + @Test + public void testWildcard() { + try (ScanResult scanResult1 = new ClassGraph().whitelistPathsNonRecursive("").scan()) { + // Find all resources within classpath elements with ".jar" extension + final List jarResourceUris = scanResult1.getResourcesWithExtension("jar").stream() + .map(r -> r.getURI().toString()).collect(Collectors.toList()); + assertThat(jarResourceUris).isNotEmpty(); + + try (ScanResult scanResult2 = new ClassGraph().overrideClasspath(jarResourceUris) + .whitelistJars("issue*.jar").scan()) { + // Find all classpath element URIs for non-nested jars + final List cpUris = scanResult2.getClasspathURIs().stream().map(URI::toString) + .filter(u -> !u.contains("!")).collect(Collectors.toList()); + assertThat(cpUris).isNotEmpty(); + + // Check that cpUris is a non-empty subset of jarResourceUris + final Set jarResourceUrisMinusCpUris = new LinkedHashSet<>(jarResourceUris); + jarResourceUrisMinusCpUris.removeAll(cpUris); + assertThat(jarResourceUrisMinusCpUris).isNotEmpty(); + assertThat(jarResourceUrisMinusCpUris.size()).isLessThan(jarResourceUris.size()); + final Set cpUrisMinusJarResourceUris = new LinkedHashSet<>(cpUris); + cpUrisMinusJarResourceUris.removeAll(jarResourceUris); + assertThat(cpUrisMinusJarResourceUris).isEmpty(); + + // Check that cpUris all end with "issue*.jar" + for (final String uri : cpUris) { + final String leaf = uri.substring(uri.lastIndexOf('/') + 1); + assertThat(leaf).matches("issue.*\\.jar"); + } + } + } + } +} From 3fe342856075b0dac752ba3cc9df88d87b913c65 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 16 May 2019 03:22:22 -0600 Subject: [PATCH 0206/1778] [maven-release-plugin] prepare release classgraph-4.8.36 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index bba754664..61622637d 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.36-SNAPSHOT + 4.8.36 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.36 From 8a703dd6e638bf89ece7c586fddedecda616c214 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 16 May 2019 03:22:28 -0600 Subject: [PATCH 0207/1778] [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 61622637d..50fc97aec 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.36 + 4.8.37-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.36 + HEAD From 46d9a7a2837458bc430a401a32b9158902e8b43e Mon Sep 17 00:00:00 2001 From: Lars Grefer Date: Mon, 20 May 2019 17:55:38 +0200 Subject: [PATCH 0208/1778] Fix classpath detection for nested ZipBundleFile If a `NestedBundleFile` contains a `ZipBundleFile` their paths must be concatenated using `!/` instead of `/`. --- .../EquinoxClassLoaderHandler.java | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) 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 78b86b3ad..1213159e4 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxClassLoaderHandler.java @@ -28,19 +28,15 @@ */ package nonapi.io.github.classgraph.classloaderhandler; -import java.lang.reflect.Array; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -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; import nonapi.io.github.classgraph.utils.LogNode; import nonapi.io.github.classgraph.utils.ReflectionUtils; +import java.lang.reflect.Array; +import java.util.*; + /** * Extract classpath entries from the Eclipse Equinox ClassLoader. */ @@ -114,7 +110,16 @@ private static void addBundleFile(final Object bundlefile, final Set pat foundClassPathElement = fieldVal != null; if (foundClassPathElement) { // We found the base file and a classpath element, e.g. "bin/" - classpathOrderOut.addClasspathEntry(basefile.toString() + "/" + fieldVal.toString(), + String pathElement = basefile.toString() + "/" + fieldVal.toString(); + + if (bundlefile.getClass().getName().equals("org.eclipse.osgi.storage.bundlefile.NestedDirBundleFile")) { + Object baseBundleFile = ReflectionUtils.getFieldVal(bundlefile, "baseBundleFile", false); + if (baseBundleFile != null && baseBundleFile.getClass().getName().equals("org.eclipse.osgi.storage.bundlefile.ZipBundleFile")) { + pathElement = baseBundleFile.toString() + "!/" + fieldVal.toString(); + } + } + + classpathOrderOut.addClasspathEntry(pathElement, classLoader, scanSpec, log); break; } From fae4a7c2485ac42edc2b61520e9ec221ddd5c29b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 20 May 2019 14:15:52 -0600 Subject: [PATCH 0209/1778] Source > Cleanup --- .../EquinoxClassLoaderHandler.java | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) 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 1213159e4..f7eafd117 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxClassLoaderHandler.java @@ -28,15 +28,19 @@ */ package nonapi.io.github.classgraph.classloaderhandler; +import java.lang.reflect.Array; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +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; import nonapi.io.github.classgraph.utils.LogNode; import nonapi.io.github.classgraph.utils.ReflectionUtils; -import java.lang.reflect.Array; -import java.util.*; - /** * Extract classpath entries from the Eclipse Equinox ClassLoader. */ @@ -112,15 +116,17 @@ private static void addBundleFile(final Object bundlefile, final Set pat // We found the base file and a classpath element, e.g. "bin/" String pathElement = basefile.toString() + "/" + fieldVal.toString(); - if (bundlefile.getClass().getName().equals("org.eclipse.osgi.storage.bundlefile.NestedDirBundleFile")) { - Object baseBundleFile = ReflectionUtils.getFieldVal(bundlefile, "baseBundleFile", false); - if (baseBundleFile != null && baseBundleFile.getClass().getName().equals("org.eclipse.osgi.storage.bundlefile.ZipBundleFile")) { + if (bundlefile.getClass().getName() + .equals("org.eclipse.osgi.storage.bundlefile.NestedDirBundleFile")) { + final Object baseBundleFile = ReflectionUtils.getFieldVal(bundlefile, "baseBundleFile", + false); + if (baseBundleFile != null && baseBundleFile.getClass().getName() + .equals("org.eclipse.osgi.storage.bundlefile.ZipBundleFile")) { pathElement = baseBundleFile.toString() + "!/" + fieldVal.toString(); } } - classpathOrderOut.addClasspathEntry(pathElement, - classLoader, scanSpec, log); + classpathOrderOut.addClasspathEntry(pathElement, classLoader, scanSpec, log); break; } } From a1db0efd56da7c85f73ba1c1be62692f11ea9f96 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 20 May 2019 14:16:43 -0600 Subject: [PATCH 0210/1778] [maven-release-plugin] prepare release classgraph-4.8.37 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 50fc97aec..859d5914f 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.37-SNAPSHOT + 4.8.37 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.37 From 0c0b125598981e32a9f7fca5cafc0cd249d8826c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 20 May 2019 14:17:06 -0600 Subject: [PATCH 0211/1778] [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 859d5914f..ff61695e0 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.37 + 4.8.38-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.37 + HEAD From e5a1fbbc2a135889a5a56781efa1e2947af366aa Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 20 May 2019 14:29:25 -0600 Subject: [PATCH 0212/1778] Refactoring --- .../EquinoxClassLoaderHandler.java | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) 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 f7eafd117..0a52c6640 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxClassLoaderHandler.java @@ -29,10 +29,7 @@ package nonapi.io.github.classgraph.classloaderhandler; import java.lang.reflect.Array; -import java.util.Arrays; -import java.util.Collections; import java.util.HashSet; -import java.util.List; import java.util.Set; import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; @@ -52,8 +49,7 @@ class EquinoxClassLoaderHandler implements ClassLoaderHandler { private static boolean alreadyReadSystemBundles; /** Field names. */ - private static final List FIELD_NAMES = Collections - .unmodifiableList(Arrays.asList("cp", "nestedDirName")); + private static final String[] FIELD_NAMES = { "cp", "nestedDirName" }; /** Class cannot be constructed. */ private EquinoxClassLoaderHandler() { @@ -106,26 +102,28 @@ 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); - if (basefile != null) { + final Object baseFile = ReflectionUtils.getFieldVal(bundlefile, "basefile", false); + if (baseFile != null) { boolean foundClassPathElement = false; for (final String fieldName : FIELD_NAMES) { final Object fieldVal = ReflectionUtils.getFieldVal(bundlefile, fieldName, false); foundClassPathElement = fieldVal != null; if (foundClassPathElement) { // We found the base file and a classpath element, e.g. "bin/" - String pathElement = basefile.toString() + "/" + fieldVal.toString(); - + Object base = baseFile; + String sep = "/"; if (bundlefile.getClass().getName() .equals("org.eclipse.osgi.storage.bundlefile.NestedDirBundleFile")) { + // Handle nested ZipBundleFile with "!/" separator final Object baseBundleFile = ReflectionUtils.getFieldVal(bundlefile, "baseBundleFile", false); if (baseBundleFile != null && baseBundleFile.getClass().getName() .equals("org.eclipse.osgi.storage.bundlefile.ZipBundleFile")) { - pathElement = baseBundleFile.toString() + "!/" + fieldVal.toString(); + base = baseBundleFile; + sep = "!/"; } } - + final String pathElement = base.toString() + sep + fieldVal.toString(); classpathOrderOut.addClasspathEntry(pathElement, classLoader, scanSpec, log); break; } @@ -133,7 +131,7 @@ private static void addBundleFile(final Object bundlefile, final Set pat if (!foundClassPathElement) { // No classpath element found, just use basefile - classpathOrderOut.addClasspathEntry(basefile.toString(), classLoader, scanSpec, log); + classpathOrderOut.addClasspathEntry(baseFile.toString(), classLoader, scanSpec, log); } } From afcfd9aad421bdc82b1f46d013d0a22f0c533062 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 5 Jun 2019 01:16:15 -0600 Subject: [PATCH 0213/1778] Get subclasses of classes with method or field annotation (#350) --- .../github/classgraph/AnnotationInfoList.java | 21 +++- .../java/io/github/classgraph/ClassInfo.java | 111 +++++++++++++++--- .../java/io/github/classgraph/FieldInfo.java | 3 +- .../java/io/github/classgraph/MethodInfo.java | 11 +- 4 files changed, 121 insertions(+), 25 deletions(-) diff --git a/src/main/java/io/github/classgraph/AnnotationInfoList.java b/src/main/java/io/github/classgraph/AnnotationInfoList.java index 366b95e4c..b31f41897 100644 --- a/src/main/java/io/github/classgraph/AnnotationInfoList.java +++ b/src/main/java/io/github/classgraph/AnnotationInfoList.java @@ -205,11 +205,14 @@ void findReferencedClassNames(final Set referencedClassNames) { * the containing class * @param forwardRelType * the forward relationship type for linking (or null for none) - * @param reverseRelType - * the reverse relationship type for linking (or null for none) + * @param reverseRelType0 + * the first reverse relationship type for linking (or null for none) + * @param reverseRelType1 + * the second reverse relationship type for linking (or null for none) */ void handleRepeatableAnnotations(final Set allRepeatableAnnotationNames, - final ClassInfo containingClassInfo, final RelType forwardRelType, final RelType reverseRelType) { + final ClassInfo containingClassInfo, final RelType forwardRelType, final RelType reverseRelType0, + final RelType reverseRelType1) { List repeatableAnnotations = null; for (int i = size() - 1; i >= 0; --i) { final AnnotationInfo ai = get(i); @@ -237,11 +240,19 @@ void handleRepeatableAnnotations(final Set allRepeatableAnnotationNames, add(ai); // Link annotation, if necessary - if (forwardRelType != null && reverseRelType != null) { + if (forwardRelType != null + && (reverseRelType0 != null || reverseRelType1 != null)) { final ClassInfo annotationClass = ai.getClassInfo(); if (annotationClass != null) { containingClassInfo.addRelatedClass(forwardRelType, annotationClass); - annotationClass.addRelatedClass(reverseRelType, containingClassInfo); + if (reverseRelType0 != null) { + annotationClass.addRelatedClass(reverseRelType0, + containingClassInfo); + } + if (reverseRelType1 != null) { + annotationClass.addRelatedClass(reverseRelType1, + containingClassInfo); + } } } } diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index 8d4dd308b..53f8411fb 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -246,6 +246,12 @@ enum RelType { */ CLASSES_WITH_METHOD_ANNOTATION, + /** + * Classes that have one or more non-private (inherited) methods annotated with this annotation, if this is + * an annotation. + */ + CLASSES_WITH_NONPRIVATE_METHOD_ANNOTATION, + /** Annotations on one or more parameters of methods of this class. */ METHOD_PARAMETER_ANNOTATIONS, @@ -255,6 +261,12 @@ enum RelType { */ CLASSES_WITH_METHOD_PARAMETER_ANNOTATION, + /** + * Classes that have one or more non-private (inherited) methods that have one or more parameters annotated + * with this annotation, if this is an annotation. + */ + CLASSES_WITH_NONPRIVATE_METHOD_PARAMETER_ANNOTATION, + // Field annotations: /** Annotations on one or more fields of this class. */ @@ -264,6 +276,12 @@ enum RelType { * Classes that have one or more fields annotated with this annotation, if this is an annotation. */ CLASSES_WITH_FIELD_ANNOTATION, + + /** + * Classes that have one or more non-private (inherited) fields annotated with this annotation, if this is + * an annotation. + */ + CLASSES_WITH_NONPRIVATE_FIELD_ANNOTATION, } /** @@ -441,11 +459,13 @@ void addClassAnnotation(final AnnotationInfo classAnnotationInfo, * the annotation info list * @param isField * the is field + * @param modifiers + * the field or method modifiers * @param classNameToClassInfo * the map from class name to class info */ private void addFieldOrMethodAnnotationInfo(final AnnotationInfoList annotationInfoList, final boolean isField, - final Map classNameToClassInfo) { + final int modifiers, final Map classNameToClassInfo) { if (annotationInfoList != null) { for (final AnnotationInfo fieldAnnotationInfo : annotationInfoList) { final ClassInfo annotationClassInfo = getOrCreateClassInfo(fieldAnnotationInfo.getName(), @@ -456,6 +476,11 @@ private void addFieldOrMethodAnnotationInfo(final AnnotationInfoList annotationI annotationClassInfo.addRelatedClass( isField ? RelType.CLASSES_WITH_FIELD_ANNOTATION : RelType.CLASSES_WITH_METHOD_ANNOTATION, this); + // For non-private methods/fields, also add to nonprivate (inherited) mapping + if (!Modifier.isPrivate(modifiers)) { + annotationClassInfo.addRelatedClass(isField ? RelType.CLASSES_WITH_NONPRIVATE_FIELD_ANNOTATION + : RelType.CLASSES_WITH_NONPRIVATE_METHOD_ANNOTATION, this); + } } } } @@ -471,7 +496,8 @@ private void addFieldOrMethodAnnotationInfo(final AnnotationInfoList annotationI void addFieldInfo(final FieldInfoList fieldInfoList, final Map classNameToClassInfo) { for (final FieldInfo fi : fieldInfoList) { // Index field annotations - addFieldOrMethodAnnotationInfo(fi.annotationInfo, /* isField = */ true, classNameToClassInfo); + addFieldOrMethodAnnotationInfo(fi.annotationInfo, /* isField = */ true, fi.getModifiers(), + classNameToClassInfo); } if (this.fieldInfo == null) { this.fieldInfo = fieldInfoList; @@ -491,7 +517,8 @@ void addFieldInfo(final FieldInfoList fieldInfoList, final Map classNameToClassInfo) { for (final MethodInfo mi : methodInfoList) { // Index method annotations - addFieldOrMethodAnnotationInfo(mi.annotationInfo, /* isField = */ false, classNameToClassInfo); + addFieldOrMethodAnnotationInfo(mi.annotationInfo, /* isField = */ false, mi.getModifiers(), + classNameToClassInfo); // Index method parameter annotations if (mi.parameterAnnotationInfo != null) { @@ -503,9 +530,14 @@ void addMethodInfo(final MethodInfoList methodInfoList, final Map classesWithMethodAnnotation = new HashSet<>( + getClassesWithFieldOrMethodAnnotation(RelType.CLASSES_WITH_METHOD_ANNOTATION)); + // Add subclasses of all classes with a method that is non-privately annotated or meta-annotated with + // this annotation (non-private methods are inherited) + for (final ClassInfo classWithNonprivateMethodAnnotationOrMetaAnnotation : // + getClassesWithFieldOrMethodAnnotation(RelType.CLASSES_WITH_NONPRIVATE_METHOD_ANNOTATION)) { + classesWithMethodAnnotation.addAll(classWithNonprivateMethodAnnotationOrMetaAnnotation.getSubclasses()); + } + return new ClassInfoList(classesWithMethodAnnotation, + new HashSet<>(getClassesWithMethodAnnotationDirectOnly()), /* sortByName = */ true); } /** - * Get all classes that have this class as a method parameter annotation. + * Get all classes that have this class as a method parameter annotation, and their subclasses, if the method is + * non-private. * * @return A list of classes that have a declared method with a parameter that is annotated with this annotation * or meta-annotation, or the empty list if none. */ public ClassInfoList getClassesWithMethodParameterAnnotation() { - return getClassesWithFieldOrMethodAnnotation(RelType.CLASSES_WITH_METHOD_PARAMETER_ANNOTATION); + // Get all classes that have a method annotated or meta-annotated with this annotation + final Set classesWithMethodParameterAnnotation = new HashSet<>( + getClassesWithFieldOrMethodAnnotation(RelType.CLASSES_WITH_METHOD_PARAMETER_ANNOTATION)); + // Add subclasses of all classes with a method that is non-privately annotated or meta-annotated with + // this annotation (non-private methods are inherited) + for (final ClassInfo classWithNonprivateMethodParameterAnnotationOrMetaAnnotation : // + getClassesWithFieldOrMethodAnnotation(RelType.CLASSES_WITH_NONPRIVATE_METHOD_PARAMETER_ANNOTATION)) { + classesWithMethodParameterAnnotation + .addAll(classWithNonprivateMethodParameterAnnotationOrMetaAnnotation.getSubclasses()); + } + return new ClassInfoList(classesWithMethodParameterAnnotation, + new HashSet<>(getClassesWithMethodParameterAnnotationDirectOnly()), /* sortByName = */ true); } /** @@ -2171,6 +2233,17 @@ ClassInfoList getClassesWithMethodAnnotationDirectOnly() { /* strictWhitelist = */ !isExternalClass), /* sortByName = */ true); } + /** + * Get the classes that have this class as a direct method parameter annotation. + * + * @return A list of classes that declare methods with parameters that are directly annotated (i.e. are not + * meta-annotated) with the requested method annotation, or the empty list if none. + */ + ClassInfoList getClassesWithMethodParameterAnnotationDirectOnly() { + return new ClassInfoList(this.filterClassInfo(RelType.CLASSES_WITH_METHOD_PARAMETER_ANNOTATION, + /* strictWhitelist = */ !isExternalClass), /* sortByName = */ true); + } + // ------------------------------------------------------------------------------------------------------------- // Fields @@ -2339,7 +2412,17 @@ public ClassInfoList getFieldAnnotations() { * none. */ public ClassInfoList getClassesWithFieldAnnotation() { - return getClassesWithFieldOrMethodAnnotation(RelType.CLASSES_WITH_FIELD_ANNOTATION); + // Get all classes that have a field annotated or meta-annotated with this annotation + final Set classesWithMethodAnnotation = new HashSet<>( + getClassesWithFieldOrMethodAnnotation(RelType.CLASSES_WITH_FIELD_ANNOTATION)); + // Add subclasses of all classes with a field that is non-privately annotated or meta-annotated with + // this annotation (non-private fields are inherited) + for (final ClassInfo classWithNonprivateMethodAnnotationOrMetaAnnotation : // + getClassesWithFieldOrMethodAnnotation(RelType.CLASSES_WITH_NONPRIVATE_FIELD_ANNOTATION)) { + classesWithMethodAnnotation.addAll(classWithNonprivateMethodAnnotationOrMetaAnnotation.getSubclasses()); + } + return new ClassInfoList(classesWithMethodAnnotation, + new HashSet<>(getClassesWithMethodAnnotationDirectOnly()), /* sortByName = */ true); } /** @@ -2600,7 +2683,7 @@ void setScanResult(final ScanResult scanResult) { void handleRepeatableAnnotations(final Set allRepeatableAnnotationNames) { if (annotationInfo != null) { annotationInfo.handleRepeatableAnnotations(allRepeatableAnnotationNames, this, - RelType.CLASS_ANNOTATIONS, RelType.CLASSES_WITH_ANNOTATION); + RelType.CLASS_ANNOTATIONS, RelType.CLASSES_WITH_ANNOTATION, null); } if (fieldInfo != null) { for (final FieldInfo fi : fieldInfo) { diff --git a/src/main/java/io/github/classgraph/FieldInfo.java b/src/main/java/io/github/classgraph/FieldInfo.java index 85b33f068..3f104daa3 100644 --- a/src/main/java/io/github/classgraph/FieldInfo.java +++ b/src/main/java/io/github/classgraph/FieldInfo.java @@ -350,7 +350,8 @@ public Field loadClassAndGetField() throws IllegalArgumentException { void handleRepeatableAnnotations(final Set allRepeatableAnnotationNames) { if (annotationInfo != null) { annotationInfo.handleRepeatableAnnotations(allRepeatableAnnotationNames, getClassInfo(), - RelType.FIELD_ANNOTATIONS, RelType.CLASSES_WITH_FIELD_ANNOTATION); + RelType.FIELD_ANNOTATIONS, RelType.CLASSES_WITH_FIELD_ANNOTATION, + RelType.CLASSES_WITH_NONPRIVATE_FIELD_ANNOTATION); } } diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index 997f97eb2..00a2f6cdd 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -570,7 +570,8 @@ public Method loadClassAndGetMethod() throws IllegalArgumentException { void handleRepeatableAnnotations(final Set allRepeatableAnnotationNames) { if (annotationInfo != null) { annotationInfo.handleRepeatableAnnotations(allRepeatableAnnotationNames, getClassInfo(), - RelType.METHOD_ANNOTATIONS, RelType.CLASSES_WITH_METHOD_ANNOTATION); + RelType.METHOD_ANNOTATIONS, RelType.CLASSES_WITH_METHOD_ANNOTATION, + RelType.CLASSES_WITH_NONPRIVATE_METHOD_ANNOTATION); } if (parameterAnnotationInfo != null) { for (int i = 0; i < parameterAnnotationInfo.length; i++) { @@ -588,10 +589,10 @@ void handleRepeatableAnnotations(final Set allRepeatableAnnotationNames) for (final AnnotationInfo ai : pai) { aiList.add(ai); } - // There is currently no RelType.CLASSES_WITH_METHOD_PARAMETER_ANNOTATION, so set - // RelType values to null - aiList.handleRepeatableAnnotations(allRepeatableAnnotationNames, getClassInfo(), null, - null); + aiList.handleRepeatableAnnotations(allRepeatableAnnotationNames, getClassInfo(), + RelType.METHOD_PARAMETER_ANNOTATIONS, + RelType.CLASSES_WITH_METHOD_PARAMETER_ANNOTATION, + RelType.CLASSES_WITH_NONPRIVATE_METHOD_PARAMETER_ANNOTATION); parameterAnnotationInfo[i] = aiList.toArray(new AnnotationInfo[0]); } } From b426f8ae8f0b00898837af91c1d878054556b437 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 7 Jun 2019 02:32:26 -0600 Subject: [PATCH 0214/1778] Add unit test for 350 --- .../classgraph/issues/issue350/Issue350.java | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 src/test/java/io/github/classgraph/issues/issue350/Issue350.java diff --git a/src/test/java/io/github/classgraph/issues/issue350/Issue350.java b/src/test/java/io/github/classgraph/issues/issue350/Issue350.java new file mode 100644 index 000000000..ff27d9372 --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue350/Issue350.java @@ -0,0 +1,73 @@ +package io.github.classgraph.issues.issue350; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import org.junit.Test; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ScanResult; + +/** + * Unit test. + */ +public class Issue350 { + /** */ + @Retention(RetentionPolicy.RUNTIME) + public static @interface SuperclassAnnotation { + } + + /** */ + public static class Pub { + /** */ + @SuperclassAnnotation + public int annotatedPublicField; + + /** */ + @SuperclassAnnotation + public void annotatedPublicMethod() { + } + } + + /** */ + public static class Priv { + /** */ + @SuperclassAnnotation + private int annotatedPrivateField; + + /** */ + @SuperclassAnnotation + private void annotatedPrivateMethod() { + } + } + + /** */ + public static class PubSub extends Pub { + } + + /** */ + 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().whitelistPackages(Issue350.class.getPackage().getName()) + .enableClassInfo().enableFieldInfo().enableMethodInfo().enableAnnotationInfo().scan()) { + assertThat(scanResult.getClassesWithFieldAnnotation(SuperclassAnnotation.class.getName()).getNames()) + .containsExactlyInAnyOrder(Pub.class.getName(), PubSub.class.getName()); + assertThat(scanResult.getClassesWithMethodAnnotation(SuperclassAnnotation.class.getName()).getNames()) + .containsExactlyInAnyOrder(Pub.class.getName(), PubSub.class.getName()); + } + try (ScanResult scanResult = new ClassGraph().whitelistPackages(Issue350.class.getPackage().getName()) + .enableClassInfo().enableFieldInfo().enableMethodInfo().enableAnnotationInfo() + .ignoreFieldVisibility().ignoreMethodVisibility().scan()) { + assertThat(scanResult.getClassesWithFieldAnnotation(SuperclassAnnotation.class.getName()).getNames()) + .containsExactlyInAnyOrder(Pub.class.getName(), PubSub.class.getName(), Priv.class.getName()); + assertThat(scanResult.getClassesWithMethodAnnotation(SuperclassAnnotation.class.getName()).getNames()) + .containsExactlyInAnyOrder(Pub.class.getName(), PubSub.class.getName(), Priv.class.getName()); + } + } +} From 0f0dead29f2d9363863dacc76f047dc278957cae Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 7 Jun 2019 02:33:27 -0600 Subject: [PATCH 0215/1778] [maven-release-plugin] prepare release classgraph-4.8.38 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ff61695e0..8aeb0261f 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.38-SNAPSHOT + 4.8.38 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.38 From 472f9eade3c661b5610f51a0a353552ba03dbfbb Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 7 Jun 2019 02:33:37 -0600 Subject: [PATCH 0216/1778] [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 8aeb0261f..42c32f943 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.38 + 4.8.39-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.38 + HEAD From c7fac8a047c9c37ad28400d9cb0396f6b94918a8 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 12 Jun 2019 03:07:33 -0600 Subject: [PATCH 0217/1778] Add `emptyList()` method (#351) --- .../java/io/github/classgraph/AnnotationInfoList.java | 9 +++++++++ .../github/classgraph/AnnotationParameterValueList.java | 9 +++++++++ src/main/java/io/github/classgraph/ClassInfoList.java | 9 +++++++++ src/main/java/io/github/classgraph/FieldInfoList.java | 9 +++++++++ src/main/java/io/github/classgraph/MethodInfoList.java | 9 +++++++++ src/main/java/io/github/classgraph/ResourceList.java | 9 +++++++++ 6 files changed, 54 insertions(+) diff --git a/src/main/java/io/github/classgraph/AnnotationInfoList.java b/src/main/java/io/github/classgraph/AnnotationInfoList.java index b31f41897..d1a2385a5 100644 --- a/src/main/java/io/github/classgraph/AnnotationInfoList.java +++ b/src/main/java/io/github/classgraph/AnnotationInfoList.java @@ -141,6 +141,15 @@ public AnnotationInfo set(final int index, final AnnotationInfo element) { throw new IllegalArgumentException("List is immutable"); } }; + + /** + * Return an unmodifiable empty {@link AnnotationInfoList}. + * + * @return the unmodifiable empty {@link AnnotationInfoList}. + */ + public static AnnotationInfoList emptyList() { + return EMPTY_LIST; + } // ------------------------------------------------------------------------------------------------------------- diff --git a/src/main/java/io/github/classgraph/AnnotationParameterValueList.java b/src/main/java/io/github/classgraph/AnnotationParameterValueList.java index 135201fbe..8b91dc9b6 100644 --- a/src/main/java/io/github/classgraph/AnnotationParameterValueList.java +++ b/src/main/java/io/github/classgraph/AnnotationParameterValueList.java @@ -112,6 +112,15 @@ public AnnotationParameterValue set(final int index, final AnnotationParameterVa throw new IllegalArgumentException("List is immutable"); } }; + + /** + * Return an unmodifiable empty {@link AnnotationParameterValueList}. + * + * @return the unmodifiable empty {@link AnnotationParameterValueList}. + */ + public static AnnotationParameterValueList emptyList() { + return EMPTY_LIST; + } // ------------------------------------------------------------------------------------------------------------- diff --git a/src/main/java/io/github/classgraph/ClassInfoList.java b/src/main/java/io/github/classgraph/ClassInfoList.java index 77f71e68c..8b9a748df 100644 --- a/src/main/java/io/github/classgraph/ClassInfoList.java +++ b/src/main/java/io/github/classgraph/ClassInfoList.java @@ -175,6 +175,15 @@ public ClassInfo set(final int index, final ClassInfo element) { throw new IllegalArgumentException("List is immutable"); } }; + + /** + * Return an unmodifiable empty {@link ClassInfoList}. + * + * @return the unmodifiable empty {@link ClassInfoList}. + */ + public static ClassInfoList emptyList() { + return EMPTY_LIST; + } // ------------------------------------------------------------------------------------------------------------- diff --git a/src/main/java/io/github/classgraph/FieldInfoList.java b/src/main/java/io/github/classgraph/FieldInfoList.java index e6a6feb7d..f01000921 100644 --- a/src/main/java/io/github/classgraph/FieldInfoList.java +++ b/src/main/java/io/github/classgraph/FieldInfoList.java @@ -112,6 +112,15 @@ public FieldInfo set(final int index, final FieldInfo element) { throw new IllegalArgumentException("List is immutable"); } }; + + /** + * Return an unmodifiable empty {@link FieldInfoList}. + * + * @return the unmodifiable empty {@link FieldInfoList}. + */ + public static FieldInfoList emptyList() { + return EMPTY_LIST; + } // ------------------------------------------------------------------------------------------------------------- diff --git a/src/main/java/io/github/classgraph/MethodInfoList.java b/src/main/java/io/github/classgraph/MethodInfoList.java index 288833a3f..3fb0d1b4d 100644 --- a/src/main/java/io/github/classgraph/MethodInfoList.java +++ b/src/main/java/io/github/classgraph/MethodInfoList.java @@ -112,6 +112,15 @@ public MethodInfo set(final int index, final MethodInfo element) { throw new IllegalArgumentException("List is immutable"); } }; + + /** + * Return an unmodifiable empty {@link MethodInfoList}. + * + * @return the unmodifiable empty {@link MethodInfoList}. + */ + public static MethodInfoList emptyList() { + return EMPTY_LIST; + } // ------------------------------------------------------------------------------------------------------------- diff --git a/src/main/java/io/github/classgraph/ResourceList.java b/src/main/java/io/github/classgraph/ResourceList.java index 07f8a34f6..f71c98160 100644 --- a/src/main/java/io/github/classgraph/ResourceList.java +++ b/src/main/java/io/github/classgraph/ResourceList.java @@ -101,6 +101,15 @@ public Resource set(final int index, final Resource element) { throw new IllegalArgumentException("List is immutable"); } }; + + /** + * Return an unmodifiable empty {@link ResourceList}. + * + * @return the unmodifiable empty {@link ResourceList}. + */ + public static ResourceList emptyList() { + return EMPTY_LIST; + } /** * Constructor. From a499d1bb40e0c83dbe686d2583c201a9f4f6e4b0 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 12 Jun 2019 03:20:23 -0600 Subject: [PATCH 0218/1778] Add iterators to empty list to prevent ConcurrentModificationException (#334) --- .../github/classgraph/AnnotationInfoList.java | 99 ++++++++++++++++++- .../AnnotationParameterValueList.java | 99 ++++++++++++++++++- .../io/github/classgraph/ClassInfoList.java | 99 ++++++++++++++++++- .../io/github/classgraph/FieldInfoList.java | 99 ++++++++++++++++++- .../io/github/classgraph/MethodInfoList.java | 99 ++++++++++++++++++- .../io/github/classgraph/ResourceList.java | 99 ++++++++++++++++++- 6 files changed, 588 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/github/classgraph/AnnotationInfoList.java b/src/main/java/io/github/classgraph/AnnotationInfoList.java index d1a2385a5..c633e6483 100644 --- a/src/main/java/io/github/classgraph/AnnotationInfoList.java +++ b/src/main/java/io/github/classgraph/AnnotationInfoList.java @@ -32,8 +32,12 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; +import java.util.Iterator; import java.util.List; +import java.util.ListIterator; import java.util.Set; +import java.util.Spliterator; +import java.util.function.Consumer; import io.github.classgraph.ClassInfo.RelType; import nonapi.io.github.classgraph.utils.CollectionUtils; @@ -140,8 +144,101 @@ public void clear() { public AnnotationInfo set(final int index, final AnnotationInfo element) { throw new IllegalArgumentException("List is immutable"); } + + // Provide replacement iterators so that there is no chance of a thread that + // is trying to sort the empty list causing a ConcurrentModificationException + // in another thread that is iterating over the empty list (#334) + @Override + public Iterator iterator() { + return new Iterator() { + @Override + public boolean hasNext() { + return false; + } + + @Override + public AnnotationInfo next() { + return null; + } + }; + } + + @Override + public Spliterator spliterator() { + return new Spliterator() { + @Override + public boolean tryAdvance(Consumer action) { + return false; + } + + @Override + public Spliterator trySplit() { + return null; + } + + @Override + public long estimateSize() { + return 0; + } + + @Override + public int characteristics() { + return 0; + } + }; + } + + @Override + public ListIterator listIterator() { + return new ListIterator() { + @Override + public boolean hasNext() { + return false; + } + + @Override + public AnnotationInfo next() { + return null; + } + + @Override + public boolean hasPrevious() { + return false; + } + + @Override + public AnnotationInfo previous() { + return null; + } + + @Override + public int nextIndex() { + return 0; + } + + @Override + public int previousIndex() { + return 0; + } + + @Override + public void remove() { + throw new IllegalArgumentException("List is immutable"); + } + + @Override + public void set(AnnotationInfo e) { + throw new IllegalArgumentException("List is immutable"); + } + + @Override + public void add(AnnotationInfo e) { + throw new IllegalArgumentException("List is immutable"); + } + }; + } }; - + /** * Return an unmodifiable empty {@link AnnotationInfoList}. * diff --git a/src/main/java/io/github/classgraph/AnnotationParameterValueList.java b/src/main/java/io/github/classgraph/AnnotationParameterValueList.java index 8b91dc9b6..7c8b8cc89 100644 --- a/src/main/java/io/github/classgraph/AnnotationParameterValueList.java +++ b/src/main/java/io/github/classgraph/AnnotationParameterValueList.java @@ -29,7 +29,11 @@ package io.github.classgraph; import java.util.Collection; +import java.util.Iterator; +import java.util.ListIterator; import java.util.Set; +import java.util.Spliterator; +import java.util.function.Consumer; /** A list of {@link AnnotationParameterValue} objects. */ public class AnnotationParameterValueList extends MappableInfoList { @@ -111,8 +115,101 @@ public void clear() { public AnnotationParameterValue set(final int index, final AnnotationParameterValue element) { throw new IllegalArgumentException("List is immutable"); } + + // Provide replacement iterators so that there is no chance of a thread that + // is trying to sort the empty list causing a ConcurrentModificationException + // in another thread that is iterating over the empty list (#334) + @Override + public Iterator iterator() { + return new Iterator() { + @Override + public boolean hasNext() { + return false; + } + + @Override + public AnnotationParameterValue next() { + return null; + } + }; + } + + @Override + public Spliterator spliterator() { + return new Spliterator() { + @Override + public boolean tryAdvance(Consumer action) { + return false; + } + + @Override + public Spliterator trySplit() { + return null; + } + + @Override + public long estimateSize() { + return 0; + } + + @Override + public int characteristics() { + return 0; + } + }; + } + + @Override + public ListIterator listIterator() { + return new ListIterator() { + @Override + public boolean hasNext() { + return false; + } + + @Override + public AnnotationParameterValue next() { + return null; + } + + @Override + public boolean hasPrevious() { + return false; + } + + @Override + public AnnotationParameterValue previous() { + return null; + } + + @Override + public int nextIndex() { + return 0; + } + + @Override + public int previousIndex() { + return 0; + } + + @Override + public void remove() { + throw new IllegalArgumentException("List is immutable"); + } + + @Override + public void set(AnnotationParameterValue e) { + throw new IllegalArgumentException("List is immutable"); + } + + @Override + public void add(AnnotationParameterValue e) { + throw new IllegalArgumentException("List is immutable"); + } + }; + } }; - + /** * Return an unmodifiable empty {@link AnnotationParameterValueList}. * diff --git a/src/main/java/io/github/classgraph/ClassInfoList.java b/src/main/java/io/github/classgraph/ClassInfoList.java index 8b9a748df..c227cc84b 100644 --- a/src/main/java/io/github/classgraph/ClassInfoList.java +++ b/src/main/java/io/github/classgraph/ClassInfoList.java @@ -36,9 +36,13 @@ import java.util.Collection; import java.util.Collections; import java.util.HashSet; +import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; +import java.util.ListIterator; import java.util.Set; +import java.util.Spliterator; +import java.util.function.Consumer; import io.github.classgraph.ClassInfo.ReachableAndDirectlyRelatedClasses; import nonapi.io.github.classgraph.scanspec.ScanSpec; @@ -174,8 +178,101 @@ public void clear() { public ClassInfo set(final int index, final ClassInfo element) { throw new IllegalArgumentException("List is immutable"); } + + // Provide replacement iterators so that there is no chance of a thread that + // is trying to sort the empty list causing a ConcurrentModificationException + // in another thread that is iterating over the empty list (#334) + @Override + public Iterator iterator() { + return new Iterator() { + @Override + public boolean hasNext() { + return false; + } + + @Override + public ClassInfo next() { + return null; + } + }; + } + + @Override + public Spliterator spliterator() { + return new Spliterator() { + @Override + public boolean tryAdvance(Consumer action) { + return false; + } + + @Override + public Spliterator trySplit() { + return null; + } + + @Override + public long estimateSize() { + return 0; + } + + @Override + public int characteristics() { + return 0; + } + }; + } + + @Override + public ListIterator listIterator() { + return new ListIterator() { + @Override + public boolean hasNext() { + return false; + } + + @Override + public ClassInfo next() { + return null; + } + + @Override + public boolean hasPrevious() { + return false; + } + + @Override + public ClassInfo previous() { + return null; + } + + @Override + public int nextIndex() { + return 0; + } + + @Override + public int previousIndex() { + return 0; + } + + @Override + public void remove() { + throw new IllegalArgumentException("List is immutable"); + } + + @Override + public void set(ClassInfo e) { + throw new IllegalArgumentException("List is immutable"); + } + + @Override + public void add(ClassInfo e) { + throw new IllegalArgumentException("List is immutable"); + } + }; + } }; - + /** * Return an unmodifiable empty {@link ClassInfoList}. * diff --git a/src/main/java/io/github/classgraph/FieldInfoList.java b/src/main/java/io/github/classgraph/FieldInfoList.java index f01000921..6bd29ab8c 100644 --- a/src/main/java/io/github/classgraph/FieldInfoList.java +++ b/src/main/java/io/github/classgraph/FieldInfoList.java @@ -29,7 +29,11 @@ package io.github.classgraph; import java.util.Collection; +import java.util.Iterator; +import java.util.ListIterator; import java.util.Set; +import java.util.Spliterator; +import java.util.function.Consumer; /** A list of {@link FieldInfo} objects. */ public class FieldInfoList extends MappableInfoList { @@ -111,8 +115,101 @@ public void clear() { public FieldInfo set(final int index, final FieldInfo element) { throw new IllegalArgumentException("List is immutable"); } + + // Provide replacement iterators so that there is no chance of a thread that + // is trying to sort the empty list causing a ConcurrentModificationException + // in another thread that is iterating over the empty list (#334) + @Override + public Iterator iterator() { + return new Iterator() { + @Override + public boolean hasNext() { + return false; + } + + @Override + public FieldInfo next() { + return null; + } + }; + } + + @Override + public Spliterator spliterator() { + return new Spliterator() { + @Override + public boolean tryAdvance(Consumer action) { + return false; + } + + @Override + public Spliterator trySplit() { + return null; + } + + @Override + public long estimateSize() { + return 0; + } + + @Override + public int characteristics() { + return 0; + } + }; + } + + @Override + public ListIterator listIterator() { + return new ListIterator() { + @Override + public boolean hasNext() { + return false; + } + + @Override + public FieldInfo next() { + return null; + } + + @Override + public boolean hasPrevious() { + return false; + } + + @Override + public FieldInfo previous() { + return null; + } + + @Override + public int nextIndex() { + return 0; + } + + @Override + public int previousIndex() { + return 0; + } + + @Override + public void remove() { + throw new IllegalArgumentException("List is immutable"); + } + + @Override + public void set(FieldInfo e) { + throw new IllegalArgumentException("List is immutable"); + } + + @Override + public void add(FieldInfo e) { + throw new IllegalArgumentException("List is immutable"); + } + }; + } }; - + /** * Return an unmodifiable empty {@link FieldInfoList}. * diff --git a/src/main/java/io/github/classgraph/MethodInfoList.java b/src/main/java/io/github/classgraph/MethodInfoList.java index 3fb0d1b4d..221799ecd 100644 --- a/src/main/java/io/github/classgraph/MethodInfoList.java +++ b/src/main/java/io/github/classgraph/MethodInfoList.java @@ -30,8 +30,12 @@ import java.util.Collection; import java.util.HashMap; +import java.util.Iterator; +import java.util.ListIterator; import java.util.Map; import java.util.Set; +import java.util.Spliterator; +import java.util.function.Consumer; /** A list of {@link MethodInfo} objects. */ public class MethodInfoList extends InfoList { @@ -111,8 +115,101 @@ public void clear() { public MethodInfo set(final int index, final MethodInfo element) { throw new IllegalArgumentException("List is immutable"); } + + // Provide replacement iterators so that there is no chance of a thread that + // is trying to sort the empty list causing a ConcurrentModificationException + // in another thread that is iterating over the empty list (#334) + @Override + public Iterator iterator() { + return new Iterator() { + @Override + public boolean hasNext() { + return false; + } + + @Override + public MethodInfo next() { + return null; + } + }; + } + + @Override + public Spliterator spliterator() { + return new Spliterator() { + @Override + public boolean tryAdvance(Consumer action) { + return false; + } + + @Override + public Spliterator trySplit() { + return null; + } + + @Override + public long estimateSize() { + return 0; + } + + @Override + public int characteristics() { + return 0; + } + }; + } + + @Override + public ListIterator listIterator() { + return new ListIterator() { + @Override + public boolean hasNext() { + return false; + } + + @Override + public MethodInfo next() { + return null; + } + + @Override + public boolean hasPrevious() { + return false; + } + + @Override + public MethodInfo previous() { + return null; + } + + @Override + public int nextIndex() { + return 0; + } + + @Override + public int previousIndex() { + return 0; + } + + @Override + public void remove() { + throw new IllegalArgumentException("List is immutable"); + } + + @Override + public void set(MethodInfo e) { + throw new IllegalArgumentException("List is immutable"); + } + + @Override + public void add(MethodInfo e) { + throw new IllegalArgumentException("List is immutable"); + } + }; + } }; - + /** * Return an unmodifiable empty {@link MethodInfoList}. * diff --git a/src/main/java/io/github/classgraph/ResourceList.java b/src/main/java/io/github/classgraph/ResourceList.java index f71c98160..05680d050 100644 --- a/src/main/java/io/github/classgraph/ResourceList.java +++ b/src/main/java/io/github/classgraph/ResourceList.java @@ -38,9 +38,13 @@ import java.util.Collection; import java.util.Comparator; import java.util.HashMap; +import java.util.Iterator; import java.util.List; +import java.util.ListIterator; import java.util.Map; +import java.util.Spliterator; import java.util.Map.Entry; +import java.util.function.Consumer; import nonapi.io.github.classgraph.utils.CollectionUtils; @@ -100,8 +104,101 @@ public void clear() { public Resource set(final int index, final Resource element) { throw new IllegalArgumentException("List is immutable"); } + + // Provide replacement iterators so that there is no chance of a thread that + // is trying to sort the empty list causing a ConcurrentModificationException + // in another thread that is iterating over the empty list (#334) + @Override + public Iterator iterator() { + return new Iterator() { + @Override + public boolean hasNext() { + return false; + } + + @Override + public Resource next() { + return null; + } + }; + } + + @Override + public Spliterator spliterator() { + return new Spliterator() { + @Override + public boolean tryAdvance(Consumer action) { + return false; + } + + @Override + public Spliterator trySplit() { + return null; + } + + @Override + public long estimateSize() { + return 0; + } + + @Override + public int characteristics() { + return 0; + } + }; + } + + @Override + public ListIterator listIterator() { + return new ListIterator() { + @Override + public boolean hasNext() { + return false; + } + + @Override + public Resource next() { + return null; + } + + @Override + public boolean hasPrevious() { + return false; + } + + @Override + public Resource previous() { + return null; + } + + @Override + public int nextIndex() { + return 0; + } + + @Override + public int previousIndex() { + return 0; + } + + @Override + public void remove() { + throw new IllegalArgumentException("List is immutable"); + } + + @Override + public void set(Resource e) { + throw new IllegalArgumentException("List is immutable"); + } + + @Override + public void add(Resource e) { + throw new IllegalArgumentException("List is immutable"); + } + }; + } }; - + /** * Return an unmodifiable empty {@link ResourceList}. * From 70079b9ac499a936abf5712c64b0047fdc212abb Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 12 Jun 2019 03:49:47 -0600 Subject: [PATCH 0219/1778] Expose constructors publicly (#350) --- .../github/classgraph/AnnotationInfoList.java | 91 ++++++----- .../AnnotationParameterValueList.java | 61 +++---- .../io/github/classgraph/ClassInfoList.java | 154 +++++++++++------- .../io/github/classgraph/FieldInfoList.java | 59 +++---- .../io/github/classgraph/MethodInfoList.java | 56 ++++--- .../io/github/classgraph/ResourceList.java | 26 +-- 6 files changed, 242 insertions(+), 205 deletions(-) diff --git a/src/main/java/io/github/classgraph/AnnotationInfoList.java b/src/main/java/io/github/classgraph/AnnotationInfoList.java index c633e6483..be280504b 100644 --- a/src/main/java/io/github/classgraph/AnnotationInfoList.java +++ b/src/main/java/io/github/classgraph/AnnotationInfoList.java @@ -51,48 +51,6 @@ public class AnnotationInfoList extends MappableInfoList { */ private AnnotationInfoList directlyRelatedAnnotations; - /** - * Constructor. - */ - AnnotationInfoList() { - super(); - } - - /** - * Constructor. - * - * @param sizeHint - * the size hint - */ - AnnotationInfoList(final int sizeHint) { - super(sizeHint); - } - - /** - * Constructor. - * - * @param reachableAnnotations - * the reachable annotations - */ - AnnotationInfoList(final AnnotationInfoList reachableAnnotations) { - // If only reachable annotations are given, treat all of them as direct - this(reachableAnnotations, reachableAnnotations); - } - - /** - * Constructor. - * - * @param reachableAnnotations - * the reachable annotations - * @param directlyRelatedAnnotations - * the directly related annotations - */ - AnnotationInfoList(final AnnotationInfoList reachableAnnotations, - final AnnotationInfoList directlyRelatedAnnotations) { - super(reachableAnnotations); - this.directlyRelatedAnnotations = directlyRelatedAnnotations; - } - /** An unmodifiable empty {@link AnnotationInfoList}. */ static final AnnotationInfoList EMPTY_LIST = new AnnotationInfoList() { @Override @@ -167,7 +125,7 @@ public AnnotationInfo next() { public Spliterator spliterator() { return new Spliterator() { @Override - public boolean tryAdvance(Consumer action) { + public boolean tryAdvance(final Consumer action) { return false; } @@ -227,12 +185,12 @@ public void remove() { } @Override - public void set(AnnotationInfo e) { + public void set(final AnnotationInfo e) { throw new IllegalArgumentException("List is immutable"); } @Override - public void add(AnnotationInfo e) { + public void add(final AnnotationInfo e) { throw new IllegalArgumentException("List is immutable"); } }; @@ -248,6 +206,49 @@ public static AnnotationInfoList emptyList() { return EMPTY_LIST; } + /** + * Construct a new modifiable empty list of {@link AnnotationInfo} objects. + */ + public AnnotationInfoList() { + super(); + } + + /** + * Construct a new modifiable empty list of {@link AnnotationInfo} objects, given a size hint. + * + * @param sizeHint + * the size hint + */ + public AnnotationInfoList(final int sizeHint) { + super(sizeHint); + } + + /** + * Construct a new modifiable empty {@link AnnotationInfoList}, given an initial list of {@link AnnotationInfo} + * objects. + * + * @param reachableAnnotations + * the reachable annotations + */ + public AnnotationInfoList(final AnnotationInfoList reachableAnnotations) { + // If only reachable annotations are given, treat all of them as direct + this(reachableAnnotations, reachableAnnotations); + } + + /** + * Constructor. + * + * @param reachableAnnotations + * the reachable annotations + * @param directlyRelatedAnnotations + * the directly related annotations + */ + AnnotationInfoList(final AnnotationInfoList reachableAnnotations, + final AnnotationInfoList directlyRelatedAnnotations) { + super(reachableAnnotations); + this.directlyRelatedAnnotations = directlyRelatedAnnotations; + } + // ------------------------------------------------------------------------------------------------------------- /** diff --git a/src/main/java/io/github/classgraph/AnnotationParameterValueList.java b/src/main/java/io/github/classgraph/AnnotationParameterValueList.java index 7c8b8cc89..2c9ab8a2d 100644 --- a/src/main/java/io/github/classgraph/AnnotationParameterValueList.java +++ b/src/main/java/io/github/classgraph/AnnotationParameterValueList.java @@ -37,32 +37,6 @@ /** A list of {@link AnnotationParameterValue} objects. */ public class AnnotationParameterValueList extends MappableInfoList { - /** - * Constructor. - */ - AnnotationParameterValueList() { - super(); - } - - /** - * Constructor. - * - * @param sizeHint - * the size hint - */ - AnnotationParameterValueList(final int sizeHint) { - super(sizeHint); - } - - /** - * Constructor. - * - * @param annotationParameterValueCollection - * the annotation parameter value collection - */ - AnnotationParameterValueList(final Collection annotationParameterValueCollection) { - super(annotationParameterValueCollection); - } /** An unmodifiable empty {@link AnnotationParameterValueList}. */ static final AnnotationParameterValueList EMPTY_LIST = new AnnotationParameterValueList() { @@ -138,7 +112,7 @@ public AnnotationParameterValue next() { public Spliterator spliterator() { return new Spliterator() { @Override - public boolean tryAdvance(Consumer action) { + public boolean tryAdvance(final Consumer action) { return false; } @@ -198,12 +172,12 @@ public void remove() { } @Override - public void set(AnnotationParameterValue e) { + public void set(final AnnotationParameterValue e) { throw new IllegalArgumentException("List is immutable"); } @Override - public void add(AnnotationParameterValue e) { + public void add(final AnnotationParameterValue e) { throw new IllegalArgumentException("List is immutable"); } }; @@ -219,6 +193,35 @@ public static AnnotationParameterValueList emptyList() { return EMPTY_LIST; } + /** + * Construct a new modifiable empty list of {@link AnnotationParameterValue} objects. + */ + public AnnotationParameterValueList() { + super(); + } + + /** + * Construct a new modifiable empty list of {@link AnnotationParameterValue} objects, given a size hint. + * + * @param sizeHint + * the size hint + */ + public AnnotationParameterValueList(final int sizeHint) { + super(sizeHint); + } + + /** + * Construct a new modifiable empty {@link AnnotationParameterValueList}, given an initial list of + * {@link AnnotationParameterValue} objects. + * + * @param annotationParameterValueCollection + * the collection of {@link AnnotationParameterValue} objects. + */ + public AnnotationParameterValueList( + final Collection annotationParameterValueCollection) { + super(annotationParameterValueCollection); + } + // ------------------------------------------------------------------------------------------------------------- /** diff --git a/src/main/java/io/github/classgraph/ClassInfoList.java b/src/main/java/io/github/classgraph/ClassInfoList.java index c227cc84b..f311fff2b 100644 --- a/src/main/java/io/github/classgraph/ClassInfoList.java +++ b/src/main/java/io/github/classgraph/ClassInfoList.java @@ -68,65 +68,6 @@ public class ClassInfoList extends MappableInfoList { /** Whether to sort by name. */ private final boolean sortByName; - /** - * Construct a list of {@link ClassInfo} objects, consisting of reachable classes (obtained through the - * transitive closure) and directly related classes (one step away in the graph). - * - * @param reachableClasses - * reachable classes - * @param directlyRelatedClasses - * directly related classes - * @param sortByName - * whether to sort by name - */ - ClassInfoList(final Set reachableClasses, final Set directlyRelatedClasses, - final boolean sortByName) { - super(reachableClasses); - this.sortByName = sortByName; - if (sortByName) { - // It's a bit dicey calling CollectionUtils.sortIfNotEmpty(this) from within a constructor, but the super-constructor - // has been called, so it should be fine :-) - CollectionUtils.sortIfNotEmpty(this); - } - // If directlyRelatedClasses was not provided, then assume all reachable classes were directly related - this.directlyRelatedClasses = directlyRelatedClasses == null ? reachableClasses : directlyRelatedClasses; - } - - /** - * Construct a list of {@link ClassInfo} objects. - * - * @param reachableAndDirectlyRelatedClasses - * reachable and directly related classes - * @param sortByName - * whether to sort by name - */ - ClassInfoList(final ReachableAndDirectlyRelatedClasses reachableAndDirectlyRelatedClasses, - final boolean sortByName) { - this(reachableAndDirectlyRelatedClasses.reachableClasses, - reachableAndDirectlyRelatedClasses.directlyRelatedClasses, sortByName); - } - - /** - * Construct a list of {@link ClassInfo} objects, where each class is directly related. - * - * @param reachableClasses - * reachable classes - * @param sortByName - * whether to sort by name - */ - ClassInfoList(final Set reachableClasses, final boolean sortByName) { - this(reachableClasses, null, sortByName); - } - - /** - * Constructor. - */ - private ClassInfoList() { - super(1); - this.sortByName = false; - directlyRelatedClasses = Collections.emptySet(); - } - /** An unmodifiable empty {@link ClassInfoList}. */ static final ClassInfoList EMPTY_LIST = new ClassInfoList() { @Override @@ -201,7 +142,7 @@ public ClassInfo next() { public Spliterator spliterator() { return new Spliterator() { @Override - public boolean tryAdvance(Consumer action) { + public boolean tryAdvance(final Consumer action) { return false; } @@ -261,12 +202,12 @@ public void remove() { } @Override - public void set(ClassInfo e) { + public void set(final ClassInfo e) { throw new IllegalArgumentException("List is immutable"); } @Override - public void add(ClassInfo e) { + public void add(final ClassInfo e) { throw new IllegalArgumentException("List is immutable"); } }; @@ -282,6 +223,95 @@ public static ClassInfoList emptyList() { return EMPTY_LIST; } + /** + * Construct a modifiable list of {@link ClassInfo} objects, consisting of reachable classes (obtained through + * the transitive closure) and directly related classes (one step away in the graph). + * + * @param reachableClasses + * reachable classes + * @param directlyRelatedClasses + * directly related classes + * @param sortByName + * whether to sort by name + */ + ClassInfoList(final Set reachableClasses, final Set directlyRelatedClasses, + final boolean sortByName) { + super(reachableClasses); + this.sortByName = sortByName; + if (sortByName) { + // It's a bit dicey calling CollectionUtils.sortIfNotEmpty(this) from within a constructor, + // but the super-constructor has been called, so it should be fine :-) + CollectionUtils.sortIfNotEmpty(this); + } + // If directlyRelatedClasses was not provided, then assume all reachable classes were directly related + this.directlyRelatedClasses = directlyRelatedClasses == null ? reachableClasses : directlyRelatedClasses; + } + + /** + * Construct a modifiable list of {@link ClassInfo} objects. + * + * @param reachableAndDirectlyRelatedClasses + * reachable and directly related classes + * @param sortByName + * whether to sort by name + */ + ClassInfoList(final ReachableAndDirectlyRelatedClasses reachableAndDirectlyRelatedClasses, + final boolean sortByName) { + this(reachableAndDirectlyRelatedClasses.reachableClasses, + reachableAndDirectlyRelatedClasses.directlyRelatedClasses, sortByName); + } + + /** + * Construct a modifiable list of {@link ClassInfo} objects, where each class is directly related. + * + * @param reachableClasses + * reachable classes + * @param sortByName + * whether to sort by name + */ + ClassInfoList(final Set reachableClasses, final boolean sortByName) { + this(reachableClasses, /* directlyRelatedClasses = */ null, sortByName); + } + + /** + * Construct a new empty modifiable list of {@link ClassInfo} objects. + */ + public ClassInfoList() { + super(1); + this.sortByName = false; + directlyRelatedClasses = new HashSet<>(2); + } + + /** + * Construct a new empty modifiable list of {@link ClassInfo} objects, given a size hint. + * + * @param sizeHint + * the size hint. + */ + public ClassInfoList(final int sizeHint) { + super(sizeHint); + this.sortByName = false; + directlyRelatedClasses = new HashSet<>(2); + } + + /** + * Construct a new modifiable empty {@link ClassInfoList}, given an initial list of {@link ClassInfo} objects. + * + *

+ * If the passed {@link Collection} is not a {@link Set}, then the {@link ClassInfo} objects will be uniquified + * (by adding them to a set) before they are added to the returned list. {@link ClassInfo} objects in the + * returned list will be sorted by name. + * + * @param classInfoCollection + * the initial collection of {@link ClassInfo} objects to add to the {@link ClassInfoList}. + */ + public ClassInfoList(final Collection classInfoCollection) { + this(classInfoCollection instanceof Set // + ? (Set) classInfoCollection + : new HashSet(classInfoCollection), // + /* directlyRelatedClasses = */ null, /* sortByName = */ true); + } + // ------------------------------------------------------------------------------------------------------------- /** diff --git a/src/main/java/io/github/classgraph/FieldInfoList.java b/src/main/java/io/github/classgraph/FieldInfoList.java index 6bd29ab8c..d423b5afd 100644 --- a/src/main/java/io/github/classgraph/FieldInfoList.java +++ b/src/main/java/io/github/classgraph/FieldInfoList.java @@ -37,32 +37,6 @@ /** A list of {@link FieldInfo} objects. */ public class FieldInfoList extends MappableInfoList { - /** - * Constructor. - */ - FieldInfoList() { - super(); - } - - /** - * Constructor. - * - * @param sizeHint - * the size hint - */ - FieldInfoList(final int sizeHint) { - super(sizeHint); - } - - /** - * Constructor. - * - * @param fieldInfoCollection - * the field info collection - */ - FieldInfoList(final Collection fieldInfoCollection) { - super(fieldInfoCollection); - } /** An unmodifiable empty {@link FieldInfoList}. */ static final FieldInfoList EMPTY_LIST = new FieldInfoList() { @@ -138,7 +112,7 @@ public FieldInfo next() { public Spliterator spliterator() { return new Spliterator() { @Override - public boolean tryAdvance(Consumer action) { + public boolean tryAdvance(final Consumer action) { return false; } @@ -198,12 +172,12 @@ public void remove() { } @Override - public void set(FieldInfo e) { + public void set(final FieldInfo e) { throw new IllegalArgumentException("List is immutable"); } @Override - public void add(FieldInfo e) { + public void add(final FieldInfo e) { throw new IllegalArgumentException("List is immutable"); } }; @@ -219,6 +193,33 @@ public static FieldInfoList emptyList() { return EMPTY_LIST; } + /** + * Construct a new modifiable empty list of {@link FieldInfo} objects. + */ + public FieldInfoList() { + super(); + } + + /** + * Construct a new modifiable empty list of {@link FieldInfo} objects, given a size hint. + * + * @param sizeHint + * the size hint + */ + public FieldInfoList(final int sizeHint) { + super(sizeHint); + } + + /** + * Construct a new modifiable empty {@link FieldInfoList}, given an initial list of {@link FieldInfo} objects. + * + * @param fieldInfoCollection + * the collection of {@link FieldInfo} objects. + */ + public FieldInfoList(final Collection fieldInfoCollection) { + super(fieldInfoCollection); + } + // ------------------------------------------------------------------------------------------------------------- /** diff --git a/src/main/java/io/github/classgraph/MethodInfoList.java b/src/main/java/io/github/classgraph/MethodInfoList.java index 221799ecd..1568e0567 100644 --- a/src/main/java/io/github/classgraph/MethodInfoList.java +++ b/src/main/java/io/github/classgraph/MethodInfoList.java @@ -39,30 +39,6 @@ /** A list of {@link MethodInfo} objects. */ public class MethodInfoList extends InfoList { - /** Construct a list of {@link MethodInfo} objects. */ - MethodInfoList() { - super(); - } - - /** - * Constructor. - * - * @param sizeHint - * the size hint - */ - MethodInfoList(final int sizeHint) { - super(sizeHint); - } - - /** - * Constructor. - * - * @param methodInfoCollection - * the method info collection - */ - MethodInfoList(final Collection methodInfoCollection) { - super(methodInfoCollection); - } /** An unmodifiable empty {@link MethodInfoList}. */ static final MethodInfoList EMPTY_LIST = new MethodInfoList() { @@ -138,7 +114,7 @@ public MethodInfo next() { public Spliterator spliterator() { return new Spliterator() { @Override - public boolean tryAdvance(Consumer action) { + public boolean tryAdvance(final Consumer action) { return false; } @@ -198,12 +174,12 @@ public void remove() { } @Override - public void set(MethodInfo e) { + public void set(final MethodInfo e) { throw new IllegalArgumentException("List is immutable"); } @Override - public void add(MethodInfo e) { + public void add(final MethodInfo e) { throw new IllegalArgumentException("List is immutable"); } }; @@ -219,6 +195,32 @@ public static MethodInfoList emptyList() { return EMPTY_LIST; } + /** Construct a new modifiable empty list of {@link MethodInfo} objects. */ + public MethodInfoList() { + super(); + } + + /** + * Construct a new modifiable empty list of {@link MethodInfo} objects, given a size hint. + * + * @param sizeHint + * the size hint + */ + public MethodInfoList(final int sizeHint) { + super(sizeHint); + } + + /** + * Construct a new modifiable empty {@link MethodInfoList}, given an initial collection of {@link MethodInfo} + * objects. + * + * @param methodInfoCollection + * the collection of {@link MethodInfo} objects. + */ + public MethodInfoList(final Collection methodInfoCollection) { + super(methodInfoCollection); + } + // ------------------------------------------------------------------------------------------------------------- /** diff --git a/src/main/java/io/github/classgraph/ResourceList.java b/src/main/java/io/github/classgraph/ResourceList.java index 05680d050..24a72e02b 100644 --- a/src/main/java/io/github/classgraph/ResourceList.java +++ b/src/main/java/io/github/classgraph/ResourceList.java @@ -42,8 +42,8 @@ import java.util.List; import java.util.ListIterator; import java.util.Map; -import java.util.Spliterator; import java.util.Map.Entry; +import java.util.Spliterator; import java.util.function.Consumer; import nonapi.io.github.classgraph.utils.CollectionUtils; @@ -127,7 +127,7 @@ public Resource next() { public Spliterator spliterator() { return new Spliterator() { @Override - public boolean tryAdvance(Consumer action) { + public boolean tryAdvance(final Consumer action) { return false; } @@ -187,12 +187,12 @@ public void remove() { } @Override - public void set(Resource e) { + public void set(final Resource e) { throw new IllegalArgumentException("List is immutable"); } @Override - public void add(Resource e) { + public void add(final Resource e) { throw new IllegalArgumentException("List is immutable"); } }; @@ -209,30 +209,30 @@ public static ResourceList emptyList() { } /** - * Constructor. + * Create a new modifiable empty list of {@link Resource} objects. */ - ResourceList() { + public ResourceList() { super(); } /** - * Constructor. + * Create a new modifiable empty list of {@link Resource} objects, given a size hint. * * @param sizeHint * the size hint */ - ResourceList(final int sizeHint) { + public ResourceList(final int sizeHint) { super(sizeHint); } /** - * Constructor. + * Create a new modifiable empty {@link ResourceList}, given an initial collection of {@link Resource} objects. * - * @param collection - * the collection + * @param resourceCollection + * the collection of {@link Resource} objects. */ - ResourceList(final Collection collection) { - super(collection); + public ResourceList(final Collection resourceCollection) { + super(resourceCollection); } // ------------------------------------------------------------------------------------------------------------- From 4b14c256ecf8bed10a418ee92414cf3e2d5956ac Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 12 Jun 2019 03:56:04 -0600 Subject: [PATCH 0220/1778] Update Javadoc --- .../java/io/github/classgraph/ClassInfoList.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassInfoList.java b/src/main/java/io/github/classgraph/ClassInfoList.java index f311fff2b..ce4d4d983 100644 --- a/src/main/java/io/github/classgraph/ClassInfoList.java +++ b/src/main/java/io/github/classgraph/ClassInfoList.java @@ -49,13 +49,16 @@ import nonapi.io.github.classgraph.utils.CollectionUtils; /** - * A list of {@link ClassInfo} objects, which stores both reachable classes (obtained through a given class - * relationship, either by direct relationship or through an indirect path), and directly related classes (classes - * reachable through a direct relationship only). + * A uniquified (deduplicated) list of {@link ClassInfo} objects, which stores both reachable classes + * (obtained through a given class relationship, either by direct relationship or through an indirect path), and + * directly related classes (classes reachable through a direct relationship only). (By default, accessing a + * {@link ClassInfoList} as a {@link List} returns only reachable classes; by calling {@link #directOnly()}, you can + * get the directly related classes.) * *

- * By default, this list returns reachable classes. By calling {@link #directOnly()}, you can get the directly - * related classes. + * Most {@link ClassInfoList} objects returned by ClassGraph are sorted into lexicographical order by the value of + * {@link ClassInfo#getName()}. One exception to this is the classes returned by + * {@link ClassInfo#getSuperclasses()}, which are in ascending order of the class hierarchy. */ public class ClassInfoList extends MappableInfoList { /** Directly related classes. */ From d0e2097799926c0167774a69630883f2a5d54245 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 12 Jun 2019 03:57:12 -0600 Subject: [PATCH 0221/1778] [maven-release-plugin] prepare release classgraph-4.8.39 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 42c32f943..e86228b99 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.39-SNAPSHOT + 4.8.39 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.39 From 03dac449d9cd5a93c870f171055232e007cbebf4 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 12 Jun 2019 03:57:18 -0600 Subject: [PATCH 0222/1778] [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 e86228b99..5e987ca68 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.39 + 4.8.40-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.39 + HEAD From 21362a07520552f1a0c68124f93ae66841090fd0 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 13 Jun 2019 01:20:51 -0600 Subject: [PATCH 0223/1778] Additionally try null classloader, if environment classloaders fail --- .../java/io/github/classgraph/ClassGraphClassLoader.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java index 2efcc9c8d..a6cdad2a0 100644 --- a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java +++ b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java @@ -60,6 +60,7 @@ class ClassGraphClassLoader extends ClassLoader { @Override protected Class findClass(final String className) throws ClassNotFoundException, LinkageError, SecurityException { + // Use cached class, if it is already loaded final Class loadedClass = findLoadedClass(className); if (loadedClass != null) { return loadedClass; @@ -85,6 +86,13 @@ protected Class findClass(final String className) } } + // Try null classloader (ClassGraph's classloader) + try { + return Class.forName(className, scanResult.scanSpec.initializeLoadedClasses, null); + } catch (ClassNotFoundException | NoClassDefFoundError e) { + // Ignore + } + // Try specific classloader for the classpath element that the classfile was obtained from if (!triedClassInfoLoader && classInfo != null && classInfo.classLoader != null) { try { From a15d05935cf3f1d94655e38836af1bbac9baccc4 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 13 Jun 2019 01:21:37 -0600 Subject: [PATCH 0224/1778] Update comment --- src/main/java/io/github/classgraph/ClassGraphClassLoader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java index a6cdad2a0..963ace2d0 100644 --- a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java +++ b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java @@ -86,7 +86,7 @@ protected Class findClass(final String className) } } - // Try null classloader (ClassGraph's classloader) + // Try null classloader (the classloader that loaded this class, as the caller of Class.forName()) try { return Class.forName(className, scanResult.scanSpec.initializeLoadedClasses, null); } catch (ClassNotFoundException | NoClassDefFoundError e) { From 67f7bdc508c66935ba04954884d2563c3cd11d4e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 13 Jun 2019 22:59:15 -0600 Subject: [PATCH 0225/1778] Fix warning message --- 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 ea12bb1ee..33cdedf94 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -133,7 +133,8 @@ class Scanner implements Callable { scanSpec.sortPrefixes(); scanSpec.log(topLevelLog); if (topLevelLog != null) { - if (scanSpec.pathWhiteBlackList != null && scanSpec.pathWhiteBlackList.isSpecificallyWhitelisted("")) { + if (scanSpec.pathWhiteBlackList != null + && scanSpec.packagePrefixWhiteBlackList.isSpecificallyWhitelisted("")) { topLevelLog.log("Note: There is no need to whitelist the root package (\"\") -- not whitelisting " + "anything will have the same effect of causing all packages to be scanned"); } From 4950a18f753b8085dfd7244c009318029762e16b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 13 Jun 2019 23:06:02 -0600 Subject: [PATCH 0226/1778] Fix warning --- .../io/github/classgraph/ClasspathElementZip.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index 1ab78e395..62bd394d0 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -56,6 +56,7 @@ import nonapi.io.github.classgraph.utils.JarUtils; import nonapi.io.github.classgraph.utils.LogNode; import nonapi.io.github.classgraph.utils.URLPathEncoder; +import nonapi.io.github.classgraph.utils.VersionFinder; /** A zip/jarfile classpath element. */ class ClasspathElementZip extends ClasspathElement { @@ -420,8 +421,14 @@ void scanPaths(final LogNode log) { // spurious files in a multi-version path (in which case, they should be ignored). if (relativePath.startsWith(LogicalZipFile.MULTI_RELEASE_PATH_PREFIX)) { if (subLog != null) { - subLog.log("Found unexpected versioned entry in jar (the jar's manifest file may be missing " - + "the \"Multi-Release\" key) -- skipping: " + relativePath); + if (VersionFinder.JAVA_MAJOR_VERSION < 9) { + subLog.log("Skipping versioned entry in jar, because JRE version " + + VersionFinder.JAVA_MAJOR_VERSION + " does not support this: " + relativePath); + } else { + subLog.log( + "Found unexpected versioned entry in jar (the jar's manifest file may be missing " + + "the \"Multi-Release\" key) -- skipping: " + relativePath); + } } continue; } From dc2035188d2082324e5bda8f8037a91cd8545465 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 13 Jun 2019 23:12:51 -0600 Subject: [PATCH 0227/1778] Add unit test for #352 --- .../classgraph/issues/issue352/Issue352.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 src/test/java/io/github/classgraph/issues/issue352/Issue352.java diff --git a/src/test/java/io/github/classgraph/issues/issue352/Issue352.java b/src/test/java/io/github/classgraph/issues/issue352/Issue352.java new file mode 100644 index 000000000..a340f81c8 --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue352/Issue352.java @@ -0,0 +1,32 @@ +package io.github.classgraph.issues.issue352; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.File; +import java.io.IOException; +import java.util.List; + +import org.junit.Test; +import org.ops4j.pax.url.mvn.MavenResolvers; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ScanResult; + +/** + * Unit test. + */ +public class Issue352 { + /** Test that module-info.class is not included in results. */ + @Test + public void test() throws IOException { + final File resolvedFile = MavenResolvers.createMavenResolver(null, null).resolve("com.sun.istack", + "istack-commons-runtime", null, null, "3.0.7"); + assertThat(resolvedFile).isFile(); + + try (ScanResult scanResult = new ClassGraph().overrideClasspath(resolvedFile) + .whitelistPackagesNonRecursive("").enableClassInfo().scan()) { + final List classNames = scanResult.getAllClasses().getNames(); + assertThat(classNames).doesNotContain("module-info"); + } + } +} From 59b85b98402ea0333894f238e6e7b4f3a8d79534 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 14 Jun 2019 05:35:13 -0600 Subject: [PATCH 0228/1778] Don't add module-info.class resource if root pkg not whitelisted (#352) --- .../github/classgraph/ClasspathElement.java | 11 +++++--- .../classgraph/ClasspathElementDir.java | 4 +-- .../classgraph/ClasspathElementModule.java | 26 ++++++++++++------- .../classgraph/ClasspathElementZip.java | 23 +++++++++------- .../classgraph/issues/issue352/Issue352.java | 8 +++--- 5 files changed, 43 insertions(+), 29 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElement.java b/src/main/java/io/github/classgraph/ClasspathElement.java index 828bb3043..07bceec28 100644 --- a/src/main/java/io/github/classgraph/ClasspathElement.java +++ b/src/main/java/io/github/classgraph/ClasspathElement.java @@ -235,11 +235,14 @@ void maskClassfiles(final int classpathIdx, final Set classpathRelativeP * the resource * @param parentMatchStatus * the parent match status + * @param isClassfileOnly + * if true, only add the resource to the list of classfile resources, not to the list of + * non-classfile resources * @param log * the log */ protected void addWhitelistedResource(final Resource resource, final ScanSpecPathMatch parentMatchStatus, - final LogNode log) { + final boolean isClassfileOnly, final LogNode log) { final String path = resource.getPath(); final boolean isClassFile = FileUtils.isClassfile(path); boolean isWhitelisted = false; @@ -255,8 +258,10 @@ protected void addWhitelistedResource(final Resource resource, final ScanSpecPat isWhitelisted = true; } - // Add resource to whitelistedResources, whether for a classfile or non-classfile resource - whitelistedResources.add(resource); + if (!isClassfileOnly) { + // Add resource to whitelistedResources, whether for a classfile or non-classfile resource + whitelistedResources.add(resource); + } // Write to log if enabled, and as long as classfile scanning is not disabled, and this is not // a blacklisted classfile diff --git a/src/main/java/io/github/classgraph/ClasspathElementDir.java b/src/main/java/io/github/classgraph/ClasspathElementDir.java index e71f68e6a..b6786b366 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementDir.java @@ -396,7 +396,7 @@ private void scanDirRecursively(final File dir, final LogNode log) { && scanSpec.classfileIsSpecificallyWhitelisted(fileInDirRelativePath))) { // Resource is whitelisted final Resource resource = newResource(fileInDirRelativePath, fileInDir); - addWhitelistedResource(resource, parentMatchStatus, subLog); + addWhitelistedResource(resource, parentMatchStatus, /* isClassfileOnly = */ false, subLog); // Save last modified time fileToLastModified.put(fileInDir, fileInDir.lastModified()); @@ -412,7 +412,7 @@ private void scanDirRecursively(final File dir, final LogNode log) { for (final File fileInDir : filesInDir) { if (fileInDir.getName().equals("module-info.class") && fileInDir.isFile()) { final Resource resource = newResource("module-info.class", fileInDir); - addWhitelistedResource(resource, parentMatchStatus, subLog); + addWhitelistedResource(resource, parentMatchStatus, /* isClassfileOnly = */ true, subLog); fileToLastModified.put(fileInDir, fileInDir.lastModified()); } } diff --git a/src/main/java/io/github/classgraph/ClasspathElementModule.java b/src/main/java/io/github/classgraph/ClasspathElementModule.java index 42217f6fc..08c0e236e 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementModule.java +++ b/src/main/java/io/github/classgraph/ClasspathElementModule.java @@ -316,16 +316,22 @@ void scanPaths(final LogNode log) { } // Found non-blacklisted relative path - if (allResourcePaths.add(relativePath) - // If resource is whitelisted - && (parentMatchStatus == ScanSpecPathMatch.HAS_WHITELISTED_PATH_PREFIX - || parentMatchStatus == ScanSpecPathMatch.AT_WHITELISTED_PATH - || (parentMatchStatus == ScanSpecPathMatch.AT_WHITELISTED_CLASS_PACKAGE - && scanSpec.classfileIsSpecificallyWhitelisted(relativePath)) - || (scanSpec.enableClassInfo && relativePath.equals("module-info.class")))) { - // Add whitelisted resource - final Resource resource = newResource(relativePath); - addWhitelistedResource(resource, parentMatchStatus, subLog); + if (allResourcePaths.add(relativePath)) { + // If resource is whitelisted + if (parentMatchStatus == ScanSpecPathMatch.HAS_WHITELISTED_PATH_PREFIX + || parentMatchStatus == ScanSpecPathMatch.AT_WHITELISTED_PATH + || (parentMatchStatus == ScanSpecPathMatch.AT_WHITELISTED_CLASS_PACKAGE + && scanSpec.classfileIsSpecificallyWhitelisted(relativePath))) { + // Add whitelisted resource + addWhitelistedResource(newResource(relativePath), parentMatchStatus, + /* isClassfileOnly = */ false, subLog); + } else if (scanSpec.enableClassInfo && relativePath.equals("module-info.class")) { + // Add module descriptor as a whitelisted classfile resource, so that it is scanned, + // but don't add it to the list of resources in the ScanResult, since it is not + // in a whitelisted package (#352) + addWhitelistedResource(newResource(relativePath), parentMatchStatus, + /* isClassfileOnly = */ true, subLog); + } } } diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index 62bd394d0..cb075068a 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -504,15 +504,20 @@ void scanPaths(final LogNode log) { // Add the ZipEntry path as a Resource final Resource resource = newResource(zipEntry, relativePath); - if (relativePathToResource.putIfAbsent(relativePath, resource) == null - // If resource is whitelisted - && (parentMatchStatus == ScanSpecPathMatch.HAS_WHITELISTED_PATH_PREFIX - || parentMatchStatus == ScanSpecPathMatch.AT_WHITELISTED_PATH - || (parentMatchStatus == ScanSpecPathMatch.AT_WHITELISTED_CLASS_PACKAGE - && scanSpec.classfileIsSpecificallyWhitelisted(relativePath)) - || (scanSpec.enableClassInfo && relativePath.equals("module-info.class")))) { - // Resource is whitelisted - addWhitelistedResource(resource, parentMatchStatus, subLog); + if (relativePathToResource.putIfAbsent(relativePath, resource) == null) { + // If resource is whitelisted + if (parentMatchStatus == ScanSpecPathMatch.HAS_WHITELISTED_PATH_PREFIX + || parentMatchStatus == ScanSpecPathMatch.AT_WHITELISTED_PATH + || (parentMatchStatus == ScanSpecPathMatch.AT_WHITELISTED_CLASS_PACKAGE + && scanSpec.classfileIsSpecificallyWhitelisted(relativePath))) { + // Resource is whitelisted + addWhitelistedResource(resource, parentMatchStatus, /* isClassfileOnly = */ false, subLog); + } else if (scanSpec.enableClassInfo && relativePath.equals("module-info.class")) { + // Add module descriptor as a whitelisted classfile resource, so that it is scanned, + // but don't add it to the list of resources in the ScanResult, since it is not + // in a whitelisted package (#352) + addWhitelistedResource(resource, parentMatchStatus, /* isClassfileOnly = */ true, subLog); + } } } diff --git a/src/test/java/io/github/classgraph/issues/issue352/Issue352.java b/src/test/java/io/github/classgraph/issues/issue352/Issue352.java index a340f81c8..0275f9646 100644 --- a/src/test/java/io/github/classgraph/issues/issue352/Issue352.java +++ b/src/test/java/io/github/classgraph/issues/issue352/Issue352.java @@ -4,7 +4,6 @@ import java.io.File; import java.io.IOException; -import java.util.List; import org.junit.Test; import org.ops4j.pax.url.mvn.MavenResolvers; @@ -16,7 +15,7 @@ * Unit test. */ public class Issue352 { - /** Test that module-info.class is not included in results. */ + /** Test that module-info.class is not included in results if the root package ("") is not whitelisted. */ @Test public void test() throws IOException { final File resolvedFile = MavenResolvers.createMavenResolver(null, null).resolve("com.sun.istack", @@ -24,9 +23,8 @@ public void test() throws IOException { assertThat(resolvedFile).isFile(); try (ScanResult scanResult = new ClassGraph().overrideClasspath(resolvedFile) - .whitelistPackagesNonRecursive("").enableClassInfo().scan()) { - final List classNames = scanResult.getAllClasses().getNames(); - assertThat(classNames).doesNotContain("module-info"); + .whitelistPackages("com.sun.istack").enableClassInfo().scan()) { + assertThat(scanResult.getAllResources().getPaths()).doesNotContain("module-info.class"); } } } From a0776d60be48048dda91714b82ddc00dc1c9547a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 14 Jun 2019 06:49:25 -0600 Subject: [PATCH 0229/1778] Improve unit test --- .../classgraph/issues/issue352/Issue352.java | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/test/java/io/github/classgraph/issues/issue352/Issue352.java b/src/test/java/io/github/classgraph/issues/issue352/Issue352.java index 0275f9646..591acd57b 100644 --- a/src/test/java/io/github/classgraph/issues/issue352/Issue352.java +++ b/src/test/java/io/github/classgraph/issues/issue352/Issue352.java @@ -10,21 +10,39 @@ import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; +import io.github.classgraph.issues.issue107.Issue107Test; /** * Unit test. */ public class Issue352 { - /** Test that module-info.class is not included in results if the root package ("") is not whitelisted. */ + /** Test **/ @Test public void test() throws IOException { final File resolvedFile = MavenResolvers.createMavenResolver(null, null).resolve("com.sun.istack", "istack-commons-runtime", null, null, "3.0.7"); assertThat(resolvedFile).isFile(); + // Test that module-info.class is not included in resource list if the root package ("") is not whitelisted + try (ScanResult scanResult = new ClassGraph().overrideClasspath(resolvedFile) + .whitelistPackagesNonRecursive("").enableClassInfo().scan()) { + assertThat(scanResult.getAllResources().getPaths()).contains("module-info.class"); + } try (ScanResult scanResult = new ClassGraph().overrideClasspath(resolvedFile) .whitelistPackages("com.sun.istack").enableClassInfo().scan()) { assertThat(scanResult.getAllResources().getPaths()).doesNotContain("module-info.class"); } + + // Test that package-info.class is only included in resource list for whitelisted packages + final String pkgInfoPath = Issue107Test.class.getPackage().getName().replace('.', '/') + + "/package-info.class"; + try (ScanResult scanResult = new ClassGraph().whitelistPackages(Issue107Test.class.getPackage().getName()) + .enableClassInfo().scan()) { + assertThat(scanResult.getAllResources().getPaths()).contains(pkgInfoPath); + } + try (ScanResult scanResult = new ClassGraph().whitelistPackages(Issue352.class.getPackage().getName()) + .enableClassInfo().scan()) { + assertThat(scanResult.getAllResources().getPaths()).doesNotContain(pkgInfoPath); + } } } From ed5154ad7342705dd79cafb12b42c37dc66bfa01 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 14 Jun 2019 06:50:19 -0600 Subject: [PATCH 0230/1778] [maven-release-plugin] prepare release classgraph-4.8.40 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 5e987ca68..d5080f6b6 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.40-SNAPSHOT + 4.8.40 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.40 From 9d928f35d2c8a2ab9470482c5d8160d32685893a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 14 Jun 2019 06:50:25 -0600 Subject: [PATCH 0231/1778] [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 d5080f6b6..478379826 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.40 + 4.8.41-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.40 + HEAD From 274eecaa61a432fbc6a0bf02aec556cbe5c0d456 Mon Sep 17 00:00:00 2001 From: Anatoly Korniltsev Date: Sat, 15 Jun 2019 19:09:00 +0300 Subject: [PATCH 0232/1778] Json parse fail test --- .../classgraph/json/JSONParserTest.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/test/java/nonapi/io/github/classgraph/json/JSONParserTest.java diff --git a/src/test/java/nonapi/io/github/classgraph/json/JSONParserTest.java b/src/test/java/nonapi/io/github/classgraph/json/JSONParserTest.java new file mode 100644 index 000000000..6fa559135 --- /dev/null +++ b/src/test/java/nonapi/io/github/classgraph/json/JSONParserTest.java @@ -0,0 +1,22 @@ +package nonapi.io.github.classgraph.json; + +import nonapi.io.github.classgraph.types.ParseException; +import org.junit.Test; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + +import static org.junit.Assert.*; + +public class JSONParserTest { + @Test + public void test1() throws ParseException { + JSONParser.parseJSON("{\"doubleValue\":-2.147483648}"); + } + + @Test + public void test2() throws ParseException { + JSONParser.parseJSON("{\"doubleValue\":-2.147483648E9}"); + } + +} \ No newline at end of file From d8326131b1b4b44a5bce19d6c801033e8f8e977d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 15 Jun 2019 11:54:44 -0600 Subject: [PATCH 0233/1778] Fix parsing of double exponents in JSON (#353) --- .../nonapi/io/github/classgraph/json/JSONParser.java | 2 +- .../io/github/classgraph/json/JSONParserTest.java | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) 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 9e7ed8038..b4ba08cea 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/JSONParser.java +++ b/src/main/java/nonapi/io/github/classgraph/json/JSONParser.java @@ -248,7 +248,7 @@ private Number parseNumber() throws ParseException { throw new ParseException(this, "Expected digits after decimal point"); } } - final boolean hasExponentPart = peek() == '.'; + final boolean hasExponentPart = peek() == 'e' || peek() == 'E'; if (hasExponentPart) { next(); final char sign = peek(); diff --git a/src/test/java/nonapi/io/github/classgraph/json/JSONParserTest.java b/src/test/java/nonapi/io/github/classgraph/json/JSONParserTest.java index 6fa559135..8c6075036 100644 --- a/src/test/java/nonapi/io/github/classgraph/json/JSONParserTest.java +++ b/src/test/java/nonapi/io/github/classgraph/json/JSONParserTest.java @@ -1,19 +1,20 @@ package nonapi.io.github.classgraph.json; -import nonapi.io.github.classgraph.types.ParseException; import org.junit.Test; -import java.lang.reflect.Constructor; -import java.lang.reflect.Method; - -import static org.junit.Assert.*; +import nonapi.io.github.classgraph.types.ParseException; +/** + * Unit test. + */ public class JSONParserTest { + /** Test double value. */ @Test public void test1() throws ParseException { JSONParser.parseJSON("{\"doubleValue\":-2.147483648}"); } + /** Test double value with exponent. */ @Test public void test2() throws ParseException { JSONParser.parseJSON("{\"doubleValue\":-2.147483648E9}"); From 78ecea48f1c6d2b5a72228f755b8c039b8932bdf Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 15 Jun 2019 11:55:29 -0600 Subject: [PATCH 0234/1778] [maven-release-plugin] prepare release classgraph-4.8.41 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 478379826..da68dd644 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.41-SNAPSHOT + 4.8.41 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.41 From 0cc4f09fbafffa42864765ded4a498ef81be7ff1 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 15 Jun 2019 11:55:35 -0600 Subject: [PATCH 0235/1778] [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 da68dd644..c69f37e88 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.41 + 4.8.42-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.41 + HEAD From 633d98924d47027b90a37876a9d85b25a6ea8e4a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 27 Jun 2019 12:50:39 -0600 Subject: [PATCH 0236/1778] Support array class references (#355) --- .../io/github/classgraph/AnnotationClassRef.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/github/classgraph/AnnotationClassRef.java b/src/main/java/io/github/classgraph/AnnotationClassRef.java index 15a683d12..562405cda 100644 --- a/src/main/java/io/github/classgraph/AnnotationClassRef.java +++ b/src/main/java/io/github/classgraph/AnnotationClassRef.java @@ -77,14 +77,14 @@ public String getName() { /** * Get the type signature. * - * @return The type signature of the {@code Class} reference. This will be a {@link ClassRefTypeSignature} or - * a {@link BaseTypeSignature}. + * @return The type signature of the {@code Class} reference. This will be a {@link ClassRefTypeSignature}, a + * {@link BaseTypeSignature}, or an {@link ArrayTypeSignature}. */ private TypeSignature getTypeSignature() { if (typeSignature == null) { try { - // There can't be any type variables to resolve in either ClassRefTypeSignature or - // BaseTypeSignature, so just set definingClassName to null + // There can't be any type variables to resolve in ClassRefTypeSignature, + // BaseTypeSignature or ArrayTypeSignature, so just set definingClassName to null typeSignature = TypeSignature.parse(typeDescriptorStr, /* definingClassName = */ null); typeSignature.setScanResult(scanResult); } catch (final ParseException e) { @@ -110,6 +110,8 @@ public Class loadClass(final boolean ignoreExceptions) { return ((BaseTypeSignature) typeSignature).getType(); } else if (typeSignature instanceof ClassRefTypeSignature) { return ((ClassRefTypeSignature) typeSignature).loadClass(ignoreExceptions); + } else if (typeSignature instanceof ArrayTypeSignature) { + return ((ArrayTypeSignature) typeSignature).loadClass(ignoreExceptions); } else { throw new IllegalArgumentException("Got unexpected type " + typeSignature.getClass().getName() + " for ref type signature: " + typeDescriptorStr); @@ -138,9 +140,11 @@ protected String getClassName() { if (className == null) { getTypeSignature(); if (typeSignature instanceof BaseTypeSignature) { - className = ((BaseTypeSignature) typeSignature).getType().getName(); + className = ((BaseTypeSignature) typeSignature).getTypeStr(); } else if (typeSignature instanceof ClassRefTypeSignature) { className = ((ClassRefTypeSignature) typeSignature).getFullyQualifiedClassName(); + } else if (typeSignature instanceof ArrayTypeSignature) { + className = ((ArrayTypeSignature) typeSignature).getClassName(); } else { throw new IllegalArgumentException("Got unexpected type " + typeSignature.getClass().getName() + " for ref type signature: " + typeDescriptorStr); From 8d59d0a83f88b7b5e813bd90f0bec96c35c8618e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 27 Jun 2019 12:51:20 -0600 Subject: [PATCH 0237/1778] [maven-release-plugin] prepare release classgraph-4.8.42 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index c69f37e88..90a529b7f 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.42-SNAPSHOT + 4.8.42 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.42 From 0488e8f9f2a40a5b7dbadc04074d680f1e8da817 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 27 Jun 2019 12:53:38 -0600 Subject: [PATCH 0238/1778] [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 90a529b7f..0b5b3a059 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.42 + 4.8.43-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.42 + HEAD From 45d962bd5bf91556978c0c7c1d6d9f647f980b61 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 27 Jun 2019 15:36:27 -0600 Subject: [PATCH 0239/1778] Improve logging when unexpected exception is thrown (#355) --- .../java/io/github/classgraph/Scanner.java | 106 +++++++----------- 1 file changed, 41 insertions(+), 65 deletions(-) diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 33cdedf94..b99c54d88 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -109,7 +109,7 @@ class Scanner implements Callable { // ------------------------------------------------------------------------------------------------------------- /** - * The classpath scanner. + * The classpath scanner. Scanning is started by calling {@link #call()} on this object. * * @param scanSpec * the scan spec @@ -998,8 +998,8 @@ public void processWorkUnit(final ClasspathElement classpathElement, @Override public ScanResult call() throws InterruptedException, CancellationException, ExecutionException { ScanResult scanResult = null; - Exception exception = null; final long scanStart = System.currentTimeMillis(); + boolean removeTemporaryFilesAfterScan = scanSpec.removeTemporaryFilesAfterScan; try { // Perform the scan scanResult = openClasspathElementsThenScan(); @@ -1020,80 +1020,56 @@ public ScanResult call() throws InterruptedException, CancellationException, Exe } } - } catch (final InterruptedException e) { + } catch (final InterruptedException | ExecutionException | RuntimeException e) { if (topLevelLog != null) { - topLevelLog.log("~", "Scan interrupted"); + topLevelLog.log("~", + e instanceof InterruptedException || e instanceof CancellationException + ? "Scan interrupted or canceled" + : e instanceof ExecutionException || e instanceof RuntimeException + ? "Uncaught exception during scan" + : e.getMessage(), + InterruptionChecker.getCause(e)); + // Flush the log + topLevelLog.flush(); } - exception = e; + + // Since an exception was thrown, remove temporary files + removeTemporaryFilesAfterScan = true; + + // Stop any running threads (should not be needed, threads should already be quiescent) interruptionChecker.interrupt(); + if (failureHandler == null) { - // Re-throw - throw e; - } - } catch (final CancellationException e) { - if (topLevelLog != null) { - topLevelLog.log("~", "Scan cancelled"); - } - exception = e; - if (failureHandler == null) { - // Re-throw - throw e; - } - } catch (final ExecutionException e) { - if (topLevelLog != null) { - topLevelLog.log("~", "Uncaught exception during scan", InterruptionChecker.getCause(e)); - } - exception = e; - if (failureHandler == null) { - // Re-throw + // If there is no failure handler set, re-throw the exception throw e; - } - } catch (final RuntimeException e) { - if (topLevelLog != null) { - topLevelLog.log("~", "Uncaught exception during scan", e); - } - exception = e; - if (failureHandler == null) { - // Wrap unchecked exceptions in a new ExecutionException - throw new ExecutionException("Exception while scanning", e); + } else { + // Otherwise, call the failure handler + try { + failureHandler.onFailure(e); + } catch (final Exception f) { + // The failure handler failed + if (topLevelLog != null) { + topLevelLog.log("~", "The failure handler threw an exception:", f); + topLevelLog.flush(); + } + // Group the two exceptions into one, using the suppressed exception mechanism + // to show the scan exception below the failure handler exception + final ExecutionException failureHandlerException = new ExecutionException( + "Exception while calling failure handler", f); + failureHandlerException.addSuppressed(e); + // 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 (exception != null || scanSpec.removeTemporaryFilesAfterScan) { - // If an exception was thrown or removeTemporaryFilesAfterScan was set, remove temporary files - // and close resources, zipfiles, and modules + if (removeTemporaryFilesAfterScan) { + // If removeTemporaryFilesAfterScan was set, remove temp files and close resources, zipfiles and modules nestedJarHandler.close(topLevelLog); } } - - if (exception != null) { - // If an exception was thrown, log the cause, and flush the toplevel log - if (topLevelLog != null) { - final Throwable cause = InterruptionChecker.getCause(exception); - topLevelLog.log("~", "An uncaught exception was thrown:", cause); - topLevelLog.flush(); - } - - // If exception is null, then failureHandler must be non-null at this point - try { - // Call the FailureHandler - failureHandler.onFailure(exception); - } catch (final Exception f) { - // The failure handler failed - if (topLevelLog != null) { - topLevelLog.log("~", "The failure handler threw an exception:", f); - } - // Group the two exceptions into one, using the suppressed exception mechanism - // to show the scan exception below the failure handler exception - final ExecutionException failureHandlerException = new ExecutionException( - "Exception while calling failure handler", f); - failureHandlerException.addSuppressed(exception); - // 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; - } - } return scanResult; } } From 1f1387af5d35478e04bef2d0b71dd3d23bbbaf9d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 27 Jun 2019 15:50:27 -0600 Subject: [PATCH 0240/1778] Improve error handling --- 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 b99c54d88..6f25c8f1f 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -1020,7 +1020,7 @@ public ScanResult call() throws InterruptedException, CancellationException, Exe } } - } catch (final InterruptedException | ExecutionException | RuntimeException e) { + } catch (final Throwable e) { if (topLevelLog != null) { topLevelLog.log("~", e instanceof InterruptedException || e instanceof CancellationException From 2347c4cbdf9b2d84eb0c7e703a6ce5fce9f1775b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 27 Jun 2019 23:23:57 -0600 Subject: [PATCH 0241/1778] Improve handling of array class references (#355) --- .../github/classgraph/AnnotationClassRef.java | 14 +-- .../classgraph/AnnotationEnumValue.java | 9 -- .../io/github/classgraph/AnnotationInfo.java | 32 +++--- .../github/classgraph/AnnotationInfoList.java | 14 ++- .../classgraph/AnnotationParameterValue.java | 14 ++- .../AnnotationParameterValueList.java | 14 ++- .../io/github/classgraph/ArrayClassInfo.java | 30 +++-- .../github/classgraph/ArrayTypeSignature.java | 72 +++++++++++- .../github/classgraph/BaseTypeSignature.java | 103 +++++++++++++++--- .../java/io/github/classgraph/ClassInfo.java | 86 +++++++++------ .../classgraph/ClassRefTypeSignature.java | 14 ++- .../github/classgraph/ClassTypeSignature.java | 38 +++++-- .../java/io/github/classgraph/Classfile.java | 9 +- .../java/io/github/classgraph/FieldInfo.java | 18 +-- .../io/github/classgraph/FieldInfoList.java | 14 ++- .../classgraph/GraphvizDotfileGenerator.java | 12 +- .../classgraph/HierarchicalTypeSignature.java | 10 -- .../java/io/github/classgraph/MethodInfo.java | 20 ++-- .../io/github/classgraph/MethodInfoList.java | 14 ++- .../classgraph/MethodTypeSignature.java | 42 +++++-- .../classgraph/ObjectTypedValueWrapper.java | 23 ++-- .../java/io/github/classgraph/ScanResult.java | 22 ++-- .../github/classgraph/ScanResultObject.java | 33 ++++-- .../java/io/github/classgraph/Scanner.java | 6 +- .../io/github/classgraph/TypeArgument.java | 12 +- .../io/github/classgraph/TypeParameter.java | 14 ++- .../io/github/classgraph/TypeSignature.java | 37 +++++++ .../classgraph/TypeVariableSignature.java | 9 +- .../classgraph/issues/issue355/Issue355.java | 76 +++++++++++++ 29 files changed, 583 insertions(+), 228 deletions(-) create mode 100644 src/test/java/io/github/classgraph/issues/issue355/Issue355.java diff --git a/src/main/java/io/github/classgraph/AnnotationClassRef.java b/src/main/java/io/github/classgraph/AnnotationClassRef.java index 562405cda..0c899a58f 100644 --- a/src/main/java/io/github/classgraph/AnnotationClassRef.java +++ b/src/main/java/io/github/classgraph/AnnotationClassRef.java @@ -28,8 +28,6 @@ */ package io.github.classgraph; -import java.util.Set; - import nonapi.io.github.classgraph.types.ParseException; /** @@ -163,8 +161,8 @@ protected String getClassName() { */ @Override public ClassInfo getClassInfo() { - getClassName(); - return super.getClassInfo(); + getTypeSignature(); + return typeSignature.getClassInfo(); } /* (non-Javadoc) @@ -178,14 +176,6 @@ void setScanResult(final ScanResult scanResult) { } } - /* (non-Javadoc) - * @see io.github.classgraph.ScanResultObject#findReferencedClassNames(java.util.Set) - */ - @Override - protected void findReferencedClassNames(final Set classNames) { - classNames.add(getClassName()); - } - // ------------------------------------------------------------------------------------------------------------- /* (non-Javadoc) diff --git a/src/main/java/io/github/classgraph/AnnotationEnumValue.java b/src/main/java/io/github/classgraph/AnnotationEnumValue.java index 90c6924c1..b68f23792 100644 --- a/src/main/java/io/github/classgraph/AnnotationEnumValue.java +++ b/src/main/java/io/github/classgraph/AnnotationEnumValue.java @@ -29,7 +29,6 @@ package io.github.classgraph; import java.lang.reflect.Field; -import java.util.Set; /** * Class for wrapping an enum constant value (split into class name and constant name), as used as an annotation @@ -145,14 +144,6 @@ public Object loadClassAndReturnEnumValue() throws IllegalArgumentException { // ------------------------------------------------------------------------------------------------------------- - /* (non-Javadoc) - * @see io.github.classgraph.ScanResultObject#findReferencedClassNames(java.util.Set) - */ - @Override - void findReferencedClassNames(final Set referencedClassNames) { - referencedClassNames.add(className); - } - /* (non-Javadoc) * @see java.lang.Comparable#compareTo(java.lang.Object) */ diff --git a/src/main/java/io/github/classgraph/AnnotationInfo.java b/src/main/java/io/github/classgraph/AnnotationInfo.java index 9e0a8cc7e..444ee665d 100644 --- a/src/main/java/io/github/classgraph/AnnotationInfo.java +++ b/src/main/java/io/github/classgraph/AnnotationInfo.java @@ -204,17 +204,6 @@ protected String getClassName() { return name; } - /** - * Get the class info. - * - * @return The {@link ClassInfo} object for the annotation class. - */ - @Override - public ClassInfo getClassInfo() { - getClassName(); - return super.getClassInfo(); - } - /* (non-Javadoc) * @see io.github.classgraph.ScanResultObject#setScanResult(io.github.classgraph.ScanResult) */ @@ -229,23 +218,32 @@ void setScanResult(final ScanResult scanResult) { } /** - * Find the names of any classes referenced in the type descriptors of annotation parameters. + * Get {@link ClassInfo} objects for any classes referenced in the type descriptor or type signature. * - * @param referencedClassNames - * the referenced class names + * @param classNameToClassInfo + * the map from class name to {@link ClassInfo}. + * @param refdClassInfo + * the referenced class info */ @Override - void findReferencedClassNames(final Set referencedClassNames) { - referencedClassNames.add(name); + protected void findReferencedClassInfo(final Map classNameToClassInfo, + final Set refdClassInfo) { + super.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); if (annotationParamValues != null) { for (final AnnotationParameterValue annotationParamValue : annotationParamValues) { - annotationParamValue.findReferencedClassNames(referencedClassNames); + annotationParamValue.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); } } } // ------------------------------------------------------------------------------------------------------------- + /** Return the {@link ClassInfo} object for the annotation class. */ + @Override + public ClassInfo getClassInfo() { + return super.getClassInfo(); + } + /** * Load the {@link Annotation} class corresponding to this {@link AnnotationInfo} object, by calling * {@code getClassInfo().loadClass()}, then create a new instance of the annotation, with the annotation diff --git a/src/main/java/io/github/classgraph/AnnotationInfoList.java b/src/main/java/io/github/classgraph/AnnotationInfoList.java index be280504b..07cd81206 100644 --- a/src/main/java/io/github/classgraph/AnnotationInfoList.java +++ b/src/main/java/io/github/classgraph/AnnotationInfoList.java @@ -35,6 +35,7 @@ import java.util.Iterator; import java.util.List; import java.util.ListIterator; +import java.util.Map; import java.util.Set; import java.util.Spliterator; import java.util.function.Consumer; @@ -290,14 +291,17 @@ public AnnotationInfoList filter(final AnnotationInfoFilter filter) { // ------------------------------------------------------------------------------------------------------------- /** - * Find the names of any classes referenced in the annotations in this list or their parameters. + * Get {@link ClassInfo} objects for any classes referenced in this list. * - * @param referencedClassNames - * the referenced class names + * @param classNameToClassInfo + * the map from class name to {@link ClassInfo}. + * @param refdClassInfo + * the referenced class info */ - void findReferencedClassNames(final Set referencedClassNames) { + protected void findReferencedClassInfo(final Map classNameToClassInfo, + final Set refdClassInfo) { for (final AnnotationInfo ai : this) { - ai.findReferencedClassNames(referencedClassNames); + ai.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); } } diff --git a/src/main/java/io/github/classgraph/AnnotationParameterValue.java b/src/main/java/io/github/classgraph/AnnotationParameterValue.java index b0ea73b7b..411c26f1d 100644 --- a/src/main/java/io/github/classgraph/AnnotationParameterValue.java +++ b/src/main/java/io/github/classgraph/AnnotationParameterValue.java @@ -29,6 +29,7 @@ package io.github.classgraph; import java.lang.reflect.Array; +import java.util.Map; import java.util.Objects; import java.util.Set; @@ -135,15 +136,18 @@ void setScanResult(final ScanResult scanResult) { } /** - * Get the names of any classes referenced in the annotation parameters. + * Get {@link ClassInfo} objects for any classes referenced in the annotation parameters. * - * @param referencedClassNames - * the referenced class names + * @param classNameToClassInfo + * the map from class name to {@link ClassInfo}. + * @param refdClassInfo + * the referenced class info */ @Override - void findReferencedClassNames(final Set referencedClassNames) { + protected void findReferencedClassInfo(final Map classNameToClassInfo, + final Set refdClassInfo) { if (value != null) { - value.findReferencedClassNames(referencedClassNames); + value.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); } } diff --git a/src/main/java/io/github/classgraph/AnnotationParameterValueList.java b/src/main/java/io/github/classgraph/AnnotationParameterValueList.java index 2c9ab8a2d..7168492f6 100644 --- a/src/main/java/io/github/classgraph/AnnotationParameterValueList.java +++ b/src/main/java/io/github/classgraph/AnnotationParameterValueList.java @@ -31,6 +31,7 @@ import java.util.Collection; import java.util.Iterator; import java.util.ListIterator; +import java.util.Map; import java.util.Set; import java.util.Spliterator; import java.util.function.Consumer; @@ -225,14 +226,17 @@ public AnnotationParameterValueList( // ------------------------------------------------------------------------------------------------------------- /** - * Find the names of any classes referenced in the methods in this list. + * Get {@link ClassInfo} objects for any classes referenced in the methods in this list. * - * @param referencedClassNames - * the referenced class names + * @param classNameToClassInfo + * the map from class name to {@link ClassInfo}. + * @param refdClassInfo + * the referenced class info */ - void findReferencedClassNames(final Set referencedClassNames) { + protected void findReferencedClassInfo(final Map classNameToClassInfo, + final Set refdClassInfo) { for (final AnnotationParameterValue apv : this) { - apv.findReferencedClassNames(referencedClassNames); + apv.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); } } diff --git a/src/main/java/io/github/classgraph/ArrayClassInfo.java b/src/main/java/io/github/classgraph/ArrayClassInfo.java index 9cf1325f0..408353bba 100644 --- a/src/main/java/io/github/classgraph/ArrayClassInfo.java +++ b/src/main/java/io/github/classgraph/ArrayClassInfo.java @@ -28,6 +28,7 @@ */ package io.github.classgraph; +import java.util.Map; import java.util.Set; /** @@ -60,14 +61,22 @@ public class ArrayClassInfo extends ClassInfo { * @param scanResult * the scan result */ - ArrayClassInfo(final ArrayTypeSignature arrayTypeSignature, final ScanResult scanResult) { + ArrayClassInfo(final ArrayTypeSignature arrayTypeSignature) { super(arrayTypeSignature.getClassName(), /* modifiers = */ 0, /* resource = */ null); this.arrayTypeSignature = arrayTypeSignature; - this.scanResult = scanResult; // Pre-load fields from element type getElementClassInfo(); } + /* (non-Javadoc) + * @see io.github.classgraph.ClassInfo#setScanResult(io.github.classgraph.ScanResult) + */ + @Override + void setScanResult(final ScanResult scanResult) { + // TODO Auto-generated method stub + super.setScanResult(scanResult); + } + // ------------------------------------------------------------------------------------------------------------- /** @@ -123,7 +132,7 @@ public int getNumDimensions() { * Get the {@link ClassInfo} instance for the array element type. * * @return the {@link ClassInfo} instance for the array element type. Returns null if the element type was not - * found during the scan. In particular, will return null for + * found during the scan. In particular, will return null for arrays that have a primitive element type. */ public ClassInfo getElementClassInfo() { if (elementClassInfo == null) { @@ -209,16 +218,17 @@ public Class loadClass() { // ------------------------------------------------------------------------------------------------------------- /** - * Get the names of any classes referenced in this class' type descriptor, or the type descriptors of fields, - * methods or annotations. + * Get {@link ClassInfo} objects for any classes referenced in the type descriptor or type signature. * - * @param referencedClassNames - * the referenced class names + * @param classNameToClassInfo + * the map from class name to {@link ClassInfo}. + * @param refdClassInfo + * the referenced class info */ @Override - protected void findReferencedClassNames(final Set referencedClassNames) { - super.findReferencedClassNames(referencedClassNames); - arrayTypeSignature.findReferencedClassNames(referencedClassNames); + protected void findReferencedClassInfo(final Map classNameToClassInfo, + final Set refdClassInfo) { + super.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); } // ------------------------------------------------------------------------------------------------------------- diff --git a/src/main/java/io/github/classgraph/ArrayTypeSignature.java b/src/main/java/io/github/classgraph/ArrayTypeSignature.java index df2d021cf..a4c3b818d 100644 --- a/src/main/java/io/github/classgraph/ArrayTypeSignature.java +++ b/src/main/java/io/github/classgraph/ArrayTypeSignature.java @@ -73,6 +73,49 @@ public class ArrayTypeSignature extends ReferenceTypeSignature { this.typeSignatureStr = typeSignatureStr; } + /** + * Constructor. + * + * @param eltClassName + * The type signature of the array elements. + * @param numDims + * The number of array dimensions. + * @param typeSignatureStr + * Raw array type signature string (e.g. "[[I") + */ + ArrayTypeSignature(final String eltClassName, final int numDims) { + super(); + final BaseTypeSignature baseTypeSignature = BaseTypeSignature.getTypeSignature(eltClassName); + String eltTypeSigStr; + if (baseTypeSignature != null) { + // Element type is a base (primitive) type + eltTypeSigStr = baseTypeSignature.getTypeSignatureChar(); + this.elementTypeSignature = baseTypeSignature; + } else { + // Element type is not a base (primitive) type -- create a type signature for element type + eltTypeSigStr = "L" + eltClassName.replace('.', '/') + ";"; + try { + this.elementTypeSignature = ClassRefTypeSignature.parse(new Parser(eltTypeSigStr), + // No type variables to resolve for generic types + /* definingClassName = */ null); + if (this.elementTypeSignature == null) { + throw new IllegalArgumentException( + "Could not form array base type signature for class " + eltClassName); + } + } catch (final ParseException e) { + throw new IllegalArgumentException( + "Could not form array base type signature for class " + eltClassName); + } + } + final StringBuilder buf = new StringBuilder(numDims + eltTypeSigStr.length()); + for (int i = 0; i < numDims; i++) { + buf.append('['); + } + buf.append(eltTypeSigStr); + this.typeSignatureStr = buf.toString(); + this.numDims = numDims; + } + /** * Get the raw array type signature string, e.g. "[[I". * @@ -127,8 +170,19 @@ protected ClassInfo getClassInfo() { * @return the {@link ArrayClassInfo} instance. */ public ArrayClassInfo getArrayClassInfo() { - if (arrayClassInfo == null && scanResult != null) { - arrayClassInfo = new ArrayClassInfo(this, scanResult); + if (arrayClassInfo == null) { + if (scanResult != null) { + final String className = getClassName(); + // Cache ArrayClassInfo instances using scanResult.classNameToClassInfo, if scanResult is available + arrayClassInfo = (ArrayClassInfo) scanResult.classNameToClassInfo.get(className); + if (arrayClassInfo == null) { + scanResult.classNameToClassInfo.put(className, arrayClassInfo = new ArrayClassInfo(this)); + arrayClassInfo.setScanResult(this.scanResult); + } + } else { + // scanResult is not yet available, create an uncached instance of an ArrayClassInfo for this type + arrayClassInfo = new ArrayClassInfo(this); + } } return arrayClassInfo; } @@ -142,14 +196,20 @@ void setScanResult(final ScanResult scanResult) { if (elementTypeSignature != null) { elementTypeSignature.setScanResult(scanResult); } + if (arrayClassInfo != null) { + arrayClassInfo.setScanResult(scanResult); + } } - /* (non-Javadoc) - * @see io.github.classgraph.HierarchicalTypeSignature#findReferencedClassNames(java.util.Set) + /** + * Get the names of any classes referenced in the type signature. + * + * @param refdClassNames + * the referenced class names. */ @Override - void findReferencedClassNames(final Set referencedClassNames) { - elementTypeSignature.findReferencedClassNames(referencedClassNames); + protected void findReferencedClassNames(final Set refdClassNames) { + elementTypeSignature.findReferencedClassNames(refdClassNames); } // ------------------------------------------------------------------------------------------------------------- diff --git a/src/main/java/io/github/classgraph/BaseTypeSignature.java b/src/main/java/io/github/classgraph/BaseTypeSignature.java index 2f389f4cd..af16b4fca 100644 --- a/src/main/java/io/github/classgraph/BaseTypeSignature.java +++ b/src/main/java/io/github/classgraph/BaseTypeSignature.java @@ -37,6 +37,36 @@ public class BaseTypeSignature extends TypeSignature { /** A base type (byte, char, double, float, int, long, short, boolean, or void). */ private final String baseType; + /** The type signature character used to represent the base type. */ + private final String typeSignatureChar; + + /** byte type signature. */ + private static final BaseTypeSignature BYTE = new BaseTypeSignature("byte", 'B'); + + /** char type signature. */ + private static final BaseTypeSignature CHAR = new BaseTypeSignature("char", 'C'); + + /** double type signature. */ + private static final BaseTypeSignature DOUBLE = new BaseTypeSignature("double", 'D'); + + /** float type signature. */ + private static final BaseTypeSignature FLOAT = new BaseTypeSignature("float", 'F'); + + /** int type signature. */ + private static final BaseTypeSignature INT = new BaseTypeSignature("int", 'I'); + + /** long type signature. */ + private static final BaseTypeSignature LONG = new BaseTypeSignature("long", 'J'); + + /** short type signature. */ + private static final BaseTypeSignature SHORT = new BaseTypeSignature("short", 'S'); + + /** boolean type signature. */ + private static final BaseTypeSignature BOOLEAN = new BaseTypeSignature("boolean", 'Z'); + + /** void type signature. */ + static final BaseTypeSignature VOID = new BaseTypeSignature("void", 'V'); + // ------------------------------------------------------------------------------------------------------------- /** @@ -45,9 +75,10 @@ public class BaseTypeSignature extends TypeSignature { * @param baseType * the base type */ - BaseTypeSignature(final String baseType) { + private BaseTypeSignature(final String baseType, final char typeSignatureChar) { super(); this.baseType = baseType; + this.typeSignatureChar = Character.toString(typeSignatureChar); } // ------------------------------------------------------------------------------------------------------------- @@ -61,6 +92,15 @@ public String getTypeStr() { return baseType; } + /** + * Get the type signature char used to represent the type, e.g. "Z" for int. + * + * @return the type signature char, as a one-char String. + */ + public String getTypeSignatureChar() { + return typeSignatureChar; + } + /** * Get the type. * @@ -91,6 +131,38 @@ public Class getType() { } } + /** + * Get the {@link BaseTypeSignature} for a given type name. + * + * @param typeName + * the name of the type. + * @return The {@link BaseTypeSignature} of the named base type, or null if typeName is not a base type. + */ + public static BaseTypeSignature getTypeSignature(final String typeName) { + switch (typeName) { + case "byte": + return BYTE; + case "char": + return CHAR; + case "double": + return DOUBLE; + case "float": + return FLOAT; + case "int": + return INT; + case "long": + return LONG; + case "short": + return SHORT; + case "boolean": + return BOOLEAN; + case "void": + return VOID; + default: + return null; + } + } + /* (non-Javadoc) * @see io.github.classgraph.ScanResultObject#loadClass() */ @@ -127,31 +199,31 @@ static BaseTypeSignature parse(final Parser parser) { switch (parser.peek()) { case 'B': parser.next(); - return new BaseTypeSignature("byte"); + return BYTE; case 'C': parser.next(); - return new BaseTypeSignature("char"); + return CHAR; case 'D': parser.next(); - return new BaseTypeSignature("double"); + return DOUBLE; case 'F': parser.next(); - return new BaseTypeSignature("float"); + return FLOAT; case 'I': parser.next(); - return new BaseTypeSignature("int"); + return INT; case 'J': parser.next(); - return new BaseTypeSignature("long"); + return LONG; case 'S': parser.next(); - return new BaseTypeSignature("short"); + return SHORT; case 'Z': parser.next(); - return new BaseTypeSignature("boolean"); + return BOOLEAN; case 'V': parser.next(); - return new BaseTypeSignature("void"); + return VOID; default: return null; } @@ -176,12 +248,15 @@ protected ClassInfo getClassInfo() { throw new IllegalArgumentException("getClassInfo() cannot be called here"); } - /* (non-Javadoc) - * @see io.github.classgraph.HierarchicalTypeSignature#findReferencedClassNames(java.util.Set) + /** + * Get the names of any classes referenced in the type signature. + * + * @param refdClassNames + * the referenced class names. */ @Override - void findReferencedClassNames(final Set classNameListOut) { - // Don't return byte.class, int.class, etc. + protected void findReferencedClassNames(final Set refdClassNames) { + // Don't add byte.class, int.class, etc. } // ------------------------------------------------------------------------------------------------------------- diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index 53f8411fb..6f565b426 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -309,20 +309,36 @@ boolean addRelatedClass(final RelType relType, final ClassInfo classInfo) { * * @param className * the class name - * @param classModifiers - * the class modifiers * @param classNameToClassInfo * the map from class name to class info - * @return the or create class info + * @return the {@link ClassInfo} object. */ - static ClassInfo getOrCreateClassInfo(final String className, final int classModifiers, + static ClassInfo getOrCreateClassInfo(final String className, final Map classNameToClassInfo) { + // Look for array class names + int numArrayDims = 0; + String baseClassName = className; + while (baseClassName.endsWith("[]")) { + numArrayDims++; + baseClassName = baseClassName.substring(0, baseClassName.length() - 2); + } + // Be resilient to the use of class descriptors rather than class names (should not be needed) + while (baseClassName.startsWith("[")) { + numArrayDims++; + baseClassName = baseClassName.substring(1); + } + if (baseClassName.endsWith(";")) { + baseClassName = baseClassName.substring(baseClassName.length() - 1); + } + baseClassName = baseClassName.replace('/', '.'); + ClassInfo classInfo = classNameToClassInfo.get(className); if (classInfo == null) { - classNameToClassInfo.put(className, - classInfo = new ClassInfo(className, classModifiers, /* classfileResource = */ null)); + classNameToClassInfo.put(className, // + classInfo = numArrayDims == 0 // + ? new ClassInfo(baseClassName, /* classModifiers = */ 0, /* classfileResource = */ null) + : new ArrayClassInfo(new ArrayTypeSignature(baseClassName, numArrayDims))); } - classInfo.setModifiers(classModifiers); return classInfo; } @@ -372,8 +388,7 @@ void setIsAnnotation(final boolean isAnnotation) { */ void addSuperclass(final String superclassName, final Map classNameToClassInfo) { if (superclassName != null && !superclassName.equals("java.lang.Object")) { - final ClassInfo superclassClassInfo = getOrCreateClassInfo(superclassName, /* classModifiers = */ 0, - classNameToClassInfo); + final ClassInfo superclassClassInfo = getOrCreateClassInfo(superclassName, classNameToClassInfo); this.addRelatedClass(RelType.SUPERCLASSES, superclassClassInfo); superclassClassInfo.addRelatedClass(RelType.SUBCLASSES, this); } @@ -388,10 +403,8 @@ void addSuperclass(final String superclassName, final Map cla * the map from class name to class info */ void addImplementedInterface(final String interfaceName, final Map classNameToClassInfo) { - final ClassInfo interfaceClassInfo = getOrCreateClassInfo(interfaceName, - /* classModifiers = */ Modifier.INTERFACE, classNameToClassInfo); + final ClassInfo interfaceClassInfo = getOrCreateClassInfo(interfaceName, classNameToClassInfo); interfaceClassInfo.setIsInterface(true); - interfaceClassInfo.modifiers |= Modifier.INTERFACE; this.addRelatedClass(RelType.IMPLEMENTED_INTERFACES, interfaceClassInfo); interfaceClassInfo.addRelatedClass(RelType.CLASSES_IMPLEMENTING, this); } @@ -408,9 +421,10 @@ static void addClassContainment(final List classContainmentEnt final Map classNameToClassInfo) { for (final ClassContainment classContainment : classContainmentEntries) { final ClassInfo innerClassInfo = ClassInfo.getOrCreateClassInfo(classContainment.innerClassName, - /* classModifiers = */ classContainment.innerClassModifierBits, classNameToClassInfo); + classNameToClassInfo); + innerClassInfo.setModifiers(classContainment.innerClassModifierBits); final ClassInfo outerClassInfo = ClassInfo.getOrCreateClassInfo(classContainment.outerClassName, - /* classModifiers = */ 0, classNameToClassInfo); + classNameToClassInfo); innerClassInfo.addRelatedClass(RelType.CONTAINED_WITHIN_OUTER_CLASS, outerClassInfo); outerClassInfo.addRelatedClass(RelType.CONTAINS_INNER_CLASS, innerClassInfo); } @@ -437,7 +451,8 @@ void addFullyQualifiedDefiningMethodName(final String fullyQualifiedDefiningMeth void addClassAnnotation(final AnnotationInfo classAnnotationInfo, final Map classNameToClassInfo) { final ClassInfo annotationClassInfo = getOrCreateClassInfo(classAnnotationInfo.getName(), - ANNOTATION_CLASS_MODIFIER, classNameToClassInfo); + classNameToClassInfo); + annotationClassInfo.setModifiers(ANNOTATION_CLASS_MODIFIER); if (this.annotationInfo == null) { this.annotationInfo = new AnnotationInfoList(2); } @@ -469,7 +484,8 @@ private void addFieldOrMethodAnnotationInfo(final AnnotationInfoList annotationI if (annotationInfoList != null) { for (final AnnotationInfo fieldAnnotationInfo : annotationInfoList) { final ClassInfo annotationClassInfo = getOrCreateClassInfo(fieldAnnotationInfo.getName(), - ANNOTATION_CLASS_MODIFIER, classNameToClassInfo); + classNameToClassInfo); + annotationClassInfo.setModifiers(ANNOTATION_CLASS_MODIFIER); // Mark this class as having a field or method with this annotation this.addRelatedClass(isField ? RelType.FIELD_ANNOTATIONS : RelType.METHOD_ANNOTATIONS, annotationClassInfo); @@ -528,8 +544,8 @@ void addMethodInfo(final MethodInfoList methodInfoList, final Map refdClassNames) { } /** - * Get the names of any classes referenced in this class' type descriptor, or the type descriptors of fields, - * methods or annotations. + * Get {@link ClassInfo} objects for any classes referenced in this class' type descriptor, or the type + * descriptors of fields, methods or annotations. * - * @param referencedClassNames - * the referenced class names + * @param classNameToClassInfo + * the map from class name to {@link ClassInfo}. + * @param refdClassInfo + * the referenced class info */ @Override - protected void findReferencedClassNames(final Set referencedClassNames) { + protected void findReferencedClassInfo(final Map classNameToClassInfo, + final Set refdClassInfo) { + // Add this class to the set of references + super.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); + if (this.referencedClassNames != null) { - referencedClassNames.addAll(this.referencedClassNames); + for (final String refdClassName : this.referencedClassNames) { + final ClassInfo classInfo = ClassInfo.getOrCreateClassInfo(refdClassName, classNameToClassInfo); + classInfo.setScanResult(scanResult); + refdClassInfo.add(classInfo); + } } - getMethodInfo().findReferencedClassNames(referencedClassNames); - getFieldInfo().findReferencedClassNames(referencedClassNames); - getAnnotationInfo().findReferencedClassNames(referencedClassNames); + getMethodInfo().findReferencedClassInfo(classNameToClassInfo, refdClassInfo); + getFieldInfo().findReferencedClassInfo(classNameToClassInfo, refdClassInfo); + getAnnotationInfo().findReferencedClassInfo(classNameToClassInfo, refdClassInfo); if (annotationDefaultParamValues != null) { - annotationDefaultParamValues.findReferencedClassNames(referencedClassNames); + annotationDefaultParamValues.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); } final ClassTypeSignature classSig = getTypeSignature(); if (classSig != null) { - classSig.findReferencedClassNames(referencedClassNames); + classSig.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); } - // Remove any self-references - referencedClassNames.remove(name); } // ------------------------------------------------------------------------------------------------------------- diff --git a/src/main/java/io/github/classgraph/ClassRefTypeSignature.java b/src/main/java/io/github/classgraph/ClassRefTypeSignature.java index e89c28e11..e1cebf573 100644 --- a/src/main/java/io/github/classgraph/ClassRefTypeSignature.java +++ b/src/main/java/io/github/classgraph/ClassRefTypeSignature.java @@ -219,15 +219,17 @@ void setScanResult(final ScanResult scanResult) { } } - /* (non-Javadoc) - * @see io.github.classgraph.HierarchicalTypeSignature#findReferencedClassNames(java.util.Set) + /** + * Get the names of any classes referenced in the type signature. + * + * @param refdClassNames + * the referenced class names. */ @Override - void findReferencedClassNames(final Set classNameListOut) { - classNameListOut.add(className); - classNameListOut.add(getFullyQualifiedClassName()); + protected void findReferencedClassNames(final Set refdClassNames) { + refdClassNames.add(getFullyQualifiedClassName()); for (final TypeArgument typeArgument : typeArguments) { - typeArgument.findReferencedClassNames(classNameListOut); + typeArgument.findReferencedClassNames(refdClassNames); } } diff --git a/src/main/java/io/github/classgraph/ClassTypeSignature.java b/src/main/java/io/github/classgraph/ClassTypeSignature.java index 454a3f967..a3d6963e3 100644 --- a/src/main/java/io/github/classgraph/ClassTypeSignature.java +++ b/src/main/java/io/github/classgraph/ClassTypeSignature.java @@ -30,7 +30,9 @@ import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import nonapi.io.github.classgraph.types.ParseException; @@ -189,19 +191,41 @@ void setScanResult(final ScanResult scanResult) { } } - /* (non-Javadoc) - * @see io.github.classgraph.HierarchicalTypeSignature#findReferencedClassNames(java.util.Set) + /** + * Get the names of any classes referenced in the type signature. + * + * @param refdClassNames + * the referenced class names. */ - @Override - void findReferencedClassNames(final Set classNameListOut) { + protected void findReferencedClassNames(final Set refdClassNames) { for (final TypeParameter typeParameter : typeParameters) { - typeParameter.findReferencedClassNames(classNameListOut); + typeParameter.findReferencedClassNames(refdClassNames); } if (superclassSignature != null) { - superclassSignature.findReferencedClassNames(classNameListOut); + superclassSignature.findReferencedClassNames(refdClassNames); } for (final ClassRefTypeSignature typeSignature : superinterfaceSignatures) { - typeSignature.findReferencedClassNames(classNameListOut); + typeSignature.findReferencedClassNames(refdClassNames); + } + } + + /** + * Get {@link ClassInfo} objects for any classes referenced in the type descriptor or type signature. + * + * @param classNameToClassInfo + * the map from class name to {@link ClassInfo}. + * @param refdClassInfo + * the referenced class info + */ + @Override + protected void findReferencedClassInfo(final Map classNameToClassInfo, + final Set refdClassInfo) { + final Set refdClassNames = new HashSet<>(); + findReferencedClassNames(refdClassNames); + for (final String refdClassName : refdClassNames) { + final ClassInfo classInfo = ClassInfo.getOrCreateClassInfo(refdClassName, classNameToClassInfo); + classInfo.scanResult = scanResult; + refdClassInfo.add(classInfo); } } diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index 757fde166..24a8d8ffb 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -1052,9 +1052,11 @@ private void readConstantPoolEntries() throws IOException { } } - // Find classes referenced in the constant pool (note that there are some class refs that will not be + // Find classes referenced in the constant pool. Note that there are some class refs that will not be // found this way, e.g. enum classes and class refs in annotation parameter values, since they are - // referenced as strings (tag 1) rather than classes (tag 7) or type signatures (part of tag 12)). + // referenced as strings (tag 1) rather than classes (tag 7) or type signatures (part of tag 12). + // Therefore, a hybrid approach needs to be applied of extracting these other class refs from + // the ClassInfo graph, and combining them with class names extracted from the constant pool here. if (scanSpec.enableInterClassDependencies) { refdClassNames = new HashSet<>(); // Get class names from direct class references in constant pool @@ -1628,6 +1630,9 @@ private void readClassAttributes() throws IOException, ClassfileFormatException ClassTypeSignature typeSig = null; try { typeSig = ClassTypeSignature.parse(typeSignature, /* classInfo = */ null); + if (refdClassNames != null) { + typeSig.findReferencedClassNames(refdClassNames); + } } catch (final ParseException e) { // Ignore } diff --git a/src/main/java/io/github/classgraph/FieldInfo.java b/src/main/java/io/github/classgraph/FieldInfo.java index 3f104daa3..330d0c5c3 100644 --- a/src/main/java/io/github/classgraph/FieldInfo.java +++ b/src/main/java/io/github/classgraph/FieldInfo.java @@ -31,6 +31,7 @@ import java.lang.annotation.Repeatable; import java.lang.reflect.Field; import java.lang.reflect.Modifier; +import java.util.Map; import java.util.Set; import io.github.classgraph.ClassInfo.RelType; @@ -388,24 +389,27 @@ void setScanResult(final ScanResult scanResult) { } /** - * Get the names of any classes in the type descriptor or type signature. + * Get {@link ClassInfo} objects for any classes referenced in the type descriptor or type signature. * - * @param classNames - * the names of any classes in the type descriptor or type signature. + * @param classNameToClassInfo + * the map from class name to {@link ClassInfo}. + * @param refdClassInfo + * the referenced class info */ @Override - protected void findReferencedClassNames(final Set classNames) { + protected void findReferencedClassInfo(final Map classNameToClassInfo, + final Set refdClassInfo) { final TypeSignature methodSig = getTypeSignature(); if (methodSig != null) { - methodSig.findReferencedClassNames(classNames); + methodSig.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); } final TypeSignature methodDesc = getTypeDescriptor(); if (methodDesc != null) { - methodDesc.findReferencedClassNames(classNames); + methodDesc.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); } if (annotationInfo != null) { for (final AnnotationInfo ai : annotationInfo) { - ai.findReferencedClassNames(classNames); + ai.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); } } } diff --git a/src/main/java/io/github/classgraph/FieldInfoList.java b/src/main/java/io/github/classgraph/FieldInfoList.java index d423b5afd..44ebcc105 100644 --- a/src/main/java/io/github/classgraph/FieldInfoList.java +++ b/src/main/java/io/github/classgraph/FieldInfoList.java @@ -31,6 +31,7 @@ import java.util.Collection; import java.util.Iterator; import java.util.ListIterator; +import java.util.Map; import java.util.Set; import java.util.Spliterator; import java.util.function.Consumer; @@ -223,14 +224,17 @@ public FieldInfoList(final Collection fieldInfoCollection) { // ------------------------------------------------------------------------------------------------------------- /** - * Find the names of any classes referenced in the fields in this list. + * Get {@link ClassInfo} objects for any classes referenced in the list. * - * @param referencedClassNames - * the referenced class names + * @param classNameToClassInfo + * the map from class name to {@link ClassInfo}. + * @param refdClassInfo + * the referenced class info */ - void findReferencedClassNames(final Set referencedClassNames) { + protected void findReferencedClassInfo(final Map classNameToClassInfo, + final Set refdClassInfo) { for (final FieldInfo fi : this) { - fi.findReferencedClassNames(referencedClassNames); + fi.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); } } diff --git a/src/main/java/io/github/classgraph/GraphvizDotfileGenerator.java b/src/main/java/io/github/classgraph/GraphvizDotfileGenerator.java index 8037b44f4..9d02d62a5 100644 --- a/src/main/java/io/github/classgraph/GraphvizDotfileGenerator.java +++ b/src/main/java/io/github/classgraph/GraphvizDotfileGenerator.java @@ -573,10 +573,10 @@ static String generateGraphVizDotFile(final ClassInfoList classInfoList, final f if (showFieldTypeDependencyEdges && classNode.fieldInfo != null) { for (final FieldInfo fi : classNode.fieldInfo) { - for (final String referencedFieldTypeName : fi.findReferencedClassNames()) { - if (allVisibleNodes.contains(referencedFieldTypeName)) { + for (final ClassInfo referencedFieldType : fi.findReferencedClassInfo()) { + if (allVisibleNodes.contains(referencedFieldType.getName())) { // class --[ ] field type (open box) - buf.append(" \"").append(referencedFieldTypeName).append("\" -> \"") + buf.append(" \"").append(referencedFieldType.getName()).append("\" -> \"") .append(classNode.getName()) .append("\" [arrowtail=obox, arrowsize=2.5, dir=back]\n"); } @@ -586,10 +586,10 @@ static String generateGraphVizDotFile(final ClassInfoList classInfoList, final f if (showMethodTypeDependencyEdges && classNode.methodInfo != null) { for (final MethodInfo mi : classNode.methodInfo) { - for (final String referencedMethodTypeName : mi.findReferencedClassNames()) { - if (allVisibleNodes.contains(referencedMethodTypeName)) { + for (final ClassInfo referencedMethodType : mi.findReferencedClassInfo()) { + if (allVisibleNodes.contains(referencedMethodType.getName())) { // class --[#] field type (open box) - buf.append(" \"").append(referencedMethodTypeName).append("\" -> \"") + buf.append(" \"").append(referencedMethodType.getName()).append("\" -> \"") .append(classNode.getName()) .append("\" [arrowtail=box, arrowsize=2.5, dir=back]\n"); } diff --git a/src/main/java/io/github/classgraph/HierarchicalTypeSignature.java b/src/main/java/io/github/classgraph/HierarchicalTypeSignature.java index ecadde023..35edb6dfd 100644 --- a/src/main/java/io/github/classgraph/HierarchicalTypeSignature.java +++ b/src/main/java/io/github/classgraph/HierarchicalTypeSignature.java @@ -28,18 +28,8 @@ */ package io.github.classgraph; -import java.util.Set; - /** * A Java type signature. Subclasses are ClassTypeSignature, MethodTypeSignature, and TypeSignature. */ public abstract class HierarchicalTypeSignature extends ScanResultObject { - /** - * Find the names of all classes referenced in the type signature. - * - * @param classNames - * The set to store class names in. - */ - @Override - abstract void findReferencedClassNames(final Set classNames); } \ No newline at end of file diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index 00a2f6cdd..6631f9ac8 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -33,6 +33,7 @@ import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Set; import io.github.classgraph.ClassInfo.RelType; @@ -647,31 +648,34 @@ void setScanResult(final ScanResult scanResult) { } /** - * Get the names of any classes in the type descriptor or type signature. + * Get {@link ClassInfo} objects for any classes referenced in the type descriptor or type signature. * - * @param classNames - * the class names + * @param classNameToClassInfo + * the map from class name to {@link ClassInfo}. + * @param refdClassInfo + * the referenced class info */ @Override - protected void findReferencedClassNames(final Set classNames) { + protected void findReferencedClassInfo(final Map classNameToClassInfo, + final Set refdClassInfo) { final MethodTypeSignature methodSig = getTypeSignature(); if (methodSig != null) { - methodSig.findReferencedClassNames(classNames); + methodSig.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); } final MethodTypeSignature methodDesc = getTypeDescriptor(); if (methodDesc != null) { - methodDesc.findReferencedClassNames(classNames); + methodDesc.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); } if (annotationInfo != null) { for (final AnnotationInfo ai : annotationInfo) { - ai.findReferencedClassNames(classNames); + ai.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); } } for (final MethodParameterInfo mpi : getParameterInfo()) { final AnnotationInfo[] aiArr = mpi.annotationInfo; if (aiArr != null) { for (final AnnotationInfo ai : aiArr) { - ai.findReferencedClassNames(classNames); + ai.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); } } } diff --git a/src/main/java/io/github/classgraph/MethodInfoList.java b/src/main/java/io/github/classgraph/MethodInfoList.java index 1568e0567..9652fd650 100644 --- a/src/main/java/io/github/classgraph/MethodInfoList.java +++ b/src/main/java/io/github/classgraph/MethodInfoList.java @@ -39,7 +39,6 @@ /** A list of {@link MethodInfo} objects. */ public class MethodInfoList extends InfoList { - /** An unmodifiable empty {@link MethodInfoList}. */ static final MethodInfoList EMPTY_LIST = new MethodInfoList() { @Override @@ -224,14 +223,17 @@ public MethodInfoList(final Collection methodInfoCollection) { // ------------------------------------------------------------------------------------------------------------- /** - * Find the names of any classes referenced in the methods in this list. + * Get {@link ClassInfo} objects for any classes referenced in the type descriptor or type signature. * - * @param referencedClassNames - * the referenced class names + * @param classNameToClassInfo + * the map from class name to {@link ClassInfo}. + * @param refdClassInfo + * the referenced class info */ - void findReferencedClassNames(final Set referencedClassNames) { + protected void findReferencedClassInfo(final Map classNameToClassInfo, + final Set refdClassInfo) { for (final MethodInfo mi : this) { - mi.findReferencedClassNames(referencedClassNames); + mi.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); } } diff --git a/src/main/java/io/github/classgraph/MethodTypeSignature.java b/src/main/java/io/github/classgraph/MethodTypeSignature.java index 276d15412..5feef8554 100644 --- a/src/main/java/io/github/classgraph/MethodTypeSignature.java +++ b/src/main/java/io/github/classgraph/MethodTypeSignature.java @@ -30,7 +30,9 @@ import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import nonapi.io.github.classgraph.types.ParseException; @@ -134,7 +136,7 @@ static MethodTypeSignature parse(final String typeDescriptor, final String defin // Special case for instance initialization method signatures in a CONSTANT_NameAndType_info structure: // https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-4.html#jvms-4.4.2 return new MethodTypeSignature(Collections. emptyList(), - Collections. emptyList(), new BaseTypeSignature("void"), + Collections. emptyList(), BaseTypeSignature.VOID, Collections. emptyList()); } final Parser parser = new Parser(typeDescriptor); @@ -240,29 +242,51 @@ void setScanResult(final ScanResult scanResult) { } } - /* (non-Javadoc) - * @see io.github.classgraph.HierarchicalTypeSignature#findReferencedClassNames(java.util.Set) + /** + * Get the names of any classes referenced in the type signature. + * + * @param refdClassNames + * the referenced class names. */ - @Override - void findReferencedClassNames(final Set classNameListOut) { + protected void findReferencedClassNames(final Set refdClassNames) { for (final TypeParameter typeParameter : typeParameters) { if (typeParameter != null) { - typeParameter.findReferencedClassNames(classNameListOut); + typeParameter.findReferencedClassNames(refdClassNames); } } for (final TypeSignature typeSignature : parameterTypeSignatures) { if (typeSignature != null) { - typeSignature.findReferencedClassNames(classNameListOut); + typeSignature.findReferencedClassNames(refdClassNames); } } - resultType.findReferencedClassNames(classNameListOut); + resultType.findReferencedClassNames(refdClassNames); for (final ClassRefOrTypeVariableSignature typeSignature : throwsSignatures) { if (typeSignature != null) { - typeSignature.findReferencedClassNames(classNameListOut); + typeSignature.findReferencedClassNames(refdClassNames); } } } + /** + * Get {@link ClassInfo} objects for any classes referenced in the type descriptor or type signature. + * + * @param classNameToClassInfo + * the map from class name to {@link ClassInfo}. + * @param refdClassInfo + * the referenced class info + */ + @Override + protected void findReferencedClassInfo(final Map classNameToClassInfo, + final Set refdClassInfo) { + final Set refdClassNames = new HashSet<>(); + findReferencedClassNames(refdClassNames); + for (final String refdClassName : refdClassNames) { + final ClassInfo classInfo = ClassInfo.getOrCreateClassInfo(refdClassName, classNameToClassInfo); + classInfo.scanResult = scanResult; + refdClassInfo.add(classInfo); + } + } + // ------------------------------------------------------------------------------------------------------------- /* (non-Javadoc) diff --git a/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java b/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java index 5a088ff19..5937fa00b 100644 --- a/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java +++ b/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java @@ -30,6 +30,7 @@ import java.lang.reflect.Array; import java.util.Arrays; +import java.util.Map; import java.util.Objects; import java.util.Set; @@ -533,22 +534,28 @@ void setScanResult(final ScanResult scanResult) { } /** - * Get the names of any classes referenced in the annotation parameters. + * Get {@link ClassInfo} objects for any classes referenced in annotation parameters. * - * @param referencedClassNames - * referenced class names + * @param classNameToClassInfo + * the map from class name to {@link ClassInfo}. + * @param refdClassInfo + * the referenced class info */ @Override - void findReferencedClassNames(final Set referencedClassNames) { + protected void findReferencedClassInfo(final Map classNameToClassInfo, + final Set refdClassInfo) { if (annotationEnumValue != null) { - annotationEnumValue.findReferencedClassNames(referencedClassNames); + annotationEnumValue.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); } else if (annotationClassRef != null) { - referencedClassNames.add(annotationClassRef.getClassName()); + final ClassInfo classInfo = annotationClassRef.getClassInfo(); + if (classInfo != null) { + refdClassInfo.add(classInfo); + } } else if (annotationInfo != null) { - annotationInfo.findReferencedClassNames(referencedClassNames); + annotationInfo.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); } else if (objectArrayValue != null) { for (final ObjectTypedValueWrapper item : objectArrayValue) { - item.findReferencedClassNames(referencedClassNames); + item.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); } } } diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index f5d121ee4..f0f89667a 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -78,7 +78,7 @@ public final class ScanResult implements Closeable, AutoCloseable { private Map pathToWhitelistedResourcesCached; /** The map from class name to {@link ClassInfo}. */ - private Map classNameToClassInfo; + Map classNameToClassInfo; /** The map from package name to {@link PackageInfo}. */ private Map packageNameToPackageInfo; @@ -316,21 +316,19 @@ private void indexResourcesAndClassInfo() { // classes that were not scanned if (scanSpec.enableInterClassDependencies) { for (final ClassInfo ci : new ArrayList<>(classNameToClassInfo.values())) { - final Set refdClasses = new HashSet<>(); - for (final String refdClassName : ci.findReferencedClassNames()) { - // Don't add circular dependencies - if (!ci.getName().equals(refdClassName)) { - // Get ClassInfo object for the named class, or create one if it doesn't exist - final ClassInfo refdClassInfo = ClassInfo.getOrCreateClassInfo(refdClassName, - /* classModifiers are unknown */ 0, classNameToClassInfo); - refdClassInfo.setScanResult(this); + final Set refdClassesFiltered = new HashSet<>(); + for (final ClassInfo refdClassInfo : ci.findReferencedClassInfo()) { + // Don't add self-references, or references to Object + if (refdClassInfo != null && !ci.equals(refdClassInfo) + && !refdClassInfo.getName().equals("java.lang.Object")) { + // Only add class to result if it is whitelisted, or external classes are enabled if (!refdClassInfo.isExternalClass() || scanSpec.enableExternalClasses) { - // Only add class to result if it is whitelisted, or external classes are enabled - refdClasses.add(refdClassInfo); + refdClassInfo.setScanResult(this); + refdClassesFiltered.add(refdClassInfo); } } } - ci.setReferencedClasses(new ClassInfoList(refdClasses, /* sortByName = */ true)); + ci.setReferencedClasses(new ClassInfoList(refdClassesFiltered, /* sortByName = */ true)); } } } diff --git a/src/main/java/io/github/classgraph/ScanResultObject.java b/src/main/java/io/github/classgraph/ScanResultObject.java index 4337b9353..c29d152e1 100644 --- a/src/main/java/io/github/classgraph/ScanResultObject.java +++ b/src/main/java/io/github/classgraph/ScanResultObject.java @@ -29,6 +29,7 @@ package io.github.classgraph; import java.util.LinkedHashSet; +import java.util.Map; import java.util.Set; /** @@ -56,25 +57,33 @@ void setScanResult(final ScanResult scanResult) { } /** - * Get the names of all referenced classes. + * Get {@link ClassInfo} objects for any classes referenced in the type descriptor or type signature. * - * @return the referenced class names + * @return the referenced class info. */ - Set findReferencedClassNames() { - final Set allReferencedClassNames = new LinkedHashSet<>(); - findReferencedClassNames(allReferencedClassNames); - // Remove references to java.lang.Object - allReferencedClassNames.remove("java.lang.Object"); - return allReferencedClassNames; + final Set findReferencedClassInfo() { + final Set refdClassInfo = new LinkedHashSet<>(); + if (scanResult != null) { + findReferencedClassInfo(scanResult.classNameToClassInfo, refdClassInfo); + } + return refdClassInfo; } /** - * Get any class names referenced in type descriptors of this object. + * Get {@link ClassInfo} objects for any classes referenced in the type descriptor or type signature. * - * @param refdClassNames - * the referenced class names + * @param classNameToClassInfo + * the map from class name to {@link ClassInfo}. + * @param refdClassInfo + * the referenced class info */ - abstract void findReferencedClassNames(Set refdClassNames); + protected void findReferencedClassInfo(final Map classNameToClassInfo, + final Set refdClassInfo) { + final ClassInfo ci = getClassInfo(); + if (ci != null) { + refdClassInfo.add(ci); + } + } /** * The name of the class (used by {@link #getClassInfo()} to fetch the {@link ClassInfo} object for the class). diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 6f25c8f1f..f9fe9f61f 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -795,8 +795,10 @@ private ScanResult performScan(final List finalClasspathEltOrd fileToLastModified.putAll(classpathElement.fileToLastModified); } - // Scan classfiles, if scanSpec.enableClassInfo is true - final Map classNameToClassInfo = new HashMap<>(); + // Scan classfiles, if scanSpec.enableClassInfo is true. + // (classNameToClassInfo is a ConcurrentHashMap because it can be modified by + // ArrayTypeSignature.getArrayClassInfo() after scanning is complete) + final Map classNameToClassInfo = new ConcurrentHashMap<>(); final Map packageNameToPackageInfo = new HashMap<>(); final Map moduleNameToModuleInfo = new HashMap<>(); if (scanSpec.enableClassInfo) { diff --git a/src/main/java/io/github/classgraph/TypeArgument.java b/src/main/java/io/github/classgraph/TypeArgument.java index 86b2dfefd..e7d8c19db 100644 --- a/src/main/java/io/github/classgraph/TypeArgument.java +++ b/src/main/java/io/github/classgraph/TypeArgument.java @@ -197,13 +197,15 @@ void setScanResult(final ScanResult scanResult) { } } - /* (non-Javadoc) - * @see io.github.classgraph.HierarchicalTypeSignature#findReferencedClassNames(java.util.Set) + /** + * Get the names of any classes referenced in the type signature. + * + * @param refdClassNames + * the referenced class names. */ - @Override - void findReferencedClassNames(final Set classNameListOut) { + public void findReferencedClassNames(final Set refdClassNames) { if (typeSignature != null) { - typeSignature.findReferencedClassNames(classNameListOut); + typeSignature.findReferencedClassNames(refdClassNames); } } diff --git a/src/main/java/io/github/classgraph/TypeParameter.java b/src/main/java/io/github/classgraph/TypeParameter.java index 5c252b692..c4e47431b 100644 --- a/src/main/java/io/github/classgraph/TypeParameter.java +++ b/src/main/java/io/github/classgraph/TypeParameter.java @@ -182,16 +182,18 @@ void setScanResult(final ScanResult scanResult) { } } - /* (non-Javadoc) - * @see io.github.classgraph.HierarchicalTypeSignature#findReferencedClassNames(java.util.Set) + /** + * Get the names of any classes referenced in the type signature. + * + * @param refdClassNames + * the referenced class names. */ - @Override - void findReferencedClassNames(final Set classNameListOut) { + protected void findReferencedClassNames(final Set refdClassNames) { if (classBound != null) { - classBound.findReferencedClassNames(classNameListOut); + classBound.findReferencedClassNames(refdClassNames); } for (final ReferenceTypeSignature typeSignature : interfaceBounds) { - typeSignature.findReferencedClassNames(classNameListOut); + typeSignature.findReferencedClassNames(refdClassNames); } } diff --git a/src/main/java/io/github/classgraph/TypeSignature.java b/src/main/java/io/github/classgraph/TypeSignature.java index 34637a9db..38db3c61a 100644 --- a/src/main/java/io/github/classgraph/TypeSignature.java +++ b/src/main/java/io/github/classgraph/TypeSignature.java @@ -28,6 +28,10 @@ */ package io.github.classgraph; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + import nonapi.io.github.classgraph.types.ParseException; import nonapi.io.github.classgraph.types.Parser; @@ -42,6 +46,39 @@ protected TypeSignature() { // Empty } + /** + * Get the names of any classes referenced in the type signature. + * + * @param refdClassNames + * the referenced class names. + */ + protected void findReferencedClassNames(final Set refdClassNames) { + final String className = getClassName(); + if (className != null && !className.isEmpty()) { + refdClassNames.add(getClassName()); + } + } + + /** + * Get {@link ClassInfo} objects for any classes referenced in the type signature. + * + * @param classNameToClassInfo + * the map from class name to {@link ClassInfo}. + * @param refdClassInfo + * the referenced class info. + */ + @Override + final protected void findReferencedClassInfo(final Map classNameToClassInfo, + final Set refdClassInfo) { + final Set refdClassNames = new HashSet<>(); + findReferencedClassNames(refdClassNames); + for (final String refdClassName : refdClassNames) { + final ClassInfo classInfo = ClassInfo.getOrCreateClassInfo(refdClassName, classNameToClassInfo); + classInfo.scanResult = scanResult; + refdClassInfo.add(classInfo); + } + } + /** * Compare base types, ignoring generic type parameters. * diff --git a/src/main/java/io/github/classgraph/TypeVariableSignature.java b/src/main/java/io/github/classgraph/TypeVariableSignature.java index 55300b02c..642f55def 100644 --- a/src/main/java/io/github/classgraph/TypeVariableSignature.java +++ b/src/main/java/io/github/classgraph/TypeVariableSignature.java @@ -164,11 +164,14 @@ protected String getClassName() { return definingClassName; } - /* (non-Javadoc) - * @see io.github.classgraph.HierarchicalTypeSignature#findReferencedClassNames(java.util.Set) + /** + * Get the names of any classes referenced in the type signature. + * + * @param refdClassNames + * the referenced class names. */ @Override - void findReferencedClassNames(final Set classNames) { + protected void findReferencedClassNames(final Set refdClassNames) { // No class names present in type variables } diff --git a/src/test/java/io/github/classgraph/issues/issue355/Issue355.java b/src/test/java/io/github/classgraph/issues/issue355/Issue355.java new file mode 100644 index 000000000..775a60a57 --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue355/Issue355.java @@ -0,0 +1,76 @@ +package io.github.classgraph.issues.issue355; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import org.junit.Test; + +import io.github.classgraph.AnnotationClassRef; +import io.github.classgraph.ArrayClassInfo; +import io.github.classgraph.ArrayTypeSignature; +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ClassInfo; +import io.github.classgraph.ClassInfoList; +import io.github.classgraph.MethodParameterInfo; +import io.github.classgraph.ScanResult; + +/** + * Unit test. + */ +public class Issue355 { + /** Annotation parameter class */ + public class X { + } + + /** Annotation with class reference array typed param */ + @Retention(RetentionPolicy.RUNTIME) + public @interface Ann { + /** Annotation parameter */ + public Class[] value(); + } + + /** Annotated with class reference array */ + @Ann({ X.class }) + public class Y { + /** method with array-typed param */ + public void y(final X[] x) { + } + } + + /** Test **/ + @Test + public void test() throws IOException { + try (ScanResult scanResult = new ClassGraph() + .whitelistPackagesNonRecursive(Issue355.class.getPackage().getName()).enableClassInfo() + .enableInterClassDependencies().scan()) { + final ClassInfo y = scanResult.getClassInfo(Y.class.getName()); + final ClassInfo x = scanResult.getClassInfo(X.class.getName()); + assertThat(y).isNotNull(); + assertThat(x).isNotNull(); + + // Test array-typed annotation parameter + final Object annParamVal = ((Object[]) y.getAnnotationInfo().get(0).getParameterValues().get(0) + .getValue())[0]; + assertThat(annParamVal).isInstanceOf(AnnotationClassRef.class); + final AnnotationClassRef annClassRef = (AnnotationClassRef) annParamVal; + assertThat(annClassRef.getClassInfo().getName()).isEqualTo(X.class.getName()); + + // Test class dep from annotation param of array element type shows up in class deps + final ClassInfoList yDeps = scanResult.getClassDependencyMap().get(y); + assertThat(yDeps).isNotNull(); + assertThat(yDeps).contains(x); + + // Test array-typed method parameter + final MethodParameterInfo yParam = y.getMethodInfo().get(0).getParameterInfo()[0]; + final ArrayTypeSignature paramTypeSignature = (ArrayTypeSignature) yParam + .getTypeSignatureOrTypeDescriptor(); + final ArrayClassInfo arrayClassInfo = paramTypeSignature.getArrayClassInfo(); + assertThat(arrayClassInfo.getElementClassInfo().equals(x)); + assertThat(arrayClassInfo.loadClass()).isEqualTo(X[].class); + assertThat(arrayClassInfo.loadElementClass()).isEqualTo(X.class); + } + } +} From d5d8a8b19b4809818605c476284de6edc4cb4efd Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 27 Jun 2019 23:24:30 -0600 Subject: [PATCH 0242/1778] [maven-release-plugin] prepare release classgraph-4.8.43 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 0b5b3a059..2176a8bbb 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.43-SNAPSHOT + 4.8.43 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.43 From d8c84dcb76a5a6bcf35c3280c373448e2b2b3646 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 27 Jun 2019 23:24:37 -0600 Subject: [PATCH 0243/1778] [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 2176a8bbb..ef59f52b0 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.43 + 4.8.44-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.43 + HEAD From 44d914e7806740bf017359a8f301951c07cd18ae Mon Sep 17 00:00:00 2001 From: Ivan Sopov Date: Tue, 16 Jul 2019 17:52:51 +0300 Subject: [PATCH 0244/1778] add maven-wrapper --- .mvn/wrapper/MavenWrapperDownloader.java | 117 +++++++++ .mvn/wrapper/maven-wrapper.jar | Bin 0 -> 50710 bytes .mvn/wrapper/maven-wrapper.properties | 2 + .travis.yml | 5 +- README.md | 6 +- mvnw | 310 +++++++++++++++++++++++ mvnw.cmd | 182 +++++++++++++ 7 files changed, 618 insertions(+), 4 deletions(-) create mode 100644 .mvn/wrapper/MavenWrapperDownloader.java create mode 100644 .mvn/wrapper/maven-wrapper.jar create mode 100644 .mvn/wrapper/maven-wrapper.properties create mode 100755 mvnw create mode 100644 mvnw.cmd diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 000000000..c32394f14 --- /dev/null +++ b/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,117 @@ +/* + * 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 new file mode 100644 index 0000000000000000000000000000000000000000..0d5e649888a4843c1520054d9672f80c62ebbb48 GIT binary patch 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(@ literal 0 HcmV?d00001 diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 000000000..fa87ad7dd --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.1/apache-maven-3.6.1-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar diff --git a/.travis.yml b/.travis.yml index 935fbadb5..b2d6506fc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,8 +4,11 @@ jdk: - openjdk11 # LTS - openjdk-ea # Early access -sudo: true # https://github.com/travis-ci/travis-ci/issues/6593 +#ignore default install step +install: true cache: directories: - $HOME/.m2 + +script: ./mvnw clean verify diff --git a/README.md b/README.md index ca3844068..db1f6e630 100644 --- a/README.md +++ b/README.md @@ -112,12 +112,12 @@ The following commands will build the most recent version of ClassGraph from git git clone https://github.com/classgraph/classgraph.git cd classgraph export JAVA_HOME=/usr/java/default # Or similar -- Maven needs JAVA_HOME -mvn -Dmaven.test.skip=true package +./mvnw -Dmaven.test.skip=true package ``` -This will allow you to build a local SNAPSHOT jar in `target/`. Alternatively, use `mvn -Dmaven.test.skip=true install` to build a SNAPSHOT jar and then copy it into your local repository, so that you can use it in your Maven projects. Note that may need to do `mvn dependency:resolve` in your project if you overwrite an older snapshot with a newer one. +This will allow you to build a local SNAPSHOT jar in `target/`. Alternatively, use `./mvnw -Dmaven.test.skip=true install` to build a SNAPSHOT jar and then copy it into your local repository, so that you can use it in your Maven projects. Note that may need to do `./mvnw dependency:resolve` in your project if you overwrite an older snapshot with a newer one. -`mvn -U` updates from remote repositories an may overwrite your local artifact. But you can always change the `artifactId` or the `groupId` of your local ClassGraph build to place your local build artifact in another location within your local repository. +`./mvnw -U` updates from remote repositories an may overwrite your local artifact. But you can always change the `artifactId` or the `groupId` of your local ClassGraph build to place your local build artifact in another location within your local repository. ## Documentation diff --git a/mvnw b/mvnw new file mode 100755 index 000000000..d2f0ea380 --- /dev/null +++ b/mvnw @@ -0,0 +1,310 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven2 Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# 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 +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# 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" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + 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 + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# 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 + +# 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)`" +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 + +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 +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$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 +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.5/maven-wrapper-0.5.5.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.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 + + 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 + + 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 +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"` +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 + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/mvnw.cmd b/mvnw.cmd new file mode 100644 index 000000000..b26ab24f0 --- /dev/null +++ b/mvnw.cmd @@ -0,0 +1,182 @@ +@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 Maven2 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 key stroke 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.5/maven-wrapper-0.5.5.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.5/maven-wrapper-0.5.5.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% From 1b7be59f83fffbf1466f78796b0b37750df14928 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 16 Jul 2019 10:39:47 -0600 Subject: [PATCH 0245/1778] Fix Javadoc issues --- src/main/java/io/github/classgraph/ArrayClassInfo.java | 2 -- src/main/java/io/github/classgraph/ArrayTypeSignature.java | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/main/java/io/github/classgraph/ArrayClassInfo.java b/src/main/java/io/github/classgraph/ArrayClassInfo.java index 408353bba..ce70ca712 100644 --- a/src/main/java/io/github/classgraph/ArrayClassInfo.java +++ b/src/main/java/io/github/classgraph/ArrayClassInfo.java @@ -58,8 +58,6 @@ public class ArrayClassInfo extends ClassInfo { * * @param arrayTypeSignature * the array type signature - * @param scanResult - * the scan result */ ArrayClassInfo(final ArrayTypeSignature arrayTypeSignature) { super(arrayTypeSignature.getClassName(), /* modifiers = */ 0, /* resource = */ null); diff --git a/src/main/java/io/github/classgraph/ArrayTypeSignature.java b/src/main/java/io/github/classgraph/ArrayTypeSignature.java index a4c3b818d..07913b740 100644 --- a/src/main/java/io/github/classgraph/ArrayTypeSignature.java +++ b/src/main/java/io/github/classgraph/ArrayTypeSignature.java @@ -80,8 +80,6 @@ public class ArrayTypeSignature extends ReferenceTypeSignature { * The type signature of the array elements. * @param numDims * The number of array dimensions. - * @param typeSignatureStr - * Raw array type signature string (e.g. "[[I") */ ArrayTypeSignature(final String eltClassName, final int numDims) { super(); From 62b8374c849e7615ae5eb780629208d406fae1f2 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 16 Jul 2019 10:45:40 -0600 Subject: [PATCH 0246/1778] Fix static analyzer warnings --- README.md | 3 +-- Zero-Bugs-Commitment.md | 14 ++++++-------- src/main/java/io/github/classgraph/ScanResult.java | 11 +++++------ 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index db1f6e630..db81dcf52 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,6 @@ ClassGraph (formerly **FastClasspathScanner**) is an uber-fast, ultra-lightweigh | 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 vs. Java Introspection ClassGraph has the ability to "invert" the Java class and/or reflection API, or has the ability to index classes and resources. For example, the Java class and reflection API can tell you the interfaces implemented by a given class, or can give you the list of annotations on a class; ClassGraph can find **all classes that implement a given interface**, or can find **all classes that are annotated with a given annotation**. The Java API can load the content of a resource file with a specific path in a specific ClassLoader, but ClassGraph can find and load **all resources in all classloaders with paths matching a given pattern**. @@ -108,7 +107,7 @@ ClassGraph must be built on JDK 8 or newer (due to the presence of `@FunctionalI The following commands will build the most recent version of ClassGraph from git master. The compiled package will then be in the "classgraph/target" directory. -``` +```bash git clone https://github.com/classgraph/classgraph.git cd classgraph export JAVA_HOME=/usr/java/default # Or similar -- Maven needs JAVA_HOME diff --git a/Zero-Bugs-Commitment.md b/Zero-Bugs-Commitment.md index b7913aaf7..399949ea3 100644 --- a/Zero-Bugs-Commitment.md +++ b/Zero-Bugs-Commitment.md @@ -1,4 +1,3 @@ - # The Zero Bugs Commitment This project adheres to the **Zero Bugs Commitment** (`#ZeroBugs`). @@ -9,7 +8,7 @@ respecting and cultivating contributions. **As developers of this project, we pledge that:** -## 1. We will prioritize fixing bugs over implementing new features. +## (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.)* @@ -18,8 +17,7 @@ things than doing the hard work to fix old, broken things.)* 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. +## (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.)* @@ -32,7 +30,7 @@ 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. +## (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 @@ -43,7 +41,7 @@ source ecosystem.)* 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. +## (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 @@ -58,7 +56,7 @@ a disagreement. --- -#### THIS DOCUMENT IS IN THE PUBLIC DOMAIN +**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 @@ -76,4 +74,4 @@ However, please leave a record of your changes below. #### Version history: -0.1: Original version (author: Luke Hutchison -- http://twitter.com/LH ) +0.1: Original version (author: Luke Hutchison) diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index f0f89667a..bcae16e9d 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -320,12 +320,11 @@ private void indexResourcesAndClassInfo() { for (final ClassInfo refdClassInfo : ci.findReferencedClassInfo()) { // Don't add self-references, or references to Object if (refdClassInfo != null && !ci.equals(refdClassInfo) - && !refdClassInfo.getName().equals("java.lang.Object")) { - // Only add class to result if it is whitelisted, or external classes are enabled - if (!refdClassInfo.isExternalClass() || scanSpec.enableExternalClasses) { - refdClassInfo.setScanResult(this); - refdClassesFiltered.add(refdClassInfo); - } + && !refdClassInfo.getName().equals("java.lang.Object") + // Only add class to result if it is whitelisted, or external classes are enabled + && (!refdClassInfo.isExternalClass() || scanSpec.enableExternalClasses)) { + refdClassInfo.setScanResult(this); + refdClassesFiltered.add(refdClassInfo); } } ci.setReferencedClasses(new ClassInfoList(refdClassesFiltered, /* sortByName = */ true)); From 8fb74aa159cd45507f1655c48ad9a25ca93cda92 Mon Sep 17 00:00:00 2001 From: Sean Sullivan Date: Wed, 24 Jul 2019 00:18:41 -0400 Subject: [PATCH 0247/1778] add [org.clapper.classutil.ClassFinder] to README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index db81dcf52..094e32c22 100644 --- a/README.md +++ b/README.md @@ -157,6 +157,7 @@ Some other classpath scanning mechanisms include: * [QDox](https://github.com/paul-hammant/qdox), a fast Java source parser and indexer * [bndtools](https://github.com/bndtools/bnd), which is able to ["crawl"/parse the bytecode of class files](https://github.com/bndtools/bnd/blob/master/biz.aQute.bndlib/src/aQute/bnd/osgi/Clazz.java) to find all imports/dependencies, among other things. * [coffea](https://github.com/sbilinski/coffea), a command line tool and Python library for analyzing static dependences in Java bytecode +* [org.clapper.classutil.ClassFinder](https://github.com/bmc/classutil/blob/master/src/main/scala/org/clapper/classutil/ClassFinder.scala) ## License From d20f26917919ca250bc061c4e59f48c3c1bdd337 Mon Sep 17 00:00:00 2001 From: Sean Sullivan Date: Wed, 24 Jul 2019 00:20:36 -0400 Subject: [PATCH 0248/1778] maven-compiler-plugin 3.8.1 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ef59f52b0..41421bb9f 100644 --- a/pom.xml +++ b/pom.xml @@ -150,7 +150,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.8.0 + 3.8.1 UTF-8 From fb9190269b822ce2d944b44091a64dfa9f65293a Mon Sep 17 00:00:00 2001 From: Ivan Sopov Date: Mon, 29 Jul 2019 16:19:13 +0300 Subject: [PATCH 0249/1778] rename some tests not run by maven --- ...tationEquality.java => AnnotationEqualityTest.java} | 6 +++--- ...=> AnnotationParamWithPrimitiveTypedArrayTest.java} | 4 ++-- ...NonDeclared.java => DeclaredVsNonDeclaredTest.java} | 10 +++++----- ...ations.java => MethodParameterAnnotationsTest.java} | 4 ++-- .../{MultiReleaseJar.java => MultiReleaseJarTest.java} | 4 ++-- ...eTypeVariable.java => ResolveTypeVariableTest.java} | 6 +++--- ...lassLoadingWorksWithParentLastLoadersStubTest.java} | 2 +- 7 files changed, 18 insertions(+), 18 deletions(-) rename src/test/java/io/github/classgraph/features/{AnnotationEquality.java => AnnotationEqualityTest.java} (93%) rename src/test/java/io/github/classgraph/features/{AnnotationParamWithPrimitiveTypedArray.java => AnnotationParamWithPrimitiveTypedArrayTest.java} (97%) rename src/test/java/io/github/classgraph/features/{DeclaredVsNonDeclared.java => DeclaredVsNonDeclaredTest.java} (94%) rename src/test/java/io/github/classgraph/features/{MethodParameterAnnotations.java => MethodParameterAnnotationsTest.java} (92%) rename src/test/java/io/github/classgraph/features/{MultiReleaseJar.java => MultiReleaseJarTest.java} (95%) rename src/test/java/io/github/classgraph/issues/{ResolveTypeVariable.java => ResolveTypeVariableTest.java} (79%) rename src/test/java/io/github/classgraph/issues/issue267/{ClassLoadingWorksWithParentLastLoadersStub.java => ClassLoadingWorksWithParentLastLoadersStubTest.java} (98%) diff --git a/src/test/java/io/github/classgraph/features/AnnotationEquality.java b/src/test/java/io/github/classgraph/features/AnnotationEqualityTest.java similarity index 93% rename from src/test/java/io/github/classgraph/features/AnnotationEquality.java rename to src/test/java/io/github/classgraph/features/AnnotationEqualityTest.java index 45c96fe0a..c38de707b 100644 --- a/src/test/java/io/github/classgraph/features/AnnotationEquality.java +++ b/src/test/java/io/github/classgraph/features/AnnotationEqualityTest.java @@ -16,7 +16,7 @@ /** * AnnotationEquality. */ -public class AnnotationEquality { +public class AnnotationEqualityTest { /** * The Interface W. */ @@ -72,7 +72,7 @@ int a() /** * The Class Y. */ - @X(b = 5, c = { Long.class, Integer.class, AnnotationEquality.class, W.class, X.class } + @X(b = 5, c = { Long.class, Integer.class, AnnotationEqualityTest.class, W.class, X.class } // , d = "xyz", e = 'w' ) private static class Y { @@ -84,7 +84,7 @@ private static class Y { @Test public void annotationEquality() { try (ScanResult scanResult = new ClassGraph() - .whitelistPackages(AnnotationEquality.class.getPackage().getName()).enableAllInfo().scan()) { + .whitelistPackages(AnnotationEqualityTest.class.getPackage().getName()).enableAllInfo().scan()) { final ClassInfo classInfo = scanResult.getClassInfo(Y.class.getName()); assertThat(classInfo).isNotNull(); final Class cls = classInfo.loadClass(); diff --git a/src/test/java/io/github/classgraph/features/AnnotationParamWithPrimitiveTypedArray.java b/src/test/java/io/github/classgraph/features/AnnotationParamWithPrimitiveTypedArrayTest.java similarity index 97% rename from src/test/java/io/github/classgraph/features/AnnotationParamWithPrimitiveTypedArray.java rename to src/test/java/io/github/classgraph/features/AnnotationParamWithPrimitiveTypedArrayTest.java index a9ddf1c25..1ad26c645 100644 --- a/src/test/java/io/github/classgraph/features/AnnotationParamWithPrimitiveTypedArray.java +++ b/src/test/java/io/github/classgraph/features/AnnotationParamWithPrimitiveTypedArrayTest.java @@ -16,7 +16,7 @@ /** * AnnotationParamWithPrimitiveTypedArray. */ -public class AnnotationParamWithPrimitiveTypedArray { +public class AnnotationParamWithPrimitiveTypedArrayTest { /** * The Interface NestedAnnotation. */ @@ -94,7 +94,7 @@ public abstract static class AnnotatedClass { @Test public void primitiveArrayParams() { try (ScanResult scanResult = new ClassGraph().enableAllInfo() - .whitelistPackages(AnnotationParamWithPrimitiveTypedArray.class.getPackage().getName()).scan()) { + .whitelistPackages(AnnotationParamWithPrimitiveTypedArrayTest.class.getPackage().getName()).scan()) { final AnnotationInfo annotationInfo = scanResult.getClassInfo(AnnotatedClass.class.getName()) .getAnnotationInfo().get(0); final AnnotationParameterValueList annotationParams = annotationInfo.getParameterValues(); diff --git a/src/test/java/io/github/classgraph/features/DeclaredVsNonDeclared.java b/src/test/java/io/github/classgraph/features/DeclaredVsNonDeclaredTest.java similarity index 94% rename from src/test/java/io/github/classgraph/features/DeclaredVsNonDeclared.java rename to src/test/java/io/github/classgraph/features/DeclaredVsNonDeclaredTest.java index d33fcc0de..bfe0c0870 100644 --- a/src/test/java/io/github/classgraph/features/DeclaredVsNonDeclared.java +++ b/src/test/java/io/github/classgraph/features/DeclaredVsNonDeclaredTest.java @@ -39,7 +39,7 @@ /** * DeclaredVsNonDeclared. */ -public class DeclaredVsNonDeclared { +public class DeclaredVsNonDeclaredTest { /** * The Class A. */ @@ -122,7 +122,7 @@ public abstract static class C extends A { @Test public void declaredVsNonDeclaredMethods() { try (ScanResult scanResult = new ClassGraph().enableAllInfo() - .whitelistPackages(DeclaredVsNonDeclared.class.getPackage().getName()).scan()) { + .whitelistPackages(DeclaredVsNonDeclaredTest.class.getPackage().getName()).scan()) { final ClassInfo A = scanResult.getClassInfo(A.class.getName()); final ClassInfo B = scanResult.getClassInfo(B.class.getName()); assertThat(B.getFieldInfo("x").getClassInfo().getName()).isEqualTo(B.class.getName()); @@ -148,7 +148,7 @@ public Object extract(final AnnotationInfo input) { }; try (ScanResult scanResult = new ClassGraph().enableAllInfo() - .whitelistPackages(DeclaredVsNonDeclared.class.getPackage().getName()).scan()) { + .whitelistPackages(DeclaredVsNonDeclaredTest.class.getPackage().getName()).scan()) { final ClassInfo A = scanResult.getClassInfo(A.class.getName()); final ClassInfo B = scanResult.getClassInfo(B.class.getName()); final ClassInfo C = scanResult.getClassInfo(C.class.getName()); @@ -190,7 +190,7 @@ public Object extract(final AnnotationInfo input) { @Test public void annotationsShouldBeAbleToDifferentiateBetweenDirectAndReachable() { try (ScanResult scanResult = new ClassGraph().enableAllInfo() - .whitelistPackages(DeclaredVsNonDeclared.class.getPackage().getName()).scan()) { + .whitelistPackages(DeclaredVsNonDeclaredTest.class.getPackage().getName()).scan()) { final ClassInfo A = scanResult.getClassInfo(A.class.getName()); final ClassInfo B = scanResult.getClassInfo(B.class.getName()); @@ -213,7 +213,7 @@ public void annotationsShouldBeAbleToDifferentiateBetweenDirectAndReachable() { @Test public void loadFieldAndMethod() { try (ScanResult scanResult = new ClassGraph().enableAllInfo() - .whitelistPackages(DeclaredVsNonDeclared.class.getPackage().getName()).scan()) { + .whitelistPackages(DeclaredVsNonDeclaredTest.class.getPackage().getName()).scan()) { final ClassInfo B = scanResult.getClassInfo(B.class.getName()); assertThat(B.getFieldInfo("x").loadClassAndGetField().getName()).isEqualTo("x"); assertThat(B.getMethodInfo("y").get(0).loadClassAndGetMethod().getName()).isEqualTo("y"); diff --git a/src/test/java/io/github/classgraph/features/MethodParameterAnnotations.java b/src/test/java/io/github/classgraph/features/MethodParameterAnnotationsTest.java similarity index 92% rename from src/test/java/io/github/classgraph/features/MethodParameterAnnotations.java rename to src/test/java/io/github/classgraph/features/MethodParameterAnnotationsTest.java index 9ae7849ad..ed488b264 100644 --- a/src/test/java/io/github/classgraph/features/MethodParameterAnnotations.java +++ b/src/test/java/io/github/classgraph/features/MethodParameterAnnotationsTest.java @@ -13,7 +13,7 @@ /** * AnnotationEquality. */ -public class MethodParameterAnnotations { +public class MethodParameterAnnotationsTest { /** * The Annotation W. */ @@ -62,7 +62,7 @@ private abstract static class Z { @Test public void annotationEquality() { try (ScanResult scanResult = new ClassGraph() - .whitelistPackages(MethodParameterAnnotations.class.getPackage().getName()).enableAllInfo() + .whitelistPackages(MethodParameterAnnotationsTest.class.getPackage().getName()).enableAllInfo() .scan()) { assertThat(scanResult.getClassInfo(Y.class.getName()).getMethodParameterAnnotations().getNames()) .containsOnly(W.class.getName()); diff --git a/src/test/java/io/github/classgraph/features/MultiReleaseJar.java b/src/test/java/io/github/classgraph/features/MultiReleaseJarTest.java similarity index 95% rename from src/test/java/io/github/classgraph/features/MultiReleaseJar.java rename to src/test/java/io/github/classgraph/features/MultiReleaseJarTest.java index 186d74c37..665218b07 100644 --- a/src/test/java/io/github/classgraph/features/MultiReleaseJar.java +++ b/src/test/java/io/github/classgraph/features/MultiReleaseJarTest.java @@ -20,9 +20,9 @@ /** * MultiReleaseJar. */ -public class MultiReleaseJar { +public class MultiReleaseJarTest { /** The Constant jarURL. */ - private static final URL jarURL = MultiReleaseJar.class.getClassLoader().getResource("multi-release-jar.jar"); + private static final URL jarURL = MultiReleaseJarTest.class.getClassLoader().getResource("multi-release-jar.jar"); /** * Multi release jar. diff --git a/src/test/java/io/github/classgraph/issues/ResolveTypeVariable.java b/src/test/java/io/github/classgraph/issues/ResolveTypeVariableTest.java similarity index 79% rename from src/test/java/io/github/classgraph/issues/ResolveTypeVariable.java rename to src/test/java/io/github/classgraph/issues/ResolveTypeVariableTest.java index 81b40a1df..9147c9d2e 100644 --- a/src/test/java/io/github/classgraph/issues/ResolveTypeVariable.java +++ b/src/test/java/io/github/classgraph/issues/ResolveTypeVariableTest.java @@ -17,7 +17,7 @@ * @param * the generic type */ -public class ResolveTypeVariable> { +public class ResolveTypeVariableTest> { /** The list. */ T list; @@ -27,8 +27,8 @@ public class ResolveTypeVariable> { @Test public void test() { try (ScanResult scanResult = new ClassGraph() - .whitelistPackages(ResolveTypeVariable.class.getPackage().getName()).enableAllInfo().scan()) { - final FieldInfoList fields = scanResult.getClassInfo(ResolveTypeVariable.class.getName()) + .whitelistPackages(ResolveTypeVariableTest.class.getPackage().getName()).enableAllInfo().scan()) { + final FieldInfoList fields = scanResult.getClassInfo(ResolveTypeVariableTest.class.getName()) .getFieldInfo(); assertThat(((TypeVariableSignature) fields.get(0).getTypeSignature()).resolve().toString()) .isEqualTo("T extends java.util.ArrayList"); diff --git a/src/test/java/io/github/classgraph/issues/issue267/ClassLoadingWorksWithParentLastLoadersStub.java b/src/test/java/io/github/classgraph/issues/issue267/ClassLoadingWorksWithParentLastLoadersStubTest.java similarity index 98% rename from src/test/java/io/github/classgraph/issues/issue267/ClassLoadingWorksWithParentLastLoadersStub.java rename to src/test/java/io/github/classgraph/issues/issue267/ClassLoadingWorksWithParentLastLoadersStubTest.java index 9740139f3..0d155114c 100644 --- a/src/test/java/io/github/classgraph/issues/issue267/ClassLoadingWorksWithParentLastLoadersStub.java +++ b/src/test/java/io/github/classgraph/issues/issue267/ClassLoadingWorksWithParentLastLoadersStubTest.java @@ -41,7 +41,7 @@ /** * ClassLoadingWorksWithParentLastLoadersStub. */ -public class ClassLoadingWorksWithParentLastLoadersStub { +public class ClassLoadingWorksWithParentLastLoadersStubTest { /** * Same class loader that found A class should load it. From 566c0c704a3f8591c979ddfa98941c6623dc608b Mon Sep 17 00:00:00 2001 From: Ivan Sopov Date: Mon, 29 Jul 2019 17:37:28 +0300 Subject: [PATCH 0250/1778] migrate to junit5 --- pom.xml | 6 +-- src/test/java/DefaultPackageTest.java | 2 +- .../java/DisableRecursiveScanningTest.java | 2 +- src/test/java/com/xyz/MetaAnnotationTest.java | 32 +++++++------- .../features/AnnotationEquality.java | 6 +-- ...nnotationParamWithPrimitiveTypedArray.java | 2 +- .../features/DeclaredVsNonDeclared.java | 2 +- .../features/MethodParameterAnnotations.java | 2 +- .../classgraph/features/MultiReleaseJar.java | 2 +- .../issues/GenericInnerClassTypedField.java | 2 +- .../github/classgraph/issues/IssuesTest.java | 2 +- .../issues/ResolveTypeVariable.java | 2 +- .../TestGetUniqueClasspathElements.java | 6 +-- .../issues/issue100/Issue100Test.java | 2 +- .../issues/issue101/Issue101Test.java | 2 +- .../issues/issue107/Issue107Test.java | 2 +- .../issues/issue128/Issue128Test.java | 2 +- .../issues/issue140/Issue140Test.java | 2 +- .../issues/issue146/Issue146Test.java | 2 +- .../issues/issue148/Issue148Test.java | 2 +- .../issues/issue151/Issue151Test.java | 2 +- .../issues/issue152/Issue152Test.java | 2 +- .../issues/issue153/Issue153Test.java | 2 +- .../issues/issue166/Issue166Test.java | 2 +- .../issues/issue167/Issue167Test.java | 4 +- .../issues/issue171/Issue171Test.java | 2 +- .../issues/issue175/Issue175Test.java | 2 +- .../issues/issue193/Issue193Test.java | 2 +- .../issues/issue209/Issue209Test.java | 2 +- .../issues/issue216/Issue216Test.java | 2 +- .../issues/issue223/Issue223Test.java | 2 +- .../issues/issue238/Issue238Test.java | 2 +- .../issues/issue245/Issue245Test.java | 2 +- .../issues/issue246/Issue246Test.java | 4 +- .../issues/issue255/Issue255Test.java | 2 +- .../issues/issue260/Issue260Test.java | 2 +- .../issues/issue261/Issue261Test.java | 2 +- ...LoadingWorksWithParentLastLoadersStub.java | 2 +- .../issues/issue277/Issue227Test.java | 2 +- .../issues/issue286/Issue286Test.java | 6 ++- .../classgraph/issues/issue289/Issue289.java | 2 +- .../issues/issue303/Issue303Test.java | 2 +- .../classgraph/issues/issue305/Issue305.java | 2 +- .../classgraph/issues/issue310/Issue310.java | 2 +- .../classgraph/issues/issue314/Issue314.java | 2 +- .../classgraph/issues/issue318/Issue318.java | 2 +- .../classgraph/issues/issue329/Issue329.java | 2 +- .../classgraph/issues/issue339/Issue339.java | 2 +- .../classgraph/issues/issue340/Issue340.java | 2 +- .../classgraph/issues/issue345/Issue345.java | 2 +- .../classgraph/issues/issue348/Issue348.java | 2 +- .../classgraph/issues/issue350/Issue350.java | 2 +- .../classgraph/issues/issue352/Issue352.java | 2 +- .../classgraph/issues/issue355/Issue355.java | 2 +- .../issues/issue37/Issue37Test.java | 2 +- .../issues/issue38/Issue38Test.java | 6 +-- .../issues/issue46/Issue46Test.java | 2 +- .../issues/issue74/Issue74Test.java | 2 +- .../issues/issue78/Issue78Test.java | 2 +- .../issues/issue80/Issue80Test.java | 2 +- .../issues/issue83/Issue83Test.java | 2 +- .../classgraph/issues/issue93/Issue93.java | 2 +- .../issues/issue99/Issue99Test.java | 2 +- .../json/JSONSerializationTest.java | 2 +- .../classgraph/test/ClassGraphTest.java | 2 +- .../github/classgraph/test/ClassInfoTest.java | 10 ++--- .../AnnotationClassRefTest.java | 2 +- .../FieldAndMethodAnnotationTest.java | 2 +- .../test/fieldinfo/FieldInfoTest.java | 10 +++-- .../test/internal/InternalExternalTest.java | 2 +- .../MethodAnnotationTest.java | 2 +- .../TestMethodMetaAnnotation.java | 2 +- .../test/methodinfo/MethodInfoTest.java | 42 ++++++++----------- ...cyForFunctionParameterAnnotationsTest.java | 10 ++--- .../classgraph/test/utils/LogNodeTest.java | 4 +- .../classgraph/json/JSONParserTest.java | 2 +- 76 files changed, 135 insertions(+), 137 deletions(-) diff --git a/pom.xml b/pom.xml index 41421bb9f..fcb803702 100644 --- a/pom.xml +++ b/pom.xml @@ -359,9 +359,9 @@ - junit - junit - 4.13-beta-1 + org.junit.jupiter + junit-jupiter + 5.5.0 test diff --git a/src/test/java/DefaultPackageTest.java b/src/test/java/DefaultPackageTest.java index 0ce87ecf2..e3985b017 100644 --- a/src/test/java/DefaultPackageTest.java +++ b/src/test/java/DefaultPackageTest.java @@ -32,7 +32,7 @@ import java.util.List; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; diff --git a/src/test/java/DisableRecursiveScanningTest.java b/src/test/java/DisableRecursiveScanningTest.java index e5c2d99b5..c351b2cd0 100644 --- a/src/test/java/DisableRecursiveScanningTest.java +++ b/src/test/java/DisableRecursiveScanningTest.java @@ -32,7 +32,7 @@ import java.util.List; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; diff --git a/src/test/java/com/xyz/MetaAnnotationTest.java b/src/test/java/com/xyz/MetaAnnotationTest.java index 87b6c3ea6..a1dfdf74d 100644 --- a/src/test/java/com/xyz/MetaAnnotationTest.java +++ b/src/test/java/com/xyz/MetaAnnotationTest.java @@ -30,9 +30,9 @@ import static org.assertj.core.api.Assertions.assertThat; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; @@ -40,15 +40,15 @@ /** * MetaAnnotationTest. */ -public class MetaAnnotationTest { +class MetaAnnotationTest { /** The scan result. */ static ScanResult scanResult; /** * Setup. */ - @BeforeClass - public static void setUp() { + @BeforeAll + static void setUp() { scanResult = new ClassGraph().whitelistPackages("com.xyz.meta").enableClassInfo().enableAnnotationInfo() .scan(); } @@ -56,8 +56,8 @@ public static void setUp() { /** * Teardown. */ - @AfterClass - public static void tearDown() { + @AfterAll + static void tearDown() { scanResult.close(); scanResult = null; } @@ -66,7 +66,7 @@ public static void tearDown() { * One level. */ @Test - public void oneLevel() { + void oneLevel() { assertThat(scanResult.getClassesWithAnnotation("com.xyz.meta.E").directOnly().getNames()) .containsExactlyInAnyOrder("com.xyz.meta.B"); assertThat(scanResult.getClassesWithAnnotation("com.xyz.meta.F").directOnly().getNames()) @@ -79,7 +79,7 @@ public void oneLevel() { * Two levels. */ @Test - public void twoLevels() { + void twoLevels() { assertThat(scanResult.getClassesWithAnnotation("com.xyz.meta.J").getNames()) .containsExactlyInAnyOrder("com.xyz.meta.F", "com.xyz.meta.E", "com.xyz.meta.B", "com.xyz.meta.A"); } @@ -88,7 +88,7 @@ public void twoLevels() { * Three levels. */ @Test - public void threeLevels() { + void threeLevels() { assertThat(scanResult.getClassesWithAnnotation("com.xyz.meta.L").getNames()) .containsExactlyInAnyOrder("com.xyz.meta.I", "com.xyz.meta.E", "com.xyz.meta.B", "com.xyz.meta.H"); } @@ -97,7 +97,7 @@ public void threeLevels() { * Across cycle. */ @Test - public void acrossCycle() { + void acrossCycle() { assertThat(scanResult.getClassesWithAnnotation("com.xyz.meta.H").directOnly().getNames()) .containsExactlyInAnyOrder("com.xyz.meta.I"); assertThat(scanResult.getAnnotationsOnClass("com.xyz.meta.H").directOnly().getNames()) @@ -118,7 +118,7 @@ public void acrossCycle() { * Cycle annotates self. */ @Test - public void cycleAnnotatesSelf() { + void cycleAnnotatesSelf() { assertThat(scanResult.getClassesWithAnnotation("com.xyz.meta.I").getNames()) .containsExactlyInAnyOrder("com.xyz.meta.E", "com.xyz.meta.B", "com.xyz.meta.H", "com.xyz.meta.I"); } @@ -127,7 +127,7 @@ public void cycleAnnotatesSelf() { * Names of meta annotations. */ @Test - public void namesOfMetaAnnotations() { + void namesOfMetaAnnotations() { assertThat(scanResult.getAnnotationsOnClass("com.xyz.meta.A").getNames()) .containsExactlyInAnyOrder("com.xyz.meta.J", "com.xyz.meta.F"); assertThat(scanResult.getAnnotationsOnClass("com.xyz.meta.C").getNames()) @@ -138,7 +138,7 @@ public void namesOfMetaAnnotations() { * Union. */ @Test - public void union() { + void union() { assertThat(scanResult.getClassesWithAnnotation("com.xyz.meta.J") .union(scanResult.getClassesWithAnnotation("com.xyz.meta.G")).directOnly().getNames()) .containsExactlyInAnyOrder("com.xyz.meta.E", "com.xyz.meta.F", "com.xyz.meta.C"); @@ -155,7 +155,7 @@ public void union() { * Intersect. */ @Test - public void intersect() { + void intersect() { assertThat(scanResult.getClassesWithAnnotation("com.xyz.meta.I") .intersect(scanResult.getClassesWithAnnotation("com.xyz.meta.J")).getNames()) .containsExactlyInAnyOrder("com.xyz.meta.E", "com.xyz.meta.B"); diff --git a/src/test/java/io/github/classgraph/features/AnnotationEquality.java b/src/test/java/io/github/classgraph/features/AnnotationEquality.java index 45c96fe0a..e91b27f9a 100644 --- a/src/test/java/io/github/classgraph/features/AnnotationEquality.java +++ b/src/test/java/io/github/classgraph/features/AnnotationEquality.java @@ -6,7 +6,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.AnnotationInfo; import io.github.classgraph.ClassGraph; @@ -16,7 +16,7 @@ /** * AnnotationEquality. */ -public class AnnotationEquality { +class AnnotationEquality { /** * The Interface W. */ @@ -82,7 +82,7 @@ private static class Y { * Test equality of JRE-instantiated Annotation with proxy instance instantiated by ClassGraph. */ @Test - public void annotationEquality() { + void annotationEquality() { try (ScanResult scanResult = new ClassGraph() .whitelistPackages(AnnotationEquality.class.getPackage().getName()).enableAllInfo().scan()) { final ClassInfo classInfo = scanResult.getClassInfo(Y.class.getName()); diff --git a/src/test/java/io/github/classgraph/features/AnnotationParamWithPrimitiveTypedArray.java b/src/test/java/io/github/classgraph/features/AnnotationParamWithPrimitiveTypedArray.java index a9ddf1c25..91c70b07e 100644 --- a/src/test/java/io/github/classgraph/features/AnnotationParamWithPrimitiveTypedArray.java +++ b/src/test/java/io/github/classgraph/features/AnnotationParamWithPrimitiveTypedArray.java @@ -6,7 +6,7 @@ import java.lang.annotation.RetentionPolicy; import java.util.Arrays; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.AnnotationInfo; import io.github.classgraph.AnnotationParameterValueList; diff --git a/src/test/java/io/github/classgraph/features/DeclaredVsNonDeclared.java b/src/test/java/io/github/classgraph/features/DeclaredVsNonDeclared.java index d33fcc0de..95e676ec4 100644 --- a/src/test/java/io/github/classgraph/features/DeclaredVsNonDeclared.java +++ b/src/test/java/io/github/classgraph/features/DeclaredVsNonDeclared.java @@ -7,7 +7,7 @@ import java.lang.annotation.RetentionPolicy; import org.assertj.core.api.iterable.Extractor; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.AnnotationInfo; import io.github.classgraph.AnnotationInfoList; diff --git a/src/test/java/io/github/classgraph/features/MethodParameterAnnotations.java b/src/test/java/io/github/classgraph/features/MethodParameterAnnotations.java index 9ae7849ad..f81f2114b 100644 --- a/src/test/java/io/github/classgraph/features/MethodParameterAnnotations.java +++ b/src/test/java/io/github/classgraph/features/MethodParameterAnnotations.java @@ -5,7 +5,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; diff --git a/src/test/java/io/github/classgraph/features/MultiReleaseJar.java b/src/test/java/io/github/classgraph/features/MultiReleaseJar.java index 186d74c37..539f74884 100644 --- a/src/test/java/io/github/classgraph/features/MultiReleaseJar.java +++ b/src/test/java/io/github/classgraph/features/MultiReleaseJar.java @@ -7,7 +7,7 @@ import java.net.URL; import java.net.URLClassLoader; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ClassInfo; diff --git a/src/test/java/io/github/classgraph/issues/GenericInnerClassTypedField.java b/src/test/java/io/github/classgraph/issues/GenericInnerClassTypedField.java index 904364705..2572eb74c 100644 --- a/src/test/java/io/github/classgraph/issues/GenericInnerClassTypedField.java +++ b/src/test/java/io/github/classgraph/issues/GenericInnerClassTypedField.java @@ -2,7 +2,7 @@ import static org.assertj.core.api.Assertions.assertThat; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ClassRefTypeSignature; diff --git a/src/test/java/io/github/classgraph/issues/IssuesTest.java b/src/test/java/io/github/classgraph/issues/IssuesTest.java index 089e097cd..ff92c32de 100644 --- a/src/test/java/io/github/classgraph/issues/IssuesTest.java +++ b/src/test/java/io/github/classgraph/issues/IssuesTest.java @@ -2,7 +2,7 @@ import static org.assertj.core.api.Assertions.assertThat; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; diff --git a/src/test/java/io/github/classgraph/issues/ResolveTypeVariable.java b/src/test/java/io/github/classgraph/issues/ResolveTypeVariable.java index 81b40a1df..00220a86a 100644 --- a/src/test/java/io/github/classgraph/issues/ResolveTypeVariable.java +++ b/src/test/java/io/github/classgraph/issues/ResolveTypeVariable.java @@ -4,7 +4,7 @@ import java.util.ArrayList; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.FieldInfoList; diff --git a/src/test/java/io/github/classgraph/issues/TestGetUniqueClasspathElements.java b/src/test/java/io/github/classgraph/issues/TestGetUniqueClasspathElements.java index 7fc40ddf9..03e64408a 100644 --- a/src/test/java/io/github/classgraph/issues/TestGetUniqueClasspathElements.java +++ b/src/test/java/io/github/classgraph/issues/TestGetUniqueClasspathElements.java @@ -5,19 +5,19 @@ import java.io.File; import java.util.List; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; /** * TestGetUniqueClasspathElements. */ -public class TestGetUniqueClasspathElements { +class TestGetUniqueClasspathElements { /** * Test get unique classpath elements. */ @Test - public void testGetUniqueClasspathElements() { + void testGetUniqueClasspathElements() { final List classpathElements = new ClassGraph().whitelistPackages("com.xyz").getClasspathFiles(); assertThat(classpathElements).isNotEmpty(); } diff --git a/src/test/java/io/github/classgraph/issues/issue100/Issue100Test.java b/src/test/java/io/github/classgraph/issues/issue100/Issue100Test.java index 3ee6d0e1f..c324fd0ea 100644 --- a/src/test/java/io/github/classgraph/issues/issue100/Issue100Test.java +++ b/src/test/java/io/github/classgraph/issues/issue100/Issue100Test.java @@ -34,7 +34,7 @@ import java.net.URLClassLoader; import java.util.ArrayList; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ClassInfo; 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 f3bf0f557..44bdbc1b4 100644 --- a/src/test/java/io/github/classgraph/issues/issue101/Issue101Test.java +++ b/src/test/java/io/github/classgraph/issues/issue101/Issue101Test.java @@ -30,7 +30,7 @@ import static org.assertj.core.api.Assertions.assertThat; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; 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 ce4120f50..c56388451 100644 --- a/src/test/java/io/github/classgraph/issues/issue107/Issue107Test.java +++ b/src/test/java/io/github/classgraph/issues/issue107/Issue107Test.java @@ -32,7 +32,7 @@ import java.util.Arrays; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; diff --git a/src/test/java/io/github/classgraph/issues/issue128/Issue128Test.java b/src/test/java/io/github/classgraph/issues/issue128/Issue128Test.java index 1455cb75f..71e034115 100644 --- a/src/test/java/io/github/classgraph/issues/issue128/Issue128Test.java +++ b/src/test/java/io/github/classgraph/issues/issue128/Issue128Test.java @@ -36,7 +36,7 @@ import java.net.URLClassLoader; import java.util.List; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; diff --git a/src/test/java/io/github/classgraph/issues/issue140/Issue140Test.java b/src/test/java/io/github/classgraph/issues/issue140/Issue140Test.java index 2cb83c9db..93664e8e1 100644 --- a/src/test/java/io/github/classgraph/issues/issue140/Issue140Test.java +++ b/src/test/java/io/github/classgraph/issues/issue140/Issue140Test.java @@ -30,7 +30,7 @@ import static org.assertj.core.api.Assertions.assertThat; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ArrayTypeSignature; import io.github.classgraph.BaseTypeSignature; diff --git a/src/test/java/io/github/classgraph/issues/issue146/Issue146Test.java b/src/test/java/io/github/classgraph/issues/issue146/Issue146Test.java index 766423d19..435bf047b 100644 --- a/src/test/java/io/github/classgraph/issues/issue146/Issue146Test.java +++ b/src/test/java/io/github/classgraph/issues/issue146/Issue146Test.java @@ -30,7 +30,7 @@ import static org.assertj.core.api.Assertions.assertThat; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ClassInfo; diff --git a/src/test/java/io/github/classgraph/issues/issue148/Issue148Test.java b/src/test/java/io/github/classgraph/issues/issue148/Issue148Test.java index 205f77c07..2c814b25a 100644 --- a/src/test/java/io/github/classgraph/issues/issue148/Issue148Test.java +++ b/src/test/java/io/github/classgraph/issues/issue148/Issue148Test.java @@ -30,7 +30,7 @@ import static org.assertj.core.api.Assertions.assertThat; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ClassInfo; 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 cd557cafd..54c3c1cb0 100644 --- a/src/test/java/io/github/classgraph/issues/issue151/Issue151Test.java +++ b/src/test/java/io/github/classgraph/issues/issue151/Issue151Test.java @@ -35,7 +35,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.MethodInfo; 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 a883d86c4..1aa03d145 100644 --- a/src/test/java/io/github/classgraph/issues/issue152/Issue152Test.java +++ b/src/test/java/io/github/classgraph/issues/issue152/Issue152Test.java @@ -34,7 +34,7 @@ import java.util.Map; import java.util.Set; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ClassInfo; diff --git a/src/test/java/io/github/classgraph/issues/issue153/Issue153Test.java b/src/test/java/io/github/classgraph/issues/issue153/Issue153Test.java index fc1f18255..debc51f84 100644 --- a/src/test/java/io/github/classgraph/issues/issue153/Issue153Test.java +++ b/src/test/java/io/github/classgraph/issues/issue153/Issue153Test.java @@ -33,7 +33,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.AnnotationEnumValue; import io.github.classgraph.AnnotationInfo; diff --git a/src/test/java/io/github/classgraph/issues/issue166/Issue166Test.java b/src/test/java/io/github/classgraph/issues/issue166/Issue166Test.java index 952d92299..eb49d80f6 100644 --- a/src/test/java/io/github/classgraph/issues/issue166/Issue166Test.java +++ b/src/test/java/io/github/classgraph/issues/issue166/Issue166Test.java @@ -32,7 +32,7 @@ import java.net.URL; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; diff --git a/src/test/java/io/github/classgraph/issues/issue167/Issue167Test.java b/src/test/java/io/github/classgraph/issues/issue167/Issue167Test.java index 92099cf8a..187ba83ab 100644 --- a/src/test/java/io/github/classgraph/issues/issue167/Issue167Test.java +++ b/src/test/java/io/github/classgraph/issues/issue167/Issue167Test.java @@ -28,14 +28,14 @@ */ package io.github.classgraph.issues.issue167; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; 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 dff45ea48..0a882268f 100644 --- a/src/test/java/io/github/classgraph/issues/issue171/Issue171Test.java +++ b/src/test/java/io/github/classgraph/issues/issue171/Issue171Test.java @@ -5,7 +5,7 @@ import java.net.URL; import java.util.List; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; 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 ee847575a..a019d938d 100644 --- a/src/test/java/io/github/classgraph/issues/issue175/Issue175Test.java +++ b/src/test/java/io/github/classgraph/issues/issue175/Issue175Test.java @@ -35,7 +35,7 @@ import java.util.ArrayList; import java.util.List; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ClassInfo; diff --git a/src/test/java/io/github/classgraph/issues/issue193/Issue193Test.java b/src/test/java/io/github/classgraph/issues/issue193/Issue193Test.java index cc51db52f..6a34fef0a 100644 --- a/src/test/java/io/github/classgraph/issues/issue193/Issue193Test.java +++ b/src/test/java/io/github/classgraph/issues/issue193/Issue193Test.java @@ -36,7 +36,7 @@ import java.net.URLClassLoader; import java.util.List; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.ops4j.pax.url.mvn.MavenResolvers; import io.github.classgraph.ClassGraph; diff --git a/src/test/java/io/github/classgraph/issues/issue209/Issue209Test.java b/src/test/java/io/github/classgraph/issues/issue209/Issue209Test.java index afa681fd5..744e822e2 100644 --- a/src/test/java/io/github/classgraph/issues/issue209/Issue209Test.java +++ b/src/test/java/io/github/classgraph/issues/issue209/Issue209Test.java @@ -33,7 +33,7 @@ import java.net.URL; import java.net.URLClassLoader; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; 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 2016afcfd..6d90441c6 100644 --- a/src/test/java/io/github/classgraph/issues/issue216/Issue216Test.java +++ b/src/test/java/io/github/classgraph/issues/issue216/Issue216Test.java @@ -32,7 +32,7 @@ import javax.persistence.Entity; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ClassInfo; diff --git a/src/test/java/io/github/classgraph/issues/issue223/Issue223Test.java b/src/test/java/io/github/classgraph/issues/issue223/Issue223Test.java index 4ad569200..354da2f22 100644 --- a/src/test/java/io/github/classgraph/issues/issue223/Issue223Test.java +++ b/src/test/java/io/github/classgraph/issues/issue223/Issue223Test.java @@ -32,7 +32,7 @@ import javax.persistence.Entity; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ClassInfo; diff --git a/src/test/java/io/github/classgraph/issues/issue238/Issue238Test.java b/src/test/java/io/github/classgraph/issues/issue238/Issue238Test.java index 5ee269250..3e6986c9d 100644 --- a/src/test/java/io/github/classgraph/issues/issue238/Issue238Test.java +++ b/src/test/java/io/github/classgraph/issues/issue238/Issue238Test.java @@ -34,7 +34,7 @@ import javax.persistence.Entity; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; diff --git a/src/test/java/io/github/classgraph/issues/issue245/Issue245Test.java b/src/test/java/io/github/classgraph/issues/issue245/Issue245Test.java index cd7460a22..29067e010 100644 --- a/src/test/java/io/github/classgraph/issues/issue245/Issue245Test.java +++ b/src/test/java/io/github/classgraph/issues/issue245/Issue245Test.java @@ -32,7 +32,7 @@ import java.net.URL; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; diff --git a/src/test/java/io/github/classgraph/issues/issue246/Issue246Test.java b/src/test/java/io/github/classgraph/issues/issue246/Issue246Test.java index 532853bab..17ee2234f 100644 --- a/src/test/java/io/github/classgraph/issues/issue246/Issue246Test.java +++ b/src/test/java/io/github/classgraph/issues/issue246/Issue246Test.java @@ -28,9 +28,9 @@ */ package io.github.classgraph.issues.issue246; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; diff --git a/src/test/java/io/github/classgraph/issues/issue255/Issue255Test.java b/src/test/java/io/github/classgraph/issues/issue255/Issue255Test.java index 0cd9d3476..a417f9210 100644 --- a/src/test/java/io/github/classgraph/issues/issue255/Issue255Test.java +++ b/src/test/java/io/github/classgraph/issues/issue255/Issue255Test.java @@ -30,7 +30,7 @@ import static org.assertj.core.api.Assertions.assertThat; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.Resource; diff --git a/src/test/java/io/github/classgraph/issues/issue260/Issue260Test.java b/src/test/java/io/github/classgraph/issues/issue260/Issue260Test.java index e6ed82778..f437c0591 100644 --- a/src/test/java/io/github/classgraph/issues/issue260/Issue260Test.java +++ b/src/test/java/io/github/classgraph/issues/issue260/Issue260Test.java @@ -30,7 +30,7 @@ import static org.assertj.core.api.Assertions.assertThat; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; 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 6df236b87..a5cf8c23d 100644 --- a/src/test/java/io/github/classgraph/issues/issue261/Issue261Test.java +++ b/src/test/java/io/github/classgraph/issues/issue261/Issue261Test.java @@ -30,7 +30,7 @@ import static org.assertj.core.api.Assertions.assertThat; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; diff --git a/src/test/java/io/github/classgraph/issues/issue267/ClassLoadingWorksWithParentLastLoadersStub.java b/src/test/java/io/github/classgraph/issues/issue267/ClassLoadingWorksWithParentLastLoadersStub.java index 9740139f3..4204c4c86 100644 --- a/src/test/java/io/github/classgraph/issues/issue267/ClassLoadingWorksWithParentLastLoadersStub.java +++ b/src/test/java/io/github/classgraph/issues/issue267/ClassLoadingWorksWithParentLastLoadersStub.java @@ -34,7 +34,7 @@ import java.lang.reflect.Method; import java.net.URL; -import org.junit.Test; +import org.junit.jupiter.api.Test; import com.xyz.meta.A; diff --git a/src/test/java/io/github/classgraph/issues/issue277/Issue227Test.java b/src/test/java/io/github/classgraph/issues/issue277/Issue227Test.java index 722c3f543..355157d63 100644 --- a/src/test/java/io/github/classgraph/issues/issue277/Issue227Test.java +++ b/src/test/java/io/github/classgraph/issues/issue277/Issue227Test.java @@ -1,6 +1,6 @@ package io.github.classgraph.issues.issue277; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; diff --git a/src/test/java/io/github/classgraph/issues/issue286/Issue286Test.java b/src/test/java/io/github/classgraph/issues/issue286/Issue286Test.java index 7b315ba89..15723912b 100644 --- a/src/test/java/io/github/classgraph/issues/issue286/Issue286Test.java +++ b/src/test/java/io/github/classgraph/issues/issue286/Issue286Test.java @@ -32,10 +32,11 @@ import java.net.URL; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; +import org.junit.jupiter.api.Timeout; /** * Issue286Test. @@ -44,7 +45,8 @@ public class Issue286Test { /** * Issue 286 test. */ - @Test(timeout = 1000) + @Test + @Timeout(value = 1) public void issue286Test() { final URL jarURL = getClass().getClassLoader().getResource("issue286.jar"); assertThat(jarURL).isNotNull(); diff --git a/src/test/java/io/github/classgraph/issues/issue289/Issue289.java b/src/test/java/io/github/classgraph/issues/issue289/Issue289.java index 14374b15d..744448a03 100644 --- a/src/test/java/io/github/classgraph/issues/issue289/Issue289.java +++ b/src/test/java/io/github/classgraph/issues/issue289/Issue289.java @@ -5,7 +5,7 @@ import java.net.URL; import java.net.URLClassLoader; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ResourceList; diff --git a/src/test/java/io/github/classgraph/issues/issue303/Issue303Test.java b/src/test/java/io/github/classgraph/issues/issue303/Issue303Test.java index 3dcdcfd7e..885c5485f 100644 --- a/src/test/java/io/github/classgraph/issues/issue303/Issue303Test.java +++ b/src/test/java/io/github/classgraph/issues/issue303/Issue303Test.java @@ -32,7 +32,7 @@ import java.util.List; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; 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 6475ac124..d849cdbbe 100644 --- a/src/test/java/io/github/classgraph/issues/issue305/Issue305.java +++ b/src/test/java/io/github/classgraph/issues/issue305/Issue305.java @@ -10,7 +10,7 @@ import java.util.logging.Level; import java.util.logging.Logger; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; diff --git a/src/test/java/io/github/classgraph/issues/issue310/Issue310.java b/src/test/java/io/github/classgraph/issues/issue310/Issue310.java index c9dcc46b3..07316d763 100644 --- a/src/test/java/io/github/classgraph/issues/issue310/Issue310.java +++ b/src/test/java/io/github/classgraph/issues/issue310/Issue310.java @@ -2,7 +2,7 @@ import static org.assertj.core.api.Assertions.assertThat; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; 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 7edd325df..de8009fc6 100644 --- a/src/test/java/io/github/classgraph/issues/issue314/Issue314.java +++ b/src/test/java/io/github/classgraph/issues/issue314/Issue314.java @@ -2,7 +2,7 @@ import static org.assertj.core.api.Assertions.assertThat; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; 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 f7b3017b3..b47446330 100644 --- a/src/test/java/io/github/classgraph/issues/issue318/Issue318.java +++ b/src/test/java/io/github/classgraph/issues/issue318/Issue318.java @@ -8,7 +8,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; diff --git a/src/test/java/io/github/classgraph/issues/issue329/Issue329.java b/src/test/java/io/github/classgraph/issues/issue329/Issue329.java index 26e179244..97e790db0 100644 --- a/src/test/java/io/github/classgraph/issues/issue329/Issue329.java +++ b/src/test/java/io/github/classgraph/issues/issue329/Issue329.java @@ -2,7 +2,7 @@ import static org.assertj.core.api.Assertions.assertThat; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ClassInfo; diff --git a/src/test/java/io/github/classgraph/issues/issue339/Issue339.java b/src/test/java/io/github/classgraph/issues/issue339/Issue339.java index df21843ce..e2e0e10d9 100644 --- a/src/test/java/io/github/classgraph/issues/issue339/Issue339.java +++ b/src/test/java/io/github/classgraph/issues/issue339/Issue339.java @@ -8,7 +8,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.AnnotationParameterValueList; import io.github.classgraph.ClassGraph; diff --git a/src/test/java/io/github/classgraph/issues/issue340/Issue340.java b/src/test/java/io/github/classgraph/issues/issue340/Issue340.java index 710cdf996..c4642613c 100644 --- a/src/test/java/io/github/classgraph/issues/issue340/Issue340.java +++ b/src/test/java/io/github/classgraph/issues/issue340/Issue340.java @@ -4,7 +4,7 @@ import java.util.stream.Collectors; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.Resource; diff --git a/src/test/java/io/github/classgraph/issues/issue345/Issue345.java b/src/test/java/io/github/classgraph/issues/issue345/Issue345.java index c5e905ff6..ac37631a4 100644 --- a/src/test/java/io/github/classgraph/issues/issue345/Issue345.java +++ b/src/test/java/io/github/classgraph/issues/issue345/Issue345.java @@ -5,7 +5,7 @@ import java.net.URL; import java.net.URLClassLoader; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ClassInfo; diff --git a/src/test/java/io/github/classgraph/issues/issue348/Issue348.java b/src/test/java/io/github/classgraph/issues/issue348/Issue348.java index 87c5b6454..b4e5df412 100644 --- a/src/test/java/io/github/classgraph/issues/issue348/Issue348.java +++ b/src/test/java/io/github/classgraph/issues/issue348/Issue348.java @@ -8,7 +8,7 @@ import java.util.Set; import java.util.stream.Collectors; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; 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 ff27d9372..a9554527a 100644 --- a/src/test/java/io/github/classgraph/issues/issue350/Issue350.java +++ b/src/test/java/io/github/classgraph/issues/issue350/Issue350.java @@ -5,7 +5,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; diff --git a/src/test/java/io/github/classgraph/issues/issue352/Issue352.java b/src/test/java/io/github/classgraph/issues/issue352/Issue352.java index 591acd57b..2ddd8d290 100644 --- a/src/test/java/io/github/classgraph/issues/issue352/Issue352.java +++ b/src/test/java/io/github/classgraph/issues/issue352/Issue352.java @@ -5,7 +5,7 @@ import java.io.File; import java.io.IOException; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.ops4j.pax.url.mvn.MavenResolvers; import io.github.classgraph.ClassGraph; diff --git a/src/test/java/io/github/classgraph/issues/issue355/Issue355.java b/src/test/java/io/github/classgraph/issues/issue355/Issue355.java index 775a60a57..6717d5a22 100644 --- a/src/test/java/io/github/classgraph/issues/issue355/Issue355.java +++ b/src/test/java/io/github/classgraph/issues/issue355/Issue355.java @@ -6,7 +6,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.AnnotationClassRef; import io.github.classgraph.ArrayClassInfo; diff --git a/src/test/java/io/github/classgraph/issues/issue37/Issue37Test.java b/src/test/java/io/github/classgraph/issues/issue37/Issue37Test.java index 44aa694e1..b46fd2190 100644 --- a/src/test/java/io/github/classgraph/issues/issue37/Issue37Test.java +++ b/src/test/java/io/github/classgraph/issues/issue37/Issue37Test.java @@ -33,7 +33,7 @@ import java.util.ArrayList; import java.util.List; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ClassInfo; 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 e9890016e..4459e3d01 100644 --- a/src/test/java/io/github/classgraph/issues/issue38/Issue38Test.java +++ b/src/test/java/io/github/classgraph/issues/issue38/Issue38Test.java @@ -4,7 +4,7 @@ import java.lang.annotation.Annotation; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; @@ -12,7 +12,7 @@ /** * Issue38Test. */ -public class Issue38Test { +class Issue38Test { /** * The Class AnnotationLiteral. * @@ -26,7 +26,7 @@ public static abstract class AnnotationLiteral implements * Test implements suppress warnings. */ @Test - public void testImplementsSuppressWarnings() { + void testImplementsSuppressWarnings() { try (ScanResult scanResult = new ClassGraph().whitelistPackages(Issue38Test.class.getPackage().getName()) .scan()) { assertThat(scanResult.getClassesImplementing(SuppressWarnings.class.getName()).getNames()) 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 6cd4e3031..8c8bb3d77 100644 --- a/src/test/java/io/github/classgraph/issues/issue46/Issue46Test.java +++ b/src/test/java/io/github/classgraph/issues/issue46/Issue46Test.java @@ -30,7 +30,7 @@ import static org.assertj.core.api.Assertions.assertThat; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; 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 f5c578f0d..66491f978 100644 --- a/src/test/java/io/github/classgraph/issues/issue74/Issue74Test.java +++ b/src/test/java/io/github/classgraph/issues/issue74/Issue74Test.java @@ -2,7 +2,7 @@ import static org.assertj.core.api.Assertions.assertThat; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; diff --git a/src/test/java/io/github/classgraph/issues/issue78/Issue78Test.java b/src/test/java/io/github/classgraph/issues/issue78/Issue78Test.java index e1a2d2f23..83daadbb0 100644 --- a/src/test/java/io/github/classgraph/issues/issue78/Issue78Test.java +++ b/src/test/java/io/github/classgraph/issues/issue78/Issue78Test.java @@ -2,7 +2,7 @@ import static org.assertj.core.api.Assertions.assertThat; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; diff --git a/src/test/java/io/github/classgraph/issues/issue80/Issue80Test.java b/src/test/java/io/github/classgraph/issues/issue80/Issue80Test.java index f4d913b80..99d904c66 100644 --- a/src/test/java/io/github/classgraph/issues/issue80/Issue80Test.java +++ b/src/test/java/io/github/classgraph/issues/issue80/Issue80Test.java @@ -2,7 +2,7 @@ import static org.assertj.core.api.Assertions.assertThat; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; diff --git a/src/test/java/io/github/classgraph/issues/issue83/Issue83Test.java b/src/test/java/io/github/classgraph/issues/issue83/Issue83Test.java index 8494de34b..73e284341 100644 --- a/src/test/java/io/github/classgraph/issues/issue83/Issue83Test.java +++ b/src/test/java/io/github/classgraph/issues/issue83/Issue83Test.java @@ -6,7 +6,7 @@ import java.util.ArrayList; import java.util.List; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.Resource; 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 c2479fe45..2ffdb81b9 100644 --- a/src/test/java/io/github/classgraph/issues/issue93/Issue93.java +++ b/src/test/java/io/github/classgraph/issues/issue93/Issue93.java @@ -5,7 +5,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; diff --git a/src/test/java/io/github/classgraph/issues/issue99/Issue99Test.java b/src/test/java/io/github/classgraph/issues/issue99/Issue99Test.java index 187b49444..cf3c73cc2 100644 --- a/src/test/java/io/github/classgraph/issues/issue99/Issue99Test.java +++ b/src/test/java/io/github/classgraph/issues/issue99/Issue99Test.java @@ -30,7 +30,7 @@ import static org.assertj.core.api.Assertions.assertThat; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; diff --git a/src/test/java/io/github/classgraph/json/JSONSerializationTest.java b/src/test/java/io/github/classgraph/json/JSONSerializationTest.java index 2245fbd8c..d6ccd0de6 100644 --- a/src/test/java/io/github/classgraph/json/JSONSerializationTest.java +++ b/src/test/java/io/github/classgraph/json/JSONSerializationTest.java @@ -7,7 +7,7 @@ import java.util.List; import java.util.Map; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; diff --git a/src/test/java/io/github/classgraph/test/ClassGraphTest.java b/src/test/java/io/github/classgraph/test/ClassGraphTest.java index 088019671..c920abb10 100644 --- a/src/test/java/io/github/classgraph/test/ClassGraphTest.java +++ b/src/test/java/io/github/classgraph/test/ClassGraphTest.java @@ -34,7 +34,7 @@ import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.FieldInfo; diff --git a/src/test/java/io/github/classgraph/test/ClassInfoTest.java b/src/test/java/io/github/classgraph/test/ClassInfoTest.java index 46394a3af..91b5f1ef0 100644 --- a/src/test/java/io/github/classgraph/test/ClassInfoTest.java +++ b/src/test/java/io/github/classgraph/test/ClassInfoTest.java @@ -4,9 +4,9 @@ import java.util.List; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ClassInfo; @@ -34,7 +34,7 @@ public class ClassInfoTest { /** * Setup. */ - @BeforeClass + @BeforeAll public static void setup() { scanResult = new ClassGraph().whitelistPackages(Impl1.class.getPackage().getName()).scan(); } @@ -42,7 +42,7 @@ public static void setup() { /** * Teardown. */ - @AfterClass + @AfterAll public static void teardown() { scanResult.close(); scanResult = null; 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 79b13b439..8379a03b5 100644 --- a/src/test/java/io/github/classgraph/test/classrefannotation/AnnotationClassRefTest.java +++ b/src/test/java/io/github/classgraph/test/classrefannotation/AnnotationClassRefTest.java @@ -34,7 +34,7 @@ import java.lang.annotation.RetentionPolicy; import java.util.List; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.AnnotationClassRef; import io.github.classgraph.AnnotationInfo; 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 b0ebeebc3..a35d59b9d 100644 --- a/src/test/java/io/github/classgraph/test/fieldannotation/FieldAndMethodAnnotationTest.java +++ b/src/test/java/io/github/classgraph/test/fieldannotation/FieldAndMethodAnnotationTest.java @@ -32,7 +32,7 @@ import java.util.List; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; diff --git a/src/test/java/io/github/classgraph/test/fieldinfo/FieldInfoTest.java b/src/test/java/io/github/classgraph/test/fieldinfo/FieldInfoTest.java index bff7a4daf..b2ca26e40 100644 --- a/src/test/java/io/github/classgraph/test/fieldinfo/FieldInfoTest.java +++ b/src/test/java/io/github/classgraph/test/fieldinfo/FieldInfoTest.java @@ -32,7 +32,8 @@ import java.util.List; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; @@ -56,11 +57,14 @@ public class FieldInfoTest { /** * Field info not enabled. */ - @Test(expected = IllegalArgumentException.class) + @Test public void fieldInfoNotEnabled() { try (ScanResult scanResult = new ClassGraph().whitelistPackages(FieldInfoTest.class.getPackage().getName()) .scan()) { - scanResult.getClassInfo(FieldInfoTest.class.getName()).getFieldInfo(); + Assertions.assertThrows( + IllegalArgumentException.class, + () -> scanResult.getClassInfo(FieldInfoTest.class.getName()).getFieldInfo() + ); } } 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 cdffd96ba..716dc45da 100644 --- a/src/test/java/io/github/classgraph/test/internal/InternalExternalTest.java +++ b/src/test/java/io/github/classgraph/test/internal/InternalExternalTest.java @@ -2,7 +2,7 @@ import static org.assertj.core.api.Assertions.assertThat; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; 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 ea2e5c10d..e1580f080 100644 --- a/src/test/java/io/github/classgraph/test/methodannotation/MethodAnnotationTest.java +++ b/src/test/java/io/github/classgraph/test/methodannotation/MethodAnnotationTest.java @@ -32,7 +32,7 @@ import java.util.List; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ClassInfo; 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 bb3610579..3a0ad3565 100644 --- a/src/test/java/io/github/classgraph/test/methodannotation2/TestMethodMetaAnnotation.java +++ b/src/test/java/io/github/classgraph/test/methodannotation2/TestMethodMetaAnnotation.java @@ -36,7 +36,7 @@ import java.lang.annotation.Target; import java.util.List; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; 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 07c0966ee..c1ab2d1be 100644 --- a/src/test/java/io/github/classgraph/test/methodinfo/MethodInfoTest.java +++ b/src/test/java/io/github/classgraph/test/methodinfo/MethodInfoTest.java @@ -34,7 +34,8 @@ import java.util.ArrayList; import java.util.List; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import io.github.classgraph.ArrayClassInfo; import io.github.classgraph.ArrayTypeSignature; @@ -99,12 +100,15 @@ private static String[] privateMethod() { /** * Method info not enabled. */ - @Test(expected = IllegalArgumentException.class) + @Test public void methodInfoNotEnabled() { // .enableSaveMethodInfo() not called try (ScanResult scanResult = new ClassGraph().whitelistPackages(MethodInfoTest.class.getPackage().getName()) .scan()) { - scanResult.getClassInfo(MethodInfoTest.class.getName()).getMethodInfo(); + Assertions.assertThrows( + IllegalArgumentException.class, + () -> scanResult.getClassInfo(MethodInfoTest.class.getName()).getMethodInfo() + ); } } @@ -129,17 +133,11 @@ public boolean accept(final MethodInfo methodInfo) { + "(java.lang.String, char, long, float[], byte[][], " + "java.util.List, " + X.class.getName() + "[][][], java.lang.String[]...)", - "@" + Test.class.getName() - + "(expected=class java.lang.IllegalArgumentException, timeout=0) " - + "public void methodInfoNotEnabled()", - "@" + Test.class.getName() + "(expected=class org.junit.Test$None, timeout=0) " - + "public void testGetMethodInfo()", - "@" + Test.class.getName() + "(expected=class org.junit.Test$None, timeout=0) " - + "public void testGetConstructorInfo()", - "@" + Test.class.getName() + "(expected=class org.junit.Test$None, timeout=0) " - + "public void testGetMethodInfoIgnoringVisibility()", - "@" + Test.class.getName() + "(expected=class org.junit.Test$None, timeout=0) " - + "public void testMethodInfoLoadMethodForArrayArg()"); + "@" + 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 testMethodInfoLoadMethodForArrayArg()"); } } @@ -177,17 +175,11 @@ public boolean accept(final MethodInfo methodInfo) { + "java.util.List, " + X.class.getName() + "[][][], java.lang.String[]...)", "private static java.lang.String[] privateMethod()", - "@" + Test.class.getName() - + "(expected=class java.lang.IllegalArgumentException, timeout=0) " - + "public void methodInfoNotEnabled()", - "@" + Test.class.getName() + "(expected=class org.junit.Test$None, timeout=0) " - + "public void testGetMethodInfo()", - "@" + Test.class.getName() + "(expected=class org.junit.Test$None, timeout=0) " - + "public void testGetConstructorInfo()", - "@" + Test.class.getName() + "(expected=class org.junit.Test$None, timeout=0) " - + "public void testGetMethodInfoIgnoringVisibility()", - "@" + Test.class.getName() + "(expected=class org.junit.Test$None, timeout=0) " - + "public void testMethodInfoLoadMethodForArrayArg()"); + "@" + 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 testMethodInfoLoadMethodForArrayArg()"); } } 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 5f8643714..47efbe33f 100644 --- a/src/test/java/io/github/classgraph/test/parameterannotation/RetentionPolicyForFunctionParameterAnnotationsTest.java +++ b/src/test/java/io/github/classgraph/test/parameterannotation/RetentionPolicyForFunctionParameterAnnotationsTest.java @@ -7,9 +7,9 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ClassInfo; @@ -86,7 +86,7 @@ public class RetentionPolicyForFunctionParameterAnnotationsTest { /** * Generate ScanResult before running tests. */ - @BeforeClass + @BeforeAll public static void beforeClass() { scanResult = new ClassGraph() .whitelistPackages(RetentionPolicyForFunctionParameterAnnotationsTest.class.getPackage().getName()) @@ -97,7 +97,7 @@ public static void beforeClass() { /** * Close ScanResult after running tests. */ - @AfterClass + @AfterAll public static void afterClass() { scanResult.close(); } 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 313897f69..94c1ee508 100644 --- a/src/test/java/io/github/classgraph/test/utils/LogNodeTest.java +++ b/src/test/java/io/github/classgraph/test/utils/LogNodeTest.java @@ -1,6 +1,6 @@ package io.github.classgraph.test.utils; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.ByteArrayOutputStream; import java.io.PrintStream; @@ -8,7 +8,7 @@ import java.util.logging.Level; import java.util.logging.Logger; -import org.junit.Test; +import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import nonapi.io.github.classgraph.utils.LogNode; diff --git a/src/test/java/nonapi/io/github/classgraph/json/JSONParserTest.java b/src/test/java/nonapi/io/github/classgraph/json/JSONParserTest.java index 8c6075036..bc1f95570 100644 --- a/src/test/java/nonapi/io/github/classgraph/json/JSONParserTest.java +++ b/src/test/java/nonapi/io/github/classgraph/json/JSONParserTest.java @@ -1,6 +1,6 @@ package nonapi.io.github.classgraph.json; -import org.junit.Test; +import org.junit.jupiter.api.Test; import nonapi.io.github.classgraph.types.ParseException; From 6584423ee614244529f7a50fb4aabe2da9138580 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 29 Jul 2019 08:53:39 -0600 Subject: [PATCH 0251/1778] Fix unit test broken by Annotation::toString variability (#366) --- .../io/github/classgraph/AnnotationClassRef.java | 9 ++++++--- .../classgraph/AnnotationParameterValue.java | 4 ++-- .../classgraph/features/AnnotationEquality.java | 14 +++++++++----- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/github/classgraph/AnnotationClassRef.java b/src/main/java/io/github/classgraph/AnnotationClassRef.java index 0c899a58f..676c0df18 100644 --- a/src/main/java/io/github/classgraph/AnnotationClassRef.java +++ b/src/main/java/io/github/classgraph/AnnotationClassRef.java @@ -204,14 +204,17 @@ public boolean equals(final Object obj) { */ @Override public String toString() { - String prefix = "class "; + // String prefix = "class "; if (scanResult != null) { final ClassInfo ci = getClassInfo(); // The JDK uses "interface" for both interfaces and annotations in Annotation::toString if (ci != null && ci.isInterfaceOrAnnotation()) { - prefix = "interface "; + // prefix = "interface "; } } - return prefix + getTypeSignature().toString(); + // More recent versions of Annotation::toString() have dropped the "class"/"interface" prefix, + // and added ".class" to the end of the class reference (which does not actually match the + // annotation source syntax...) + return /* prefix + */ getTypeSignature().toString() + ".class"; } } \ No newline at end of file diff --git a/src/main/java/io/github/classgraph/AnnotationParameterValue.java b/src/main/java/io/github/classgraph/AnnotationParameterValue.java index 411c26f1d..66e3dff95 100644 --- a/src/main/java/io/github/classgraph/AnnotationParameterValue.java +++ b/src/main/java/io/github/classgraph/AnnotationParameterValue.java @@ -214,7 +214,7 @@ void toStringParamValueOnly(final StringBuilder buf) { final Object paramVal = value.get(); final Class valClass = paramVal.getClass(); if (valClass.isArray()) { - buf.append('['); + buf.append('{'); for (int j = 0, n = Array.getLength(paramVal); j < n; j++) { if (j > 0) { buf.append(", "); @@ -222,7 +222,7 @@ void toStringParamValueOnly(final StringBuilder buf) { final Object elt = Array.get(paramVal, j); buf.append(elt == null ? "null" : elt.toString()); } - buf.append(']'); + buf.append('}'); } else if (paramVal instanceof String) { buf.append('"'); buf.append(paramVal.toString().replace("\"", "\\\"").replace("\n", "\\n").replace("\r", "\\r")); diff --git a/src/test/java/io/github/classgraph/features/AnnotationEquality.java b/src/test/java/io/github/classgraph/features/AnnotationEquality.java index 45c96fe0a..f33b085ea 100644 --- a/src/test/java/io/github/classgraph/features/AnnotationEquality.java +++ b/src/test/java/io/github/classgraph/features/AnnotationEquality.java @@ -88,15 +88,19 @@ public void annotationEquality() { final ClassInfo classInfo = scanResult.getClassInfo(Y.class.getName()); assertThat(classInfo).isNotNull(); final Class cls = classInfo.loadClass(); - final Annotation annotation = cls.getAnnotations()[0]; + final X annotation = (X) cls.getAnnotations()[0]; assertThat(X.class.isInstance(annotation)); final AnnotationInfo annotationInfo = classInfo.getAnnotationInfo().get(0); - final Annotation proxyAnnotation = annotationInfo.loadClassAndInstantiate(); - assertThat(X.class.isInstance(proxyAnnotation)); + final Annotation proxyAnnotationGeneric = annotationInfo.loadClassAndInstantiate(); + assertThat(X.class.isInstance(proxyAnnotationGeneric)); + final X proxyAnnotation = (X) proxyAnnotationGeneric; + assertThat(proxyAnnotation.b()).isEqualTo(annotation.b()); + assertThat(proxyAnnotation.c()).isEqualTo(annotation.c()); assertThat(annotation.hashCode()).isEqualTo(proxyAnnotation.hashCode()); assertThat(annotation).isEqualTo(proxyAnnotation); - assertThat(annotation.toString()).isEqualTo(annotationInfo.toString()); - assertThat(annotation.toString()).isEqualTo(proxyAnnotation.toString()); + // Annotation::toString is implementation-dependent (#361) + // assertThat(annotation.toString()).isEqualTo(annotationInfo.toString()); + // assertThat(annotation.toString()).isEqualTo(proxyAnnotation.toString()); } } } From dbc6d6ee9b2793c534106a4ba71379cd65898d87 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 29 Jul 2019 09:18:57 -0600 Subject: [PATCH 0252/1778] Fix more unit tests (#361) --- ...ationParamWithPrimitiveTypedArrayTest.java | 2 +- .../issues/issue153/Issue153Test.java | 10 +++++----- .../test/methodinfo/MethodInfoTest.java | 20 +++++++++---------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/test/java/io/github/classgraph/features/AnnotationParamWithPrimitiveTypedArrayTest.java b/src/test/java/io/github/classgraph/features/AnnotationParamWithPrimitiveTypedArrayTest.java index 1ad26c645..145b8280b 100644 --- a/src/test/java/io/github/classgraph/features/AnnotationParamWithPrimitiveTypedArrayTest.java +++ b/src/test/java/io/github/classgraph/features/AnnotationParamWithPrimitiveTypedArrayTest.java @@ -116,7 +116,7 @@ public void primitiveArrayParams() { assertThat(v2).isEqualTo(new String[] { "x" }); assertThat(v3).isEqualTo(new int[] {}); assertThat(Arrays.toString((Object[]) v4)) - .isEqualTo("[@" + NestedAnnotation.class.getName() + "(str=\"Test\", intArray=[9])]"); + .isEqualTo("[@" + NestedAnnotation.class.getName() + "(str=\"Test\", intArray={9})]"); final AnnotationWithPrimitiveArrayParams annotation = (AnnotationWithPrimitiveArrayParams) annotationInfo .loadClassAndInstantiate(); diff --git a/src/test/java/io/github/classgraph/issues/issue153/Issue153Test.java b/src/test/java/io/github/classgraph/issues/issue153/Issue153Test.java index fc1f18255..e8f48030d 100644 --- a/src/test/java/io/github/classgraph/issues/issue153/Issue153Test.java +++ b/src/test/java/io/github/classgraph/issues/issue153/Issue153Test.java @@ -219,13 +219,13 @@ public void classAnnotationParameters() { // Read class annotation parameters assertThat(classInfo.getAnnotationInfo().getAsStrings()) // .containsExactlyInAnyOrder("@" + StringAnnotation.class.getName() + "(\"classlabel\")", // - "@" + TwoParamAnnotation.class.getName() + "(value1='x', value2=[1, 2, 3])", // + "@" + TwoParamAnnotation.class.getName() + "(value1='x', value2={1, 2, 3})", // "@" + EnumAnnotation.class.getName() + "(" + FruitEnum.class.getName() + ".BANANA" + ")", // - "@" + NestedAnnotation.class.getName() + "([@" + StringAnnotation.class.getName() - + "(\"one\"), @" + StringAnnotation.class.getName() + "(\"two\")])", // - "@" + ClassRefAnnotation.class.getName() + "(class " + Issue153Test.class.getName() - + ")"); + "@" + NestedAnnotation.class.getName() + "({@" + StringAnnotation.class.getName() + + "(\"one\"), @" + StringAnnotation.class.getName() + "(\"two\")})", // + "@" + ClassRefAnnotation.class.getName() + "(" + Issue153Test.class.getName() + + ".class)"); assertThat(classInfo.getFieldInfo("testField").getAnnotationInfo().getAsStrings()) // .containsExactly("@" + StringAnnotation.class.getName() + "(\"fieldlabel\")"); 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 07c0966ee..6e62f06c0 100644 --- a/src/test/java/io/github/classgraph/test/methodinfo/MethodInfoTest.java +++ b/src/test/java/io/github/classgraph/test/methodinfo/MethodInfoTest.java @@ -130,15 +130,15 @@ public boolean accept(final MethodInfo methodInfo) { + "java.util.List, " + X.class.getName() + "[][][], java.lang.String[]...)", "@" + Test.class.getName() - + "(expected=class java.lang.IllegalArgumentException, timeout=0) " + + "(expected=java.lang.IllegalArgumentException.class, timeout=0) " + "public void methodInfoNotEnabled()", - "@" + Test.class.getName() + "(expected=class org.junit.Test$None, timeout=0) " + "@" + Test.class.getName() + "(expected=org.junit.Test$None.class, timeout=0) " + "public void testGetMethodInfo()", - "@" + Test.class.getName() + "(expected=class org.junit.Test$None, timeout=0) " + "@" + Test.class.getName() + "(expected=org.junit.Test$None.class, timeout=0) " + "public void testGetConstructorInfo()", - "@" + Test.class.getName() + "(expected=class org.junit.Test$None, timeout=0) " + "@" + Test.class.getName() + "(expected=org.junit.Test$None.class, timeout=0) " + "public void testGetMethodInfoIgnoringVisibility()", - "@" + Test.class.getName() + "(expected=class org.junit.Test$None, timeout=0) " + "@" + Test.class.getName() + "(expected=org.junit.Test$None.class, timeout=0) " + "public void testMethodInfoLoadMethodForArrayArg()"); } } @@ -178,15 +178,15 @@ public boolean accept(final MethodInfo methodInfo) { + "[][][], java.lang.String[]...)", "private static java.lang.String[] privateMethod()", "@" + Test.class.getName() - + "(expected=class java.lang.IllegalArgumentException, timeout=0) " + + "(expected=java.lang.IllegalArgumentException.class, timeout=0) " + "public void methodInfoNotEnabled()", - "@" + Test.class.getName() + "(expected=class org.junit.Test$None, timeout=0) " + "@" + Test.class.getName() + "(expected=org.junit.Test$None.class, timeout=0) " + "public void testGetMethodInfo()", - "@" + Test.class.getName() + "(expected=class org.junit.Test$None, timeout=0) " + "@" + Test.class.getName() + "(expected=org.junit.Test$None.class, timeout=0) " + "public void testGetConstructorInfo()", - "@" + Test.class.getName() + "(expected=class org.junit.Test$None, timeout=0) " + "@" + Test.class.getName() + "(expected=org.junit.Test$None.class, timeout=0) " + "public void testGetMethodInfoIgnoringVisibility()", - "@" + Test.class.getName() + "(expected=class org.junit.Test$None, timeout=0) " + "@" + Test.class.getName() + "(expected=org.junit.Test$None.class, timeout=0) " + "public void testMethodInfoLoadMethodForArrayArg()"); } } From 999d9097066b3616ae7ec9738e836be310ef1b5e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 29 Jul 2019 09:23:52 -0600 Subject: [PATCH 0253/1778] Merge --- .../test/methodinfo/MethodInfoTest.java | 28 ------------------- 1 file changed, 28 deletions(-) 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 380d9c33f..c1ab2d1be 100644 --- a/src/test/java/io/github/classgraph/test/methodinfo/MethodInfoTest.java +++ b/src/test/java/io/github/classgraph/test/methodinfo/MethodInfoTest.java @@ -133,25 +133,11 @@ public boolean accept(final MethodInfo methodInfo) { + "(java.lang.String, char, long, float[], byte[][], " + "java.util.List, " + X.class.getName() + "[][][], java.lang.String[]...)", -<<<<<<< HEAD - "@" + Test.class.getName() - + "(expected=java.lang.IllegalArgumentException.class, timeout=0) " - + "public void methodInfoNotEnabled()", - "@" + Test.class.getName() + "(expected=org.junit.Test$None.class, timeout=0) " - + "public void testGetMethodInfo()", - "@" + Test.class.getName() + "(expected=org.junit.Test$None.class, timeout=0) " - + "public void testGetConstructorInfo()", - "@" + Test.class.getName() + "(expected=org.junit.Test$None.class, timeout=0) " - + "public void testGetMethodInfoIgnoringVisibility()", - "@" + Test.class.getName() + "(expected=org.junit.Test$None.class, timeout=0) " - + "public void testMethodInfoLoadMethodForArrayArg()"); -======= "@" + 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 testMethodInfoLoadMethodForArrayArg()"); ->>>>>>> 566c0c704a3f8591c979ddfa98941c6623dc608b } } @@ -189,25 +175,11 @@ public boolean accept(final MethodInfo methodInfo) { + "java.util.List, " + X.class.getName() + "[][][], java.lang.String[]...)", "private static java.lang.String[] privateMethod()", -<<<<<<< HEAD - "@" + Test.class.getName() - + "(expected=java.lang.IllegalArgumentException.class, timeout=0) " - + "public void methodInfoNotEnabled()", - "@" + Test.class.getName() + "(expected=org.junit.Test$None.class, timeout=0) " - + "public void testGetMethodInfo()", - "@" + Test.class.getName() + "(expected=org.junit.Test$None.class, timeout=0) " - + "public void testGetConstructorInfo()", - "@" + Test.class.getName() + "(expected=org.junit.Test$None.class, timeout=0) " - + "public void testGetMethodInfoIgnoringVisibility()", - "@" + Test.class.getName() + "(expected=org.junit.Test$None.class, timeout=0) " - + "public void testMethodInfoLoadMethodForArrayArg()"); -======= "@" + 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 testMethodInfoLoadMethodForArrayArg()"); ->>>>>>> 566c0c704a3f8591c979ddfa98941c6623dc608b } } From c35e383b2767f4e144d8921d210d007e5c684f00 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 29 Jul 2019 09:25:05 -0600 Subject: [PATCH 0254/1778] Fix visibility --- .../io/github/classgraph/features/AnnotationEqualityTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/io/github/classgraph/features/AnnotationEqualityTest.java b/src/test/java/io/github/classgraph/features/AnnotationEqualityTest.java index 06c069495..fda22ce39 100644 --- a/src/test/java/io/github/classgraph/features/AnnotationEqualityTest.java +++ b/src/test/java/io/github/classgraph/features/AnnotationEqualityTest.java @@ -16,7 +16,7 @@ /** * AnnotationEqualityTest. */ -public class AnnotationEqualityTest { +class AnnotationEqualityTest { /** * The Interface W. */ From 06ae85ffdfd336c3417f3443816aa6968326df90 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 13 Aug 2019 21:58:44 -0600 Subject: [PATCH 0255/1778] Remove Spliterator usage to restore JDK 7 compatibility (#363) --- .../github/classgraph/AnnotationInfoList.java | 153 +--------- .../AnnotationParameterValueList.java | 152 +--------- .../io/github/classgraph/ClassInfoList.java | 152 +--------- .../io/github/classgraph/FieldInfoList.java | 152 +--------- .../java/io/github/classgraph/InfoList.java | 8 +- .../io/github/classgraph/MethodInfoList.java | 152 +--------- .../PotentiallyUnmodifiableList.java | 282 ++++++++++++++++++ .../io/github/classgraph/ResourceList.java | 154 +--------- 8 files changed, 311 insertions(+), 894 deletions(-) create mode 100644 src/main/java/io/github/classgraph/PotentiallyUnmodifiableList.java diff --git a/src/main/java/io/github/classgraph/AnnotationInfoList.java b/src/main/java/io/github/classgraph/AnnotationInfoList.java index 07cd81206..dabe39cae 100644 --- a/src/main/java/io/github/classgraph/AnnotationInfoList.java +++ b/src/main/java/io/github/classgraph/AnnotationInfoList.java @@ -30,15 +30,10 @@ import java.lang.annotation.Repeatable; import java.util.ArrayList; -import java.util.Collection; import java.util.HashSet; -import java.util.Iterator; import java.util.List; -import java.util.ListIterator; import java.util.Map; import java.util.Set; -import java.util.Spliterator; -import java.util.function.Consumer; import io.github.classgraph.ClassInfo.RelType; import nonapi.io.github.classgraph.utils.CollectionUtils; @@ -53,150 +48,10 @@ public class AnnotationInfoList extends MappableInfoList { private AnnotationInfoList directlyRelatedAnnotations; /** An unmodifiable empty {@link AnnotationInfoList}. */ - static final AnnotationInfoList EMPTY_LIST = new AnnotationInfoList() { - @Override - public boolean add(final AnnotationInfo e) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public void add(final int index, final AnnotationInfo element) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public boolean remove(final Object o) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public AnnotationInfo remove(final int index) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public boolean addAll(final Collection c) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public boolean addAll(final int index, final Collection c) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public boolean removeAll(final Collection c) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public boolean retainAll(final Collection c) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public void clear() { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public AnnotationInfo set(final int index, final AnnotationInfo element) { - throw new IllegalArgumentException("List is immutable"); - } - - // Provide replacement iterators so that there is no chance of a thread that - // is trying to sort the empty list causing a ConcurrentModificationException - // in another thread that is iterating over the empty list (#334) - @Override - public Iterator iterator() { - return new Iterator() { - @Override - public boolean hasNext() { - return false; - } - - @Override - public AnnotationInfo next() { - return null; - } - }; - } - - @Override - public Spliterator spliterator() { - return new Spliterator() { - @Override - public boolean tryAdvance(final Consumer action) { - return false; - } - - @Override - public Spliterator trySplit() { - return null; - } - - @Override - public long estimateSize() { - return 0; - } - - @Override - public int characteristics() { - return 0; - } - }; - } - - @Override - public ListIterator listIterator() { - return new ListIterator() { - @Override - public boolean hasNext() { - return false; - } - - @Override - public AnnotationInfo next() { - return null; - } - - @Override - public boolean hasPrevious() { - return false; - } - - @Override - public AnnotationInfo previous() { - return null; - } - - @Override - public int nextIndex() { - return 0; - } - - @Override - public int previousIndex() { - return 0; - } - - @Override - public void remove() { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public void set(final AnnotationInfo e) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public void add(final AnnotationInfo e) { - throw new IllegalArgumentException("List is immutable"); - } - }; - } - }; + static final AnnotationInfoList EMPTY_LIST = new AnnotationInfoList(); + static { + EMPTY_LIST.makeUnmodifiable(); + } /** * Return an unmodifiable empty {@link AnnotationInfoList}. diff --git a/src/main/java/io/github/classgraph/AnnotationParameterValueList.java b/src/main/java/io/github/classgraph/AnnotationParameterValueList.java index 7168492f6..93414cc4d 100644 --- a/src/main/java/io/github/classgraph/AnnotationParameterValueList.java +++ b/src/main/java/io/github/classgraph/AnnotationParameterValueList.java @@ -29,161 +29,17 @@ package io.github.classgraph; import java.util.Collection; -import java.util.Iterator; -import java.util.ListIterator; import java.util.Map; import java.util.Set; -import java.util.Spliterator; -import java.util.function.Consumer; /** A list of {@link AnnotationParameterValue} objects. */ public class AnnotationParameterValueList extends MappableInfoList { /** An unmodifiable empty {@link AnnotationParameterValueList}. */ - static final AnnotationParameterValueList EMPTY_LIST = new AnnotationParameterValueList() { - @Override - public boolean add(final AnnotationParameterValue e) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public void add(final int index, final AnnotationParameterValue element) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public boolean remove(final Object o) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public AnnotationParameterValue remove(final int index) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public boolean addAll(final Collection c) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public boolean addAll(final int index, final Collection c) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public boolean removeAll(final Collection c) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public boolean retainAll(final Collection c) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public void clear() { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public AnnotationParameterValue set(final int index, final AnnotationParameterValue element) { - throw new IllegalArgumentException("List is immutable"); - } - - // Provide replacement iterators so that there is no chance of a thread that - // is trying to sort the empty list causing a ConcurrentModificationException - // in another thread that is iterating over the empty list (#334) - @Override - public Iterator iterator() { - return new Iterator() { - @Override - public boolean hasNext() { - return false; - } - - @Override - public AnnotationParameterValue next() { - return null; - } - }; - } - - @Override - public Spliterator spliterator() { - return new Spliterator() { - @Override - public boolean tryAdvance(final Consumer action) { - return false; - } - - @Override - public Spliterator trySplit() { - return null; - } - - @Override - public long estimateSize() { - return 0; - } - - @Override - public int characteristics() { - return 0; - } - }; - } - - @Override - public ListIterator listIterator() { - return new ListIterator() { - @Override - public boolean hasNext() { - return false; - } - - @Override - public AnnotationParameterValue next() { - return null; - } - - @Override - public boolean hasPrevious() { - return false; - } - - @Override - public AnnotationParameterValue previous() { - return null; - } - - @Override - public int nextIndex() { - return 0; - } - - @Override - public int previousIndex() { - return 0; - } - - @Override - public void remove() { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public void set(final AnnotationParameterValue e) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public void add(final AnnotationParameterValue e) { - throw new IllegalArgumentException("List is immutable"); - } - }; - } - }; + static final AnnotationParameterValueList EMPTY_LIST = new AnnotationParameterValueList(); + static { + EMPTY_LIST.makeUnmodifiable(); + } /** * Return an unmodifiable empty {@link AnnotationParameterValueList}. diff --git a/src/main/java/io/github/classgraph/ClassInfoList.java b/src/main/java/io/github/classgraph/ClassInfoList.java index ce4d4d983..d7a0c1c75 100644 --- a/src/main/java/io/github/classgraph/ClassInfoList.java +++ b/src/main/java/io/github/classgraph/ClassInfoList.java @@ -36,13 +36,9 @@ import java.util.Collection; import java.util.Collections; import java.util.HashSet; -import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; -import java.util.ListIterator; import java.util.Set; -import java.util.Spliterator; -import java.util.function.Consumer; import io.github.classgraph.ClassInfo.ReachableAndDirectlyRelatedClasses; import nonapi.io.github.classgraph.scanspec.ScanSpec; @@ -72,150 +68,10 @@ public class ClassInfoList extends MappableInfoList { private final boolean sortByName; /** An unmodifiable empty {@link ClassInfoList}. */ - static final ClassInfoList EMPTY_LIST = new ClassInfoList() { - @Override - public boolean add(final ClassInfo e) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public void add(final int index, final ClassInfo element) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public boolean remove(final Object o) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public ClassInfo remove(final int index) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public boolean addAll(final Collection c) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public boolean addAll(final int index, final Collection c) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public boolean removeAll(final Collection c) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public boolean retainAll(final Collection c) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public void clear() { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public ClassInfo set(final int index, final ClassInfo element) { - throw new IllegalArgumentException("List is immutable"); - } - - // Provide replacement iterators so that there is no chance of a thread that - // is trying to sort the empty list causing a ConcurrentModificationException - // in another thread that is iterating over the empty list (#334) - @Override - public Iterator iterator() { - return new Iterator() { - @Override - public boolean hasNext() { - return false; - } - - @Override - public ClassInfo next() { - return null; - } - }; - } - - @Override - public Spliterator spliterator() { - return new Spliterator() { - @Override - public boolean tryAdvance(final Consumer action) { - return false; - } - - @Override - public Spliterator trySplit() { - return null; - } - - @Override - public long estimateSize() { - return 0; - } - - @Override - public int characteristics() { - return 0; - } - }; - } - - @Override - public ListIterator listIterator() { - return new ListIterator() { - @Override - public boolean hasNext() { - return false; - } - - @Override - public ClassInfo next() { - return null; - } - - @Override - public boolean hasPrevious() { - return false; - } - - @Override - public ClassInfo previous() { - return null; - } - - @Override - public int nextIndex() { - return 0; - } - - @Override - public int previousIndex() { - return 0; - } - - @Override - public void remove() { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public void set(final ClassInfo e) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public void add(final ClassInfo e) { - throw new IllegalArgumentException("List is immutable"); - } - }; - } - }; + static final ClassInfoList EMPTY_LIST = new ClassInfoList(); + static { + EMPTY_LIST.makeUnmodifiable(); + } /** * Return an unmodifiable empty {@link ClassInfoList}. diff --git a/src/main/java/io/github/classgraph/FieldInfoList.java b/src/main/java/io/github/classgraph/FieldInfoList.java index 44ebcc105..821116136 100644 --- a/src/main/java/io/github/classgraph/FieldInfoList.java +++ b/src/main/java/io/github/classgraph/FieldInfoList.java @@ -29,161 +29,17 @@ package io.github.classgraph; import java.util.Collection; -import java.util.Iterator; -import java.util.ListIterator; import java.util.Map; import java.util.Set; -import java.util.Spliterator; -import java.util.function.Consumer; /** A list of {@link FieldInfo} objects. */ public class FieldInfoList extends MappableInfoList { /** An unmodifiable empty {@link FieldInfoList}. */ - static final FieldInfoList EMPTY_LIST = new FieldInfoList() { - @Override - public boolean add(final FieldInfo e) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public void add(final int index, final FieldInfo element) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public boolean remove(final Object o) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public FieldInfo remove(final int index) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public boolean addAll(final Collection c) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public boolean addAll(final int index, final Collection c) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public boolean removeAll(final Collection c) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public boolean retainAll(final Collection c) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public void clear() { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public FieldInfo set(final int index, final FieldInfo element) { - throw new IllegalArgumentException("List is immutable"); - } - - // Provide replacement iterators so that there is no chance of a thread that - // is trying to sort the empty list causing a ConcurrentModificationException - // in another thread that is iterating over the empty list (#334) - @Override - public Iterator iterator() { - return new Iterator() { - @Override - public boolean hasNext() { - return false; - } - - @Override - public FieldInfo next() { - return null; - } - }; - } - - @Override - public Spliterator spliterator() { - return new Spliterator() { - @Override - public boolean tryAdvance(final Consumer action) { - return false; - } - - @Override - public Spliterator trySplit() { - return null; - } - - @Override - public long estimateSize() { - return 0; - } - - @Override - public int characteristics() { - return 0; - } - }; - } - - @Override - public ListIterator listIterator() { - return new ListIterator() { - @Override - public boolean hasNext() { - return false; - } - - @Override - public FieldInfo next() { - return null; - } - - @Override - public boolean hasPrevious() { - return false; - } - - @Override - public FieldInfo previous() { - return null; - } - - @Override - public int nextIndex() { - return 0; - } - - @Override - public int previousIndex() { - return 0; - } - - @Override - public void remove() { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public void set(final FieldInfo e) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public void add(final FieldInfo e) { - throw new IllegalArgumentException("List is immutable"); - } - }; - } - }; + static final FieldInfoList EMPTY_LIST = new FieldInfoList(); + static { + EMPTY_LIST.makeUnmodifiable(); + } /** * Return an unmodifiable empty {@link FieldInfoList}. diff --git a/src/main/java/io/github/classgraph/InfoList.java b/src/main/java/io/github/classgraph/InfoList.java index 71aa3006b..e9745780a 100644 --- a/src/main/java/io/github/classgraph/InfoList.java +++ b/src/main/java/io/github/classgraph/InfoList.java @@ -39,10 +39,13 @@ * @param * the element type */ -public class InfoList extends ArrayList { +public class InfoList extends PotentiallyUnmodifiableList { /** serialVersionUID. */ static final long serialVersionUID = 1L; + /** Whether or not the list is modifiable. */ + boolean modifiable = true; + /** * Constructor. */ @@ -107,7 +110,4 @@ public List getAsStrings() { return toStringVals; } } - - // ------------------------------------------------------------------------------------------------------------- - } diff --git a/src/main/java/io/github/classgraph/MethodInfoList.java b/src/main/java/io/github/classgraph/MethodInfoList.java index 9652fd650..50bd018fd 100644 --- a/src/main/java/io/github/classgraph/MethodInfoList.java +++ b/src/main/java/io/github/classgraph/MethodInfoList.java @@ -30,160 +30,16 @@ import java.util.Collection; import java.util.HashMap; -import java.util.Iterator; -import java.util.ListIterator; import java.util.Map; import java.util.Set; -import java.util.Spliterator; -import java.util.function.Consumer; /** A list of {@link MethodInfo} objects. */ public class MethodInfoList extends InfoList { /** An unmodifiable empty {@link MethodInfoList}. */ - static final MethodInfoList EMPTY_LIST = new MethodInfoList() { - @Override - public boolean add(final MethodInfo e) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public void add(final int index, final MethodInfo element) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public boolean remove(final Object o) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public MethodInfo remove(final int index) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public boolean addAll(final Collection c) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public boolean addAll(final int index, final Collection c) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public boolean removeAll(final Collection c) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public boolean retainAll(final Collection c) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public void clear() { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public MethodInfo set(final int index, final MethodInfo element) { - throw new IllegalArgumentException("List is immutable"); - } - - // Provide replacement iterators so that there is no chance of a thread that - // is trying to sort the empty list causing a ConcurrentModificationException - // in another thread that is iterating over the empty list (#334) - @Override - public Iterator iterator() { - return new Iterator() { - @Override - public boolean hasNext() { - return false; - } - - @Override - public MethodInfo next() { - return null; - } - }; - } - - @Override - public Spliterator spliterator() { - return new Spliterator() { - @Override - public boolean tryAdvance(final Consumer action) { - return false; - } - - @Override - public Spliterator trySplit() { - return null; - } - - @Override - public long estimateSize() { - return 0; - } - - @Override - public int characteristics() { - return 0; - } - }; - } - - @Override - public ListIterator listIterator() { - return new ListIterator() { - @Override - public boolean hasNext() { - return false; - } - - @Override - public MethodInfo next() { - return null; - } - - @Override - public boolean hasPrevious() { - return false; - } - - @Override - public MethodInfo previous() { - return null; - } - - @Override - public int nextIndex() { - return 0; - } - - @Override - public int previousIndex() { - return 0; - } - - @Override - public void remove() { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public void set(final MethodInfo e) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public void add(final MethodInfo e) { - throw new IllegalArgumentException("List is immutable"); - } - }; - } - }; + static final MethodInfoList EMPTY_LIST = new MethodInfoList(); + static { + EMPTY_LIST.makeUnmodifiable(); + } /** * Return an unmodifiable empty {@link MethodInfoList}. diff --git a/src/main/java/io/github/classgraph/PotentiallyUnmodifiableList.java b/src/main/java/io/github/classgraph/PotentiallyUnmodifiableList.java new file mode 100644 index 000000000..1183fab64 --- /dev/null +++ b/src/main/java/io/github/classgraph/PotentiallyUnmodifiableList.java @@ -0,0 +1,282 @@ +/* + * 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.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.ListIterator; + +/** + * A potentially unmodifiable list of objects. + * + * @param + * the element type + */ +class PotentiallyUnmodifiableList extends ArrayList { + /** serialVersionUID. */ + static final long serialVersionUID = 1L; + + /** Whether or not the list is modifiable. */ + boolean modifiable = true; + + /** + * Constructor. + */ + PotentiallyUnmodifiableList() { + super(); + } + + /** + * Constructor. + * + * @param sizeHint + * the size hint + */ + PotentiallyUnmodifiableList(final int sizeHint) { + super(sizeHint); + } + + /** + * Constructor. + * + * @param collection + * the initial elements. + */ + PotentiallyUnmodifiableList(final Collection collection) { + super(collection); + } + + /** Make this list unmodifiable. */ + void makeUnmodifiable() { + modifiable = false; + } + + @Override + public boolean add(final T element) { + if (!modifiable) { + throw new IllegalArgumentException("List is immutable"); + } else { + return super.add(element); + } + } + + @Override + public void add(final int index, final T element) { + if (!modifiable) { + throw new IllegalArgumentException("List is immutable"); + } else { + super.add(index, element); + } + } + + @Override + public boolean remove(final Object o) { + if (!modifiable) { + throw new IllegalArgumentException("List is immutable"); + } else { + return super.remove(o); + } + } + + @Override + public T remove(final int index) { + if (!modifiable) { + throw new IllegalArgumentException("List is immutable"); + } else { + return super.remove(index); + } + } + + @Override + public boolean addAll(final Collection c) { + if (!modifiable && !c.isEmpty()) { + throw new IllegalArgumentException("List is immutable"); + } else { + return super.addAll(c); + } + } + + @Override + public boolean addAll(final int index, final Collection c) { + if (!modifiable && !c.isEmpty()) { + throw new IllegalArgumentException("List is immutable"); + } else { + return super.addAll(index, c); + } + } + + @Override + public boolean removeAll(final Collection c) { + if (!modifiable && !c.isEmpty()) { + throw new IllegalArgumentException("List is immutable"); + } else { + return super.removeAll(c); + } + } + + @Override + public boolean retainAll(final Collection c) { + if (!modifiable && !isEmpty()) { + throw new IllegalArgumentException("List is immutable"); + } else { + return super.retainAll(c); + } + } + + @Override + public void clear() { + if (!modifiable && !isEmpty()) { + throw new IllegalArgumentException("List is immutable"); + } else { + super.clear(); + } + } + + @Override + public T set(final int index, final T element) { + if (!modifiable) { + throw new IllegalArgumentException("List is immutable"); + } else { + return super.set(index, element); + } + } + + // Provide replacement iterators so that there is no chance of a thread that + // is trying to sort the empty list causing a ConcurrentModificationException + // in another thread that is iterating over the empty list (#334) + + @Override + public Iterator iterator() { + final Iterator iterator = super.iterator(); + return new Iterator() { + @Override + public boolean hasNext() { + if (isEmpty()) { + return false; + } else { + return iterator.hasNext(); + } + } + + @Override + public T next() { + return iterator.next(); + } + + @Override + public void remove() { + if (!modifiable) { + throw new IllegalArgumentException("List is immutable"); + } else { + iterator.remove(); + } + } + }; + } + + @Override + public ListIterator listIterator() { + final ListIterator iterator = super.listIterator(); + return new ListIterator() { + @Override + public boolean hasNext() { + if (isEmpty()) { + return false; + } else { + return iterator.hasNext(); + } + } + + @Override + public T next() { + return iterator.next(); + } + + @Override + public boolean hasPrevious() { + if (isEmpty()) { + return false; + } else { + return iterator.hasPrevious(); + } + } + + @Override + public T previous() { + return iterator.previous(); + } + + @Override + public int nextIndex() { + if (isEmpty()) { + return 0; + } else { + return iterator.nextIndex(); + } + } + + @Override + public int previousIndex() { + if (isEmpty()) { + return -1; + } else { + return iterator.previousIndex(); + } + } + + @Override + public void remove() { + if (!modifiable) { + throw new IllegalArgumentException("List is immutable"); + } else { + iterator.remove(); + } + } + + @Override + public void set(final T e) { + if (!modifiable) { + throw new IllegalArgumentException("List is immutable"); + } else { + iterator.set(e); + } + } + + @Override + public void add(final T e) { + if (!modifiable) { + throw new IllegalArgumentException("List is immutable"); + } else { + iterator.add(e); + } + } + }; + } +} diff --git a/src/main/java/io/github/classgraph/ResourceList.java b/src/main/java/io/github/classgraph/ResourceList.java index 24a72e02b..a90f3efa6 100644 --- a/src/main/java/io/github/classgraph/ResourceList.java +++ b/src/main/java/io/github/classgraph/ResourceList.java @@ -38,166 +38,22 @@ import java.util.Collection; import java.util.Comparator; import java.util.HashMap; -import java.util.Iterator; import java.util.List; -import java.util.ListIterator; import java.util.Map; import java.util.Map.Entry; -import java.util.Spliterator; -import java.util.function.Consumer; import nonapi.io.github.classgraph.utils.CollectionUtils; /** An AutoCloseable list of AutoCloseable {@link Resource} objects. */ -public class ResourceList extends ArrayList implements AutoCloseable { +public class ResourceList extends PotentiallyUnmodifiableList implements AutoCloseable { /** serialVersionUID. */ static final long serialVersionUID = 1L; /** An unmodifiable empty {@link ResourceList}. */ - static final ResourceList EMPTY_LIST = new ResourceList() { - @Override - public boolean add(final Resource e) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public void add(final int index, final Resource element) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public boolean remove(final Object o) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public Resource remove(final int index) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public boolean addAll(final Collection c) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public boolean addAll(final int index, final Collection c) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public boolean removeAll(final Collection c) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public boolean retainAll(final Collection c) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public void clear() { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public Resource set(final int index, final Resource element) { - throw new IllegalArgumentException("List is immutable"); - } - - // Provide replacement iterators so that there is no chance of a thread that - // is trying to sort the empty list causing a ConcurrentModificationException - // in another thread that is iterating over the empty list (#334) - @Override - public Iterator iterator() { - return new Iterator() { - @Override - public boolean hasNext() { - return false; - } - - @Override - public Resource next() { - return null; - } - }; - } - - @Override - public Spliterator spliterator() { - return new Spliterator() { - @Override - public boolean tryAdvance(final Consumer action) { - return false; - } - - @Override - public Spliterator trySplit() { - return null; - } - - @Override - public long estimateSize() { - return 0; - } - - @Override - public int characteristics() { - return 0; - } - }; - } - - @Override - public ListIterator listIterator() { - return new ListIterator() { - @Override - public boolean hasNext() { - return false; - } - - @Override - public Resource next() { - return null; - } - - @Override - public boolean hasPrevious() { - return false; - } - - @Override - public Resource previous() { - return null; - } - - @Override - public int nextIndex() { - return 0; - } - - @Override - public int previousIndex() { - return 0; - } - - @Override - public void remove() { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public void set(final Resource e) { - throw new IllegalArgumentException("List is immutable"); - } - - @Override - public void add(final Resource e) { - throw new IllegalArgumentException("List is immutable"); - } - }; - } - }; + static final ResourceList EMPTY_LIST = new ResourceList(); + static { + EMPTY_LIST.makeUnmodifiable(); + } /** * Return an unmodifiable empty {@link ResourceList}. From 71a10c7799f7a1a3fccaea95200b7bf058209657 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 13 Aug 2019 23:20:22 -0600 Subject: [PATCH 0256/1778] Fix JDK 9+ deprecation warning using reflection --- .../io/github/classgraph/json/JSONUtils.java | 138 ++++++++++++++++-- 1 file changed, 123 insertions(+), 15 deletions(-) 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 a63fa94ff..8ec1d3357 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/JSONUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/json/JSONUtils.java @@ -30,6 +30,7 @@ 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; @@ -303,6 +304,56 @@ static Class getRawType(final Type type) { } } + // private static MethodHandle canAccess = null; + //private static Method canAccessMethod = null; + // private static MethodHandle isAccessible = null; + private static Method isAccessibleMethod = null; + // private static MethodHandle trySetAccessible = null; + private static Method trySetAccessibleMethod = null; + static { + // TODO: MethodHandles are disabled for now, due to Animal Sniffer bug: + // https://github.com/mojohaus/animal-sniffer/issues/67 + + // final Lookup lookup = MethodHandles.lookup(); + // try { + // // JDK 9+: use AccessibleObject::canAccess(instance) + // canAccess = 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() + // isAccessible = 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) + // trySetAccessible = 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 + } + } + /** * Return true if the field is accessible, or can be made accessible (and make it accessible if so). * @@ -311,30 +362,87 @@ static Class getRawType(final Type type) { * @return true if accessible */ static boolean isAccessibleOrMakeAccessible(final AccessibleObject fieldOrConstructor) { - // Make field accessible if needed - final AtomicBoolean isAccessible = new AtomicBoolean( - // Replace with (in JDK 9+): fieldOrConstructor.canAccess(instance); - fieldOrConstructor.isAccessible()); - if (!isAccessible.get()) { - try { - fieldOrConstructor.setAccessible(true); - isAccessible.set(true); - } catch (final RuntimeException e) { // JDK 9+: InaccessibleObjectException | SecurityException + // 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 (canAccess != null) { + // // // JDK 9+: use canAccess(instance) + // // try { + // // accessible.set((Boolean) canAccess.invokeExact(fieldOrConstructor, instance)); + // // } catch (Throwable e) { + // // // Ignore + // // } + // } + // if (canAccessMethod != null) { + // accessible.set((Boolean) canAccessMethod.invoke(fieldOrConstructor, instance)); + // } + if (!accessible.get()) { + // if (isAccessible != null) { + // // JDK 7/8: use isAccessible (deprecated in JDK 9+) + // try { + // // accessible.set((Boolean) isAccessible.invoke(fieldOrConstructor)); + // } 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 (trySetAccessible != null) { + // accessible.set((Boolean) trySetAccessible.invoke(fieldOrConstructor)); + // } 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() { - try { - fieldOrConstructor.setAccessible(true); - isAccessible.set(true); - } catch (final RuntimeException e) { // JDK 9+: InaccessibleObjectException | SecurityException - // Ignore + // if (trySetAccessible != null) { + // accessible.set((Boolean) trySetAccessible.invoke(fieldOrConstructor)); + // } 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 isAccessible.get(); + return accessible.get(); } /** From a28bb7b3f670e9678680529bbde9234690b12e76 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 13 Aug 2019 23:20:33 -0600 Subject: [PATCH 0257/1778] Source > Format --- .../nonapi/io/github/classgraph/json/ClassFieldCache.java | 1 + .../java/nonapi/io/github/classgraph/json/ClassFields.java | 1 + .../AnnotationParamWithPrimitiveTypedArrayTest.java | 3 ++- .../io/github/classgraph/features/MultiReleaseJarTest.java | 3 ++- .../io/github/classgraph/issues/issue286/Issue286Test.java | 2 +- .../io/github/classgraph/test/fieldinfo/FieldInfoTest.java | 6 ++---- .../github/classgraph/test/methodinfo/MethodInfoTest.java | 6 ++---- 7 files changed, 11 insertions(+), 11 deletions(-) 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 e9b779394..f2d4aff0f 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/ClassFieldCache.java +++ b/src/main/java/nonapi/io/github/classgraph/json/ClassFieldCache.java @@ -79,6 +79,7 @@ class ClassFieldCache { private final boolean onlySerializePublicFields; /** The default constructor for each concrete type. */ + // TODO: replace these with constructor MethodHandles for speed private final Map, Constructor> defaultConstructorForConcreteType = new HashMap<>(); /** The constructor with size hint for each concrete type. */ 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 dcee9e8cb..7feef95aa 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/ClassFields.java +++ b/src/main/java/nonapi/io/github/classgraph/json/ClassFields.java @@ -61,6 +61,7 @@ class ClassFields { final Map fieldNameToFieldTypeInfo = new HashMap<>(); /** If non-null, this is the field that has an {@link Id} annotation. */ + // TODO: replace this with getter/setter MethodHandles for speed Field idField; /** diff --git a/src/test/java/io/github/classgraph/features/AnnotationParamWithPrimitiveTypedArrayTest.java b/src/test/java/io/github/classgraph/features/AnnotationParamWithPrimitiveTypedArrayTest.java index 3de9751cd..309e20efd 100644 --- a/src/test/java/io/github/classgraph/features/AnnotationParamWithPrimitiveTypedArrayTest.java +++ b/src/test/java/io/github/classgraph/features/AnnotationParamWithPrimitiveTypedArrayTest.java @@ -94,7 +94,8 @@ public abstract static class AnnotatedClass { @Test public void primitiveArrayParams() { try (ScanResult scanResult = new ClassGraph().enableAllInfo() - .whitelistPackages(AnnotationParamWithPrimitiveTypedArrayTest.class.getPackage().getName()).scan()) { + .whitelistPackages(AnnotationParamWithPrimitiveTypedArrayTest.class.getPackage().getName()) + .scan()) { final AnnotationInfo annotationInfo = scanResult.getClassInfo(AnnotatedClass.class.getName()) .getAnnotationInfo().get(0); final AnnotationParameterValueList annotationParams = annotationInfo.getParameterValues(); diff --git a/src/test/java/io/github/classgraph/features/MultiReleaseJarTest.java b/src/test/java/io/github/classgraph/features/MultiReleaseJarTest.java index e73a22f3e..39837714a 100644 --- a/src/test/java/io/github/classgraph/features/MultiReleaseJarTest.java +++ b/src/test/java/io/github/classgraph/features/MultiReleaseJarTest.java @@ -22,7 +22,8 @@ */ public class MultiReleaseJarTest { /** The Constant jarURL. */ - private static final URL jarURL = MultiReleaseJarTest.class.getClassLoader().getResource("multi-release-jar.jar"); + private static final URL jarURL = MultiReleaseJarTest.class.getClassLoader() + .getResource("multi-release-jar.jar"); /** * Multi release jar. diff --git a/src/test/java/io/github/classgraph/issues/issue286/Issue286Test.java b/src/test/java/io/github/classgraph/issues/issue286/Issue286Test.java index 15723912b..248621441 100644 --- a/src/test/java/io/github/classgraph/issues/issue286/Issue286Test.java +++ b/src/test/java/io/github/classgraph/issues/issue286/Issue286Test.java @@ -33,10 +33,10 @@ import java.net.URL; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; -import org.junit.jupiter.api.Timeout; /** * Issue286Test. diff --git a/src/test/java/io/github/classgraph/test/fieldinfo/FieldInfoTest.java b/src/test/java/io/github/classgraph/test/fieldinfo/FieldInfoTest.java index b2ca26e40..011907f33 100644 --- a/src/test/java/io/github/classgraph/test/fieldinfo/FieldInfoTest.java +++ b/src/test/java/io/github/classgraph/test/fieldinfo/FieldInfoTest.java @@ -61,10 +61,8 @@ public class FieldInfoTest { public void fieldInfoNotEnabled() { try (ScanResult scanResult = new ClassGraph().whitelistPackages(FieldInfoTest.class.getPackage().getName()) .scan()) { - Assertions.assertThrows( - IllegalArgumentException.class, - () -> scanResult.getClassInfo(FieldInfoTest.class.getName()).getFieldInfo() - ); + Assertions.assertThrows(IllegalArgumentException.class, + () -> scanResult.getClassInfo(FieldInfoTest.class.getName()).getFieldInfo()); } } 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 c1ab2d1be..7f28b9197 100644 --- a/src/test/java/io/github/classgraph/test/methodinfo/MethodInfoTest.java +++ b/src/test/java/io/github/classgraph/test/methodinfo/MethodInfoTest.java @@ -105,10 +105,8 @@ public void methodInfoNotEnabled() { // .enableSaveMethodInfo() not called try (ScanResult scanResult = new ClassGraph().whitelistPackages(MethodInfoTest.class.getPackage().getName()) .scan()) { - Assertions.assertThrows( - IllegalArgumentException.class, - () -> scanResult.getClassInfo(MethodInfoTest.class.getName()).getMethodInfo() - ); + Assertions.assertThrows(IllegalArgumentException.class, + () -> scanResult.getClassInfo(MethodInfoTest.class.getName()).getMethodInfo()); } } From a986ba94c9e257abe83f02e0766fbec7798ed3fa Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 13 Aug 2019 23:20:45 -0600 Subject: [PATCH 0258/1778] Small updates --- .classpath | 6 +----- pom.xml | 10 ++++++++++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/.classpath b/.classpath index 5576b4361..6ba1358a6 100644 --- a/.classpath +++ b/.classpath @@ -31,10 +31,6 @@ - - - - - + diff --git a/pom.xml b/pom.xml index fcb803702..c06256409 100644 --- a/pom.xml +++ b/pom.xml @@ -56,6 +56,11 @@ + + + + + @@ -159,6 +164,11 @@ + + + + + 7 7 From 42276b218eac39b9e65a29572348870fcf5757ee Mon Sep 17 00:00:00 2001 From: James Ward Date: Wed, 14 Aug 2019 00:12:39 -0600 Subject: [PATCH 0259/1778] add support for last modified and posix file perms - fixes #364 --- .../classgraph/ClasspathElementZip.java | 6 +- .../java/io/github/classgraph/Resource.java | 53 ++++++++- .../fastzipfilereader/FastZipEntry.java | 23 +++- .../fastzipfilereader/LogicalZipFile.java | 46 ++++++- .../issues/issue364/Issue364Test.java | 112 ++++++++++++++++++ .../resources/issue364-no-permissions.jar | Bin 0 -> 2115 bytes src/test/resources/issue364-permissions.jar | Bin 0 -> 2462 bytes 7 files changed, 227 insertions(+), 13 deletions(-) create mode 100644 src/test/java/io/github/classgraph/issues/issue364/Issue364Test.java create mode 100644 src/test/resources/issue364-no-permissions.jar create mode 100644 src/test/resources/issue364-permissions.jar diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index cb075068a..e9211c58d 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) // Get the normalized path of the logical zipfile zipFilePath = FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, logicalZipFile.getPath()); - // Get package root of jarfile + // Get package root of jarfile final String packageRoot = logicalZipFileAndPackageRoot.getValue(); if (!packageRoot.isEmpty()) { packageRootPrefix = packageRoot + "/"; @@ -238,7 +238,7 @@ void open(final WorkQueue workQueue, final LogNode log) } } } - // Add paths in an OSGi bundle jar manifest's "Bundle-ClassPath" entry to the classpath, resolving + // Add paths in an OSGi bundle jar manifest's "Bundle-ClassPath" entry to the classpath, resolving // the paths relative to the root of the jarfile if (logicalZipFile.bundleClassPathManifestEntryValue != null) { final String zipFilePathPrefix = zipFilePath + "!/"; @@ -281,7 +281,7 @@ void open(final WorkQueue workQueue, final LogNode log) * @return the resource */ private Resource newResource(final FastZipEntry zipEntry, final String pathRelativeToPackageRoot) { - return new Resource(this, zipEntry.uncompressedSize) { + return new Resource(this, zipEntry.uncompressedSize, zipEntry.lastModified, zipEntry.posixFilePermissions) { /** * Path with package root prefix and/or any Spring Boot prefix ("BOOT-INF/classes/" or * "WEB-INF/classes/") removed. diff --git a/src/main/java/io/github/classgraph/Resource.java b/src/main/java/io/github/classgraph/Resource.java index acb21266e..a27f64f7f 100644 --- a/src/main/java/io/github/classgraph/Resource.java +++ b/src/main/java/io/github/classgraph/Resource.java @@ -38,6 +38,8 @@ import java.net.URL; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; +import java.nio.file.attribute.PosixFilePermission; +import java.util.Set; import java.util.zip.ZipEntry; import nonapi.io.github.classgraph.utils.FileUtils; @@ -68,6 +70,13 @@ public abstract class Resource implements Closeable, Comparable { /** The cached result of toString(). */ private String toString; + /** The last modified millis since the epoch, or 0L if it is unknown */ + private long lastModified; + + /** The file permissions for this resource, or null if unknown */ + private Set posixFilePermissions; + + /** * The {@link LogNode} used to log that the resource was found when classpath element paths are scanned. In the * case of whitelisted classfile resources, sublog entries are added when the classfile's contents are scanned. @@ -87,6 +96,23 @@ public abstract class Resource implements Closeable, Comparable { public Resource(final ClasspathElement classpathElement, final long length) { this.classpathElement = classpathElement; this.length = length; + this.lastModified = 0L; + this.posixFilePermissions = null; + } + + /** + * Constructor. + * + * @param classpathElement + * the classpath element this resource was obtained from. + * @param length + * the length the length of the resource. + */ + public Resource(final ClasspathElement classpathElement, final long length, final long lastModified, final Set posixFilePermissions) { + this.classpathElement = classpathElement; + this.length = length; + this.lastModified = lastModified; + this.posixFilePermissions = posixFilePermissions; } // ------------------------------------------------------------------------------------------------------------- @@ -289,7 +315,7 @@ public void close() throws IOException { /** * Mark the resource as open. - * + * * @throws IOException * If the resource is already open. */ @@ -459,7 +485,7 @@ public String getContentAsString() throws IOException { /** * Open an {@link InputStream} for a classpath resource. Make sure you call {@link Resource#close()} when you * are finished with the {@link InputStream}, so that the {@link InputStream} is closed. - * + * * @return The opened {@link InputStream}. * @throws IOException * If the {@link InputStream} could not be opened. @@ -469,7 +495,7 @@ 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. - * + * * @return The allocated or mapped {@link ByteBuffer} for the resource file content. * @throws IOException * If the resource could not be opened. @@ -480,7 +506,7 @@ public String getContentAsString() throws IOException { * 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 * InputStream is closed or the underlying ByteBuffer is released or unmapped. - * + * * @return The contents of the resource file. * @throws IOException * If the file contents could not be loaded in their entirety. @@ -509,6 +535,25 @@ public long getLength() { return length; } + /** + * Get the last modified millis since the epoch + * + * @return The millis since the epoch indicating the date / time that this file resource was last modified. + * Returns 0L if the last modified date is unknown. + */ + public long getLastModified() { + return lastModified; + } + + /** + * Get the Posix File Permissions for the resource + * + * @return The set of PosixFilePermission for the resource or null if they are unknown + */ + public Set getPosixFilePermissions() { + return posixFilePermissions; + } + // ------------------------------------------------------------------------------------------------------------- /** 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 46ff9aca6..21e779f72 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/FastZipEntry.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/FastZipEntry.java @@ -34,6 +34,8 @@ import java.nio.Buffer; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; +import java.nio.file.attribute.PosixFilePermission; +import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import java.util.zip.DataFormatException; import java.util.zip.Inflater; @@ -66,6 +68,12 @@ public class FastZipEntry implements Comparable { /** The uncompressed size of the zip entry, in bytes. */ public final long uncompressedSize; + /** The last modified millis since the epoch, or 0L if it is unknown */ + public final long lastModified; + + /** The file permissions for this resource, or null if unknown */ + public final Set posixFilePermissions; + /** * The version code (>= 9), or 8 for the base layer or a non-versioned jar (whether JDK 7 or 8 compatible). */ @@ -86,7 +94,6 @@ public class FastZipEntry implements Comparable { /** * Constructor. - * * @param parentLogicalZipFile * The parent logical zipfile containing this entry. * @param locHeaderPos @@ -101,10 +108,14 @@ public class FastZipEntry implements Comparable { * The uncompressed size of the entry. * @param nestedJarHandler * The {@link NestedJarHandler}. + * @param lastModified + * The last modified date/time in millis since the epoch + * @param posixFilePermissions + * The POSIX file permissions */ FastZipEntry(final LogicalZipFile parentLogicalZipFile, final long locHeaderPos, final String entryName, - final boolean isDeflated, final long compressedSize, final long uncompressedSize, - final NestedJarHandler nestedJarHandler) { + final boolean isDeflated, final long compressedSize, final long uncompressedSize, + final NestedJarHandler nestedJarHandler, final long lastModified, final Set posixFilePermissions) { this.parentLogicalZipFile = parentLogicalZipFile; this.locHeaderPos = locHeaderPos; this.entryName = entryName; @@ -112,6 +123,8 @@ public class FastZipEntry implements Comparable { this.compressedSize = compressedSize; this.uncompressedSize = !isDeflated && uncompressedSize < 0 ? compressedSize : uncompressedSize; this.nestedJarHandler = nestedJarHandler; + this.lastModified = lastModified; + this.posixFilePermissions = posixFilePermissions; // Get multi-release jar version number, and strip any version prefix int entryVersion = 8; @@ -387,7 +400,7 @@ private int readDeflated(final byte[] buf, final int off, final int len) // N.B. the ByteBuffer version of setInput doesn't seem to need the extra // padding byte at the end when using the "nowrap" Inflater option. - // Copy from the ByteBuffer into a temporary byte[] array (needed for JDK<11). + // Copy from the ByteBuffer into a temporary byte[] array (needed for JDK<11). try { final int remaining = currChunkByteBuf.remaining(); if (isLastChunk && remaining < inflateBuf.length) { @@ -655,4 +668,4 @@ public boolean equals(final Object obj) { public int hashCode() { return parentLogicalZipFile.hashCode() ^ version ^ entryName.hashCode() ^ (int) locHeaderPos; } -} \ No newline at end of file +} 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 938528a27..8bc25ba33 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java @@ -33,8 +33,10 @@ import java.io.IOException; import java.io.UnsupportedEncodingException; import java.nio.charset.StandardCharsets; +import java.nio.file.attribute.PosixFilePermission; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; +import java.util.Calendar; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -42,6 +44,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.TimeZone; import java.util.concurrent.ConcurrentHashMap; import io.github.classgraph.ClassGraphException; @@ -394,6 +397,7 @@ private void parseManifest(final FastZipEntry manifestZipEntry, final LogNode lo i = manifestValueAndEndIdx.getValue(); } else { + // Key name was unrecognized -- skip to next key skip = true; } @@ -609,11 +613,51 @@ private void readCentralDirectory(final ZipFileSliceReader zipFileSliceReader, f } final boolean isDeflated = compressionMethod == /* deflated */ 8; + final int lastModifiedTime = entryBytes != null ? ZipFileSliceReader.getShort(entryBytes, entOff + 12) + : zipFileSliceReader.getShort(cenPos + entOff + 12); + + final int lastModifiedDate = entryBytes != null ? ZipFileSliceReader.getShort(entryBytes, entOff + 14) + : zipFileSliceReader.getShort(cenPos + entOff + 14); + + // MS-DOS Date & Time Format + final int lastModifiedSecond = (lastModifiedTime & 0b11111) * 2; + final int lastModifiedMinute = lastModifiedTime >> 5 & 0b111111; + final int lastModifiedHour = lastModifiedTime >> 11; + final int lastModifiedDay = lastModifiedDate & 0b11111; + final int lastModifiedMonth = (lastModifiedDate >> 5 & 0b111) - 1; + final int lastModifiedYear = (lastModifiedDate >> 9) + 1980; + + final Calendar lastModifiedCalendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + lastModifiedCalendar.set(lastModifiedYear, lastModifiedMonth, lastModifiedDay, lastModifiedHour, lastModifiedMinute, lastModifiedSecond); + lastModifiedCalendar.set(Calendar.MILLISECOND, 0); + // Get compressed and uncompressed size long compressedSize = entryBytes != null ? ZipFileSliceReader.getInt(entryBytes, entOff + 20) : zipFileSliceReader.getInt(cenPos + entOff + 20); long uncompressedSize = entryBytes != null ? ZipFileSliceReader.getInt(entryBytes, entOff + 24) : zipFileSliceReader.getInt(cenPos + entOff + 24); + + // Get external file attributes + int externalFileAttributes = entryBytes != null ? ZipFileSliceReader.getShort(entryBytes, entOff + 40) + : zipFileSliceReader.getShort(cenPos + entOff + 40); + + Set perms; + if (externalFileAttributes == 0) { + perms = null; + } + else { + perms = new HashSet<>(); + if ((externalFileAttributes & 0400) > 0) perms.add(PosixFilePermission.OWNER_READ); + if ((externalFileAttributes & 0200) > 0) perms.add(PosixFilePermission.OWNER_WRITE); + if ((externalFileAttributes & 0100) > 0) perms.add(PosixFilePermission.OWNER_EXECUTE); + if ((externalFileAttributes & 0040) > 0) perms.add(PosixFilePermission.GROUP_READ); + if ((externalFileAttributes & 0020) > 0) perms.add(PosixFilePermission.GROUP_WRITE); + if ((externalFileAttributes & 0010) > 0) perms.add(PosixFilePermission.GROUP_EXECUTE); + if ((externalFileAttributes & 0004) > 0) perms.add(PosixFilePermission.OTHERS_READ); + if ((externalFileAttributes & 0002) > 0) perms.add(PosixFilePermission.OTHERS_WRITE); + if ((externalFileAttributes & 0001) > 0) perms.add(PosixFilePermission.OTHERS_EXECUTE); + } + long pos = entryBytes != null ? ZipFileSliceReader.getInt(entryBytes, entOff + 42) : zipFileSliceReader.getInt(cenPos + entOff + 42); @@ -674,7 +718,7 @@ private void readCentralDirectory(final ZipFileSliceReader zipFileSliceReader, f // Add zip entry final FastZipEntry entry = new FastZipEntry(this, locHeaderPos, entryNameSanitized, isDeflated, - compressedSize, uncompressedSize, physicalZipFile.nestedJarHandler); + compressedSize, uncompressedSize, physicalZipFile.nestedJarHandler, lastModifiedCalendar.getTimeInMillis(), perms); entries.add(entry); // Record manifest entry diff --git a/src/test/java/io/github/classgraph/issues/issue364/Issue364Test.java b/src/test/java/io/github/classgraph/issues/issue364/Issue364Test.java new file mode 100644 index 000000000..4394cec4f --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue364/Issue364Test.java @@ -0,0 +1,112 @@ +/* + * This file is part of ClassGraph. + * + * Author: James Ward + * + * 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.issues.issue364; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ScanResult; +import org.junit.jupiter.api.Test; + +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.attribute.PosixFilePermission; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Issue364Test. + */ +public class Issue364Test { + + /** + * Test No Permissions. + */ + @Test + public void testNoPermissions() { + final ClassLoader classLoader = Issue364Test.class.getClassLoader(); + final String aJarName = "issue364-no-permissions.jar"; + final URL aJarURL = classLoader.getResource(aJarName); + final URLClassLoader overrideClassLoader = new URLClassLoader(new URL[] { aJarURL }); + + try (ScanResult result = new ClassGraph().overrideClassLoaders(overrideClassLoader).ignoreParentClassLoaders().scan()) { + assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/all").get(0).getLastModified()).isEqualTo(1434543812000L); + assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/all").get(0).getPosixFilePermissions()).isNull(); + assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/groupreadwrite").get(0).getLastModified()).isEqualTo(1434557162000L); + assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/groupreadwrite").get(0).getPosixFilePermissions()).isNull(); + assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/owneronlyread").get(0).getLastModified()).isEqualTo(1434557150000L); + assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/owneronlyread").get(0).getPosixFilePermissions()).isNull(); + assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/ownerreadwrite").get(0).getLastModified()).isEqualTo(1434543812000L); + assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/ownerreadwrite").get(0).getPosixFilePermissions()).isNull(); + } + } + + /** + * Test Permissions. + */ + @Test + public void testPermissions() { + final ClassLoader classLoader = Issue364Test.class.getClassLoader(); + final String aJarName = "issue364-permissions.jar"; + final URL aJarURL = classLoader.getResource(aJarName); + final URLClassLoader overrideClassLoader = new URLClassLoader(new URL[] { aJarURL }); + + try (ScanResult result = new ClassGraph().overrideClassLoaders(overrideClassLoader).ignoreParentClassLoaders().scan()) { + assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/all").get(0).getLastModified()).isEqualTo(1434543812000L); + assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/all").get(0).getPosixFilePermissions()).containsExactlyInAnyOrder( + PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE, PosixFilePermission.OWNER_EXECUTE, + PosixFilePermission.GROUP_READ, PosixFilePermission.GROUP_WRITE, PosixFilePermission.GROUP_EXECUTE, + PosixFilePermission.OTHERS_READ, PosixFilePermission.OTHERS_WRITE, PosixFilePermission.OTHERS_EXECUTE + ); + + assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/execute").get(0).getLastModified()).isEqualTo(1434557130000L); + assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/execute").get(0).getPosixFilePermissions()).containsExactlyInAnyOrder( + PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE, PosixFilePermission.OWNER_EXECUTE, + PosixFilePermission.GROUP_READ, PosixFilePermission.GROUP_EXECUTE, + PosixFilePermission.OTHERS_READ, PosixFilePermission.OTHERS_EXECUTE + ); + + assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/groupreadwrite").get(0).getLastModified()).isEqualTo(1434557162000L); + assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/groupreadwrite").get(0).getPosixFilePermissions()).containsExactlyInAnyOrder( + PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE, + PosixFilePermission.GROUP_READ, PosixFilePermission.GROUP_WRITE + ); + + assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/owneronlyread").get(0).getLastModified()).isEqualTo(1434557152000L); + assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/owneronlyread").get(0).getPosixFilePermissions()).containsExactlyInAnyOrder( + PosixFilePermission.OWNER_READ + ); + + assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/ownerreadwrite").get(0).getLastModified()).isEqualTo(1434543812000L); + assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/ownerreadwrite").get(0).getPosixFilePermissions()).containsExactlyInAnyOrder( + PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE, + PosixFilePermission.GROUP_READ, + PosixFilePermission.OTHERS_READ + ); + } + } +} diff --git a/src/test/resources/issue364-no-permissions.jar b/src/test/resources/issue364-no-permissions.jar new file mode 100644 index 0000000000000000000000000000000000000000..eceee1939f6949737475e74e5c7c7b70c352eb60 GIT binary patch literal 2115 zcmWIWW@Zs#;Nak3XpFn;#()Gk8CV#6T|*poJ^kGD|D9rBU}gyLX6FE@V1g&nwminuO$n zz6Mk$8p$k_kK|A`$^CPSK+V5~83WNH5ASEhtJ&OertQM7CfjngveeT999!ms*sc zms1I{1mOYn04Li6VDphYz{n)RfST1|`52VdQ2{)}Kp8*48&xZE?gM3X1h54%;aZWh zJ;($u2Hd$6VZtF~6A;mb&j3)aMS#yhCfo@K17Puu&md4PMgSc~yau5s9AtyB_d+*P^p0c9TeGzREDs!fehsV OLKk3>@e*h(0|NlKZ1Q#h literal 0 HcmV?d00001 diff --git a/src/test/resources/issue364-permissions.jar b/src/test/resources/issue364-permissions.jar new file mode 100644 index 0000000000000000000000000000000000000000..482721515ad498ed5e907c5213818d83799fcff7 GIT binary patch literal 2462 zcmbW1F;Buk6vrQwk|1hKa4^9*F-breNEl2E5=q2p80ZH8yqFqnNoxZRCe+PICqIF^ zE-r4YZjQP;lDIhQT?@3m_KFmE7S; zZAWS*KzREYO4qBVWDJrJiTy+XV(a+N4Q52j9ibszHM9=WIm6PyVSUs-Z)v=t+3?UF z(k^6OmsLg2Vah)%zZ-fdSzHWK%op+n8#P`r=S-YJm3UBXS+Q|-e7T_){7vmhnHSgU zcCD4ld=4dYm@D-}HMq-5L<7_@E|Epu#(k2*q>d^X6ks*Jhq>f1+4Kc-H*T@LjWh2$65SG54%(R5Xbr{{^5`;>XdB7b z3p*|a?zpNu&TXf~gVUbi``}>5REq<$9>7;Pz;CVjK) Date: Wed, 14 Aug 2019 01:05:05 -0600 Subject: [PATCH 0260/1778] Source > Cleanup --- .../java/io/github/classgraph/Resource.java | 16 ++- .../fastzipfilereader/FastZipEntry.java | 10 +- .../fastzipfilereader/LogicalZipFile.java | 54 ++++++--- .../issues/issue364/Issue364Test.java | 109 +++++++++++------- 4 files changed, 122 insertions(+), 67 deletions(-) diff --git a/src/main/java/io/github/classgraph/Resource.java b/src/main/java/io/github/classgraph/Resource.java index a27f64f7f..8d173b334 100644 --- a/src/main/java/io/github/classgraph/Resource.java +++ b/src/main/java/io/github/classgraph/Resource.java @@ -70,12 +70,11 @@ public abstract class Resource implements Closeable, Comparable { /** The cached result of toString(). */ private String toString; - /** The last modified millis since the epoch, or 0L if it is unknown */ - private long lastModified; - - /** The file permissions for this resource, or null if unknown */ - private Set posixFilePermissions; + /** The last modified millis since the epoch, or 0L if it is unknown */ + private final long lastModified; + /** The file permissions for this resource, or null if unknown */ + private final Set posixFilePermissions; /** * The {@link LogNode} used to log that the resource was found when classpath element paths are scanned. In the @@ -107,8 +106,13 @@ public Resource(final ClasspathElement classpathElement, final long length) { * the classpath element this resource was obtained from. * @param length * the length the length of the resource. + * @param lastModified + * The last modified date, in milliseconds since the epoch, or 0 if unknown. + * @param posixFilePermissions + * The POSIX file permissions, or null if unknown. */ - public Resource(final ClasspathElement classpathElement, final long length, final long lastModified, final Set posixFilePermissions) { + public Resource(final ClasspathElement classpathElement, final long length, final long lastModified, + final Set posixFilePermissions) { this.classpathElement = classpathElement; this.length = length; this.lastModified = lastModified; 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 21e779f72..c79b7495f 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/FastZipEntry.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/FastZipEntry.java @@ -68,10 +68,10 @@ public class FastZipEntry implements Comparable { /** The uncompressed size of the zip entry, in bytes. */ public final long uncompressedSize; - /** The last modified millis since the epoch, or 0L if it is unknown */ + /** The last modified millis since the epoch, or 0L if it is unknown */ public final long lastModified; - /** The file permissions for this resource, or null if unknown */ + /** The file permissions for this resource, or null if unknown */ public final Set posixFilePermissions; /** @@ -94,6 +94,7 @@ public class FastZipEntry implements Comparable { /** * Constructor. + * * @param parentLogicalZipFile * The parent logical zipfile containing this entry. * @param locHeaderPos @@ -114,8 +115,9 @@ public class FastZipEntry implements Comparable { * The POSIX file permissions */ FastZipEntry(final LogicalZipFile parentLogicalZipFile, final long locHeaderPos, final String entryName, - final boolean isDeflated, final long compressedSize, final long uncompressedSize, - final NestedJarHandler nestedJarHandler, final long lastModified, final Set posixFilePermissions) { + final boolean isDeflated, final long compressedSize, final long uncompressedSize, + final NestedJarHandler nestedJarHandler, final long lastModified, + final Set posixFilePermissions) { this.parentLogicalZipFile = parentLogicalZipFile; this.locHeaderPos = locHeaderPos; this.entryName = entryName; 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 8bc25ba33..4d1e37abb 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java @@ -613,10 +613,12 @@ private void readCentralDirectory(final ZipFileSliceReader zipFileSliceReader, f } final boolean isDeflated = compressionMethod == /* deflated */ 8; - final int lastModifiedTime = entryBytes != null ? ZipFileSliceReader.getShort(entryBytes, entOff + 12) + final int lastModifiedTime = entryBytes != null + ? ZipFileSliceReader.getShort(entryBytes, entOff + 12) : zipFileSliceReader.getShort(cenPos + entOff + 12); - final int lastModifiedDate = entryBytes != null ? ZipFileSliceReader.getShort(entryBytes, entOff + 14) + final int lastModifiedDate = entryBytes != null + ? ZipFileSliceReader.getShort(entryBytes, entOff + 14) : zipFileSliceReader.getShort(cenPos + entOff + 14); // MS-DOS Date & Time Format @@ -628,7 +630,8 @@ private void readCentralDirectory(final ZipFileSliceReader zipFileSliceReader, f final int lastModifiedYear = (lastModifiedDate >> 9) + 1980; final Calendar lastModifiedCalendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - lastModifiedCalendar.set(lastModifiedYear, lastModifiedMonth, lastModifiedDay, lastModifiedHour, lastModifiedMinute, lastModifiedSecond); + lastModifiedCalendar.set(lastModifiedYear, lastModifiedMonth, lastModifiedDay, lastModifiedHour, + lastModifiedMinute, lastModifiedSecond); lastModifiedCalendar.set(Calendar.MILLISECOND, 0); // Get compressed and uncompressed size @@ -638,24 +641,42 @@ private void readCentralDirectory(final ZipFileSliceReader zipFileSliceReader, f : zipFileSliceReader.getInt(cenPos + entOff + 24); // Get external file attributes - int externalFileAttributes = entryBytes != null ? ZipFileSliceReader.getShort(entryBytes, entOff + 40) + final int externalFileAttributes = entryBytes != null + ? ZipFileSliceReader.getShort(entryBytes, entOff + 40) : zipFileSliceReader.getShort(cenPos + entOff + 40); Set perms; if (externalFileAttributes == 0) { perms = null; - } - else { + } else { perms = new HashSet<>(); - if ((externalFileAttributes & 0400) > 0) perms.add(PosixFilePermission.OWNER_READ); - if ((externalFileAttributes & 0200) > 0) perms.add(PosixFilePermission.OWNER_WRITE); - if ((externalFileAttributes & 0100) > 0) perms.add(PosixFilePermission.OWNER_EXECUTE); - if ((externalFileAttributes & 0040) > 0) perms.add(PosixFilePermission.GROUP_READ); - if ((externalFileAttributes & 0020) > 0) perms.add(PosixFilePermission.GROUP_WRITE); - if ((externalFileAttributes & 0010) > 0) perms.add(PosixFilePermission.GROUP_EXECUTE); - if ((externalFileAttributes & 0004) > 0) perms.add(PosixFilePermission.OTHERS_READ); - if ((externalFileAttributes & 0002) > 0) perms.add(PosixFilePermission.OTHERS_WRITE); - if ((externalFileAttributes & 0001) > 0) perms.add(PosixFilePermission.OTHERS_EXECUTE); + if ((externalFileAttributes & 0400) > 0) { + perms.add(PosixFilePermission.OWNER_READ); + } + if ((externalFileAttributes & 0200) > 0) { + perms.add(PosixFilePermission.OWNER_WRITE); + } + if ((externalFileAttributes & 0100) > 0) { + perms.add(PosixFilePermission.OWNER_EXECUTE); + } + if ((externalFileAttributes & 0040) > 0) { + perms.add(PosixFilePermission.GROUP_READ); + } + if ((externalFileAttributes & 0020) > 0) { + perms.add(PosixFilePermission.GROUP_WRITE); + } + if ((externalFileAttributes & 0010) > 0) { + perms.add(PosixFilePermission.GROUP_EXECUTE); + } + if ((externalFileAttributes & 0004) > 0) { + perms.add(PosixFilePermission.OTHERS_READ); + } + if ((externalFileAttributes & 0002) > 0) { + perms.add(PosixFilePermission.OTHERS_WRITE); + } + if ((externalFileAttributes & 0001) > 0) { + perms.add(PosixFilePermission.OTHERS_EXECUTE); + } } long pos = entryBytes != null ? ZipFileSliceReader.getInt(entryBytes, entOff + 42) @@ -718,7 +739,8 @@ private void readCentralDirectory(final ZipFileSliceReader zipFileSliceReader, f // Add zip entry final FastZipEntry entry = new FastZipEntry(this, locHeaderPos, entryNameSanitized, isDeflated, - compressedSize, uncompressedSize, physicalZipFile.nestedJarHandler, lastModifiedCalendar.getTimeInMillis(), perms); + compressedSize, uncompressedSize, physicalZipFile.nestedJarHandler, + lastModifiedCalendar.getTimeInMillis(), perms); entries.add(entry); // Record manifest entry diff --git a/src/test/java/io/github/classgraph/issues/issue364/Issue364Test.java b/src/test/java/io/github/classgraph/issues/issue364/Issue364Test.java index 4394cec4f..d669164e5 100644 --- a/src/test/java/io/github/classgraph/issues/issue364/Issue364Test.java +++ b/src/test/java/io/github/classgraph/issues/issue364/Issue364Test.java @@ -28,15 +28,16 @@ */ package io.github.classgraph.issues.issue364; -import io.github.classgraph.ClassGraph; -import io.github.classgraph.ScanResult; -import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; import java.net.URL; import java.net.URLClassLoader; import java.nio.file.attribute.PosixFilePermission; -import static org.assertj.core.api.Assertions.assertThat; +import org.junit.jupiter.api.Test; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ScanResult; /** * Issue364Test. @@ -53,15 +54,30 @@ public void testNoPermissions() { final URL aJarURL = classLoader.getResource(aJarName); final URLClassLoader overrideClassLoader = new URLClassLoader(new URL[] { aJarURL }); - try (ScanResult result = new ClassGraph().overrideClassLoaders(overrideClassLoader).ignoreParentClassLoaders().scan()) { - assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/all").get(0).getLastModified()).isEqualTo(1434543812000L); - assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/all").get(0).getPosixFilePermissions()).isNull(); - assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/groupreadwrite").get(0).getLastModified()).isEqualTo(1434557162000L); - assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/groupreadwrite").get(0).getPosixFilePermissions()).isNull(); - assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/owneronlyread").get(0).getLastModified()).isEqualTo(1434557150000L); - assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/owneronlyread").get(0).getPosixFilePermissions()).isNull(); - assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/ownerreadwrite").get(0).getLastModified()).isEqualTo(1434543812000L); - assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/ownerreadwrite").get(0).getPosixFilePermissions()).isNull(); + try (ScanResult result = new ClassGraph().overrideClassLoaders(overrideClassLoader) + .ignoreParentClassLoaders().scan()) { + assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/all") + .get(0).getLastModified()).isEqualTo(1434543812000L); + assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/all") + .get(0).getPosixFilePermissions()).isNull(); + assertThat(result + .getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/groupreadwrite") + .get(0).getLastModified()).isEqualTo(1434557162000L); + assertThat(result + .getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/groupreadwrite") + .get(0).getPosixFilePermissions()).isNull(); + assertThat(result + .getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/owneronlyread") + .get(0).getLastModified()).isEqualTo(1434557150000L); + assertThat(result + .getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/owneronlyread") + .get(0).getPosixFilePermissions()).isNull(); + assertThat(result + .getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/ownerreadwrite") + .get(0).getLastModified()).isEqualTo(1434543812000L); + assertThat(result + .getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/ownerreadwrite") + .get(0).getPosixFilePermissions()).isNull(); } } @@ -75,38 +91,49 @@ public void testPermissions() { final URL aJarURL = classLoader.getResource(aJarName); final URLClassLoader overrideClassLoader = new URLClassLoader(new URL[] { aJarURL }); - try (ScanResult result = new ClassGraph().overrideClassLoaders(overrideClassLoader).ignoreParentClassLoaders().scan()) { - assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/all").get(0).getLastModified()).isEqualTo(1434543812000L); - assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/all").get(0).getPosixFilePermissions()).containsExactlyInAnyOrder( - PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE, PosixFilePermission.OWNER_EXECUTE, - PosixFilePermission.GROUP_READ, PosixFilePermission.GROUP_WRITE, PosixFilePermission.GROUP_EXECUTE, - PosixFilePermission.OTHERS_READ, PosixFilePermission.OTHERS_WRITE, PosixFilePermission.OTHERS_EXECUTE - ); + try (ScanResult result = new ClassGraph().overrideClassLoaders(overrideClassLoader) + .ignoreParentClassLoaders().scan()) { + assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/all") + .get(0).getLastModified()).isEqualTo(1434543812000L); + assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/all") + .get(0).getPosixFilePermissions()).containsExactlyInAnyOrder(PosixFilePermission.OWNER_READ, + PosixFilePermission.OWNER_WRITE, PosixFilePermission.OWNER_EXECUTE, + PosixFilePermission.GROUP_READ, PosixFilePermission.GROUP_WRITE, + PosixFilePermission.GROUP_EXECUTE, PosixFilePermission.OTHERS_READ, + PosixFilePermission.OTHERS_WRITE, PosixFilePermission.OTHERS_EXECUTE); - assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/execute").get(0).getLastModified()).isEqualTo(1434557130000L); - assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/execute").get(0).getPosixFilePermissions()).containsExactlyInAnyOrder( - PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE, PosixFilePermission.OWNER_EXECUTE, - PosixFilePermission.GROUP_READ, PosixFilePermission.GROUP_EXECUTE, - PosixFilePermission.OTHERS_READ, PosixFilePermission.OTHERS_EXECUTE - ); + assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/execute") + .get(0).getLastModified()).isEqualTo(1434557130000L); + assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/execute") + .get(0).getPosixFilePermissions()).containsExactlyInAnyOrder(PosixFilePermission.OWNER_READ, + PosixFilePermission.OWNER_WRITE, PosixFilePermission.OWNER_EXECUTE, + PosixFilePermission.GROUP_READ, PosixFilePermission.GROUP_EXECUTE, + PosixFilePermission.OTHERS_READ, PosixFilePermission.OTHERS_EXECUTE); - assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/groupreadwrite").get(0).getLastModified()).isEqualTo(1434557162000L); - assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/groupreadwrite").get(0).getPosixFilePermissions()).containsExactlyInAnyOrder( - PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE, - PosixFilePermission.GROUP_READ, PosixFilePermission.GROUP_WRITE - ); + assertThat(result + .getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/groupreadwrite") + .get(0).getLastModified()).isEqualTo(1434557162000L); + assertThat(result + .getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/groupreadwrite") + .get(0).getPosixFilePermissions()).containsExactlyInAnyOrder(PosixFilePermission.OWNER_READ, + PosixFilePermission.OWNER_WRITE, PosixFilePermission.GROUP_READ, + PosixFilePermission.GROUP_WRITE); - assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/owneronlyread").get(0).getLastModified()).isEqualTo(1434557152000L); - assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/owneronlyread").get(0).getPosixFilePermissions()).containsExactlyInAnyOrder( - PosixFilePermission.OWNER_READ - ); + assertThat(result + .getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/owneronlyread") + .get(0).getLastModified()).isEqualTo(1434557152000L); + assertThat(result + .getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/owneronlyread") + .get(0).getPosixFilePermissions()).containsExactlyInAnyOrder(PosixFilePermission.OWNER_READ); - assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/ownerreadwrite").get(0).getLastModified()).isEqualTo(1434543812000L); - assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/ownerreadwrite").get(0).getPosixFilePermissions()).containsExactlyInAnyOrder( - PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE, - PosixFilePermission.GROUP_READ, - PosixFilePermission.OTHERS_READ - ); + assertThat(result + .getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/ownerreadwrite") + .get(0).getLastModified()).isEqualTo(1434543812000L); + assertThat(result + .getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/ownerreadwrite") + .get(0).getPosixFilePermissions()).containsExactlyInAnyOrder(PosixFilePermission.OWNER_READ, + PosixFilePermission.OWNER_WRITE, PosixFilePermission.GROUP_READ, + PosixFilePermission.OTHERS_READ); } } } From 94a9eea5134a1ca15c4464e504155a232bd01252 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 14 Aug 2019 01:14:05 -0600 Subject: [PATCH 0261/1778] Add POSIX file perms and timestamps for dir-based resources (#364) --- .../io/github/classgraph/ClasspathElementDir.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementDir.java b/src/main/java/io/github/classgraph/ClasspathElementDir.java index b6786b366..51654a746 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementDir.java @@ -38,6 +38,8 @@ import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; +import java.nio.file.attribute.PosixFileAttributes; +import java.nio.file.attribute.PosixFilePermission; import java.util.AbstractMap.SimpleEntry; import java.util.Arrays; import java.util.HashSet; @@ -152,7 +154,15 @@ void open(final WorkQueue workQueue, final LogNode log) * @return the resource */ private Resource newResource(final String relativePath, final File classpathResourceFile) { - return new Resource(this, classpathResourceFile.length()) { + Set posixFilePermissions = null; + try { + posixFilePermissions = Files.readAttributes(classpathResourceFile.toPath(), PosixFileAttributes.class) + .permissions(); + } catch (UnsupportedOperationException | IOException | SecurityException e) { + // POSIX attributes not supported + } + return new Resource(this, classpathResourceFile.length(), classpathResourceFile.lastModified(), + posixFilePermissions) { /** The random access file. */ private RandomAccessFile randomAccessFile; From c53bc2d9801252aea2cf9fda461f624f6ef197eb Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 14 Aug 2019 01:16:21 -0600 Subject: [PATCH 0262/1778] [maven-release-plugin] prepare release classgraph-4.8.44 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index c06256409..56b274cf1 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.44-SNAPSHOT + 4.8.44 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.44 From 27ed48d5d3a14774b93cb300a42fa66d408dd914 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 14 Aug 2019 01:17:12 -0600 Subject: [PATCH 0263/1778] [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 56b274cf1..d96ca8dbc 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.44 + 4.8.45-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.44 + HEAD From 831bf26538794786b9305eb060d08ae241151c6d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 14 Aug 2019 01:28:09 -0600 Subject: [PATCH 0264/1778] Update Javadoc (#364) --- .../java/io/github/classgraph/Resource.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/github/classgraph/Resource.java b/src/main/java/io/github/classgraph/Resource.java index 8d173b334..7d4fb3c03 100644 --- a/src/main/java/io/github/classgraph/Resource.java +++ b/src/main/java/io/github/classgraph/Resource.java @@ -540,7 +540,16 @@ public long getLength() { } /** - * Get the last modified millis since the epoch + * Get the last modified time for the resource, in milliseconds since the epoch. This time is obtained from the + * directory entry, if this resource is a file on disk, or from the zipfile central directory, if this resource + * is a zipfile entry. Timestamps are not available for resources obtained from system modules or jlink'd + * modules. + * + *

+ * Note: The ZIP format has no notion of timezone, so timestamps are only meaningful if it is known what + * timezone they were created in. We arbitrarily assume that zipfile timestamps are in the UTC timezone. This + * may be a wrong assumption, so you may need to apply a timezone correction if you know the timezone used by + * the zipfile creator. * * @return The millis since the epoch indicating the date / time that this file resource was last modified. * Returns 0L if the last modified date is unknown. @@ -550,9 +559,12 @@ public long getLastModified() { } /** - * Get the Posix File Permissions for the resource + * Get the POSIX file permissions for the resource. POSIX file permissions are obtained from the directory + * entry, if this resource is a file on disk, or from the zipfile central directory, if this resource is a + * zipfile entry. POSIX file permissions are not available for resources obtained from system modules or jlink'd + * modules, and may not be available on non-POSIX-compliant operating systems or non-POSIX filesystems. * - * @return The set of PosixFilePermission for the resource or null if they are unknown + * @return The set of {@link PosixFilePermission} permission flags for the resource, or null if unknown. */ public Set getPosixFilePermissions() { return posixFilePermissions; From 52f30db5eab16256c6b4c1fcc347714bbac11e18 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 14 Aug 2019 02:37:50 -0600 Subject: [PATCH 0265/1778] Keep static analyzers happy --- .../io/github/classgraph/ArrayTypeSignature.java | 6 +++--- .../io/github/classgraph/ClassTypeSignature.java | 6 +++--- .../io/github/classgraph/ClasspathElementZip.java | 14 +++++++------- src/main/java/io/github/classgraph/InfoList.java | 15 ++++++++++++--- .../classgraph/PotentiallyUnmodifiableList.java | 12 ++++++++++++ .../io/github/classgraph/ScanResultObject.java | 4 ++-- 6 files changed, 39 insertions(+), 18 deletions(-) diff --git a/src/main/java/io/github/classgraph/ArrayTypeSignature.java b/src/main/java/io/github/classgraph/ArrayTypeSignature.java index 07913b740..f25f35d61 100644 --- a/src/main/java/io/github/classgraph/ArrayTypeSignature.java +++ b/src/main/java/io/github/classgraph/ArrayTypeSignature.java @@ -170,11 +170,11 @@ protected ClassInfo getClassInfo() { public ArrayClassInfo getArrayClassInfo() { if (arrayClassInfo == null) { if (scanResult != null) { - final String className = getClassName(); + final String clsName = getClassName(); // Cache ArrayClassInfo instances using scanResult.classNameToClassInfo, if scanResult is available - arrayClassInfo = (ArrayClassInfo) scanResult.classNameToClassInfo.get(className); + arrayClassInfo = (ArrayClassInfo) scanResult.classNameToClassInfo.get(clsName); if (arrayClassInfo == null) { - scanResult.classNameToClassInfo.put(className, arrayClassInfo = new ArrayClassInfo(this)); + scanResult.classNameToClassInfo.put(clsName, arrayClassInfo = new ArrayClassInfo(this)); arrayClassInfo.setScanResult(this.scanResult); } } else { diff --git a/src/main/java/io/github/classgraph/ClassTypeSignature.java b/src/main/java/io/github/classgraph/ClassTypeSignature.java index a3d6963e3..387ecef45 100644 --- a/src/main/java/io/github/classgraph/ClassTypeSignature.java +++ b/src/main/java/io/github/classgraph/ClassTypeSignature.java @@ -223,9 +223,9 @@ protected void findReferencedClassInfo(final Map classNameToC final Set refdClassNames = new HashSet<>(); findReferencedClassNames(refdClassNames); for (final String refdClassName : refdClassNames) { - final ClassInfo classInfo = ClassInfo.getOrCreateClassInfo(refdClassName, classNameToClassInfo); - classInfo.scanResult = scanResult; - refdClassInfo.add(classInfo); + final ClassInfo clsInfo = ClassInfo.getOrCreateClassInfo(refdClassName, classNameToClassInfo); + clsInfo.scanResult = scanResult; + refdClassInfo.add(clsInfo); } } diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index e9211c58d..95c8434ae 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -216,20 +216,20 @@ void open(final WorkQueue workQueue, final LogNode log) .split(" ")) { if (!childClassPathEltPathRelative.isEmpty()) { // Resolve Class-Path entry relative to containing dir - String childClassPathEltPath = FastPathResolver.resolve(jarParentDir, + final String childClassPathEltPath = FastPathResolver.resolve(jarParentDir, childClassPathEltPathRelative); // If this is a nested jar, prepend outer jar prefix final ZipFileSlice parentZipFileSlice = logicalZipFile.getParentZipFileSlice(); - if (parentZipFileSlice != null) { - childClassPathEltPath = parentZipFileSlice.getPath() - + (childClassPathEltPath.startsWith("/") ? "!" : "!/") + childClassPathEltPath; - } + final String childClassPathEltPathWithPrefix = parentZipFileSlice == null + ? childClassPathEltPath + : parentZipFileSlice.getPath() + (childClassPathEltPath.startsWith("/") ? "!" : "!/") + + childClassPathEltPath; // Only add child classpath elements once - if (scheduledChildClasspathElements.add(childClassPathEltPath)) { + if (scheduledChildClasspathElements.add(childClassPathEltPathWithPrefix)) { // Schedule child classpath element for scanning workQueue.addWorkUnit( // new ClasspathEntryWorkUnit( - /* rawClasspathEntry = */ new SimpleEntry<>(childClassPathEltPath, + /* rawClasspathEntry = */ new SimpleEntry<>(childClassPathEltPathWithPrefix, classLoader), /* parentClasspathElement = */ this, /* orderWithinParentClasspathElement = */ diff --git a/src/main/java/io/github/classgraph/InfoList.java b/src/main/java/io/github/classgraph/InfoList.java index e9745780a..51d82ae98 100644 --- a/src/main/java/io/github/classgraph/InfoList.java +++ b/src/main/java/io/github/classgraph/InfoList.java @@ -43,9 +43,6 @@ public class InfoList extends PotentiallyUnmodifiableList /** serialVersionUID. */ static final long serialVersionUID = 1L; - /** Whether or not the list is modifiable. */ - boolean modifiable = true; - /** * Constructor. */ @@ -73,6 +70,18 @@ public class InfoList extends PotentiallyUnmodifiableList super(infoCollection); } + // Keep Scrutinizer happy + @Override + public boolean equals(final Object o) { + return super.equals(o); + } + + // Keep Scrutinizer happy + @Override + public int hashCode() { + return super.hashCode(); + } + // ------------------------------------------------------------------------------------------------------------- /** diff --git a/src/main/java/io/github/classgraph/PotentiallyUnmodifiableList.java b/src/main/java/io/github/classgraph/PotentiallyUnmodifiableList.java index 1183fab64..d8b70a66e 100644 --- a/src/main/java/io/github/classgraph/PotentiallyUnmodifiableList.java +++ b/src/main/java/io/github/classgraph/PotentiallyUnmodifiableList.java @@ -73,6 +73,18 @@ class PotentiallyUnmodifiableList extends ArrayList { super(collection); } + // Keep Scrutinizer happy + @Override + public boolean equals(final Object o) { + return super.equals(o); + } + + // Keep Scrutinizer happy + @Override + public int hashCode() { + return super.hashCode(); + } + /** Make this list unmodifiable. */ void makeUnmodifiable() { modifiable = false; diff --git a/src/main/java/io/github/classgraph/ScanResultObject.java b/src/main/java/io/github/classgraph/ScanResultObject.java index c29d152e1..a65edf28b 100644 --- a/src/main/java/io/github/classgraph/ScanResultObject.java +++ b/src/main/java/io/github/classgraph/ScanResultObject.java @@ -57,7 +57,7 @@ void setScanResult(final ScanResult scanResult) { } /** - * Get {@link ClassInfo} objects for any classes referenced in the type descriptor or type signature. + * Get {@link ClassInfo} objects for any classes referenced by this object. * * @return the referenced class info. */ @@ -70,7 +70,7 @@ final Set findReferencedClassInfo() { } /** - * Get {@link ClassInfo} objects for any classes referenced in the type descriptor or type signature. + * Get {@link ClassInfo} objects for any classes referenced by this object. * * @param classNameToClassInfo * the map from class name to {@link ClassInfo}. From c46abd517578698979300533678e9d67c0a33763 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 17 Aug 2019 03:34:39 -0600 Subject: [PATCH 0266/1778] Fix NPE on empty packages (Fixes #366). --- src/main/java/io/github/classgraph/PackageInfo.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/github/classgraph/PackageInfo.java b/src/main/java/io/github/classgraph/PackageInfo.java index 17a57f799..15d2269ba 100644 --- a/src/main/java/io/github/classgraph/PackageInfo.java +++ b/src/main/java/io/github/classgraph/PackageInfo.java @@ -204,7 +204,7 @@ public int compare(final PackageInfo o1, final PackageInfo o2) { * in this package. */ public ClassInfo getClassInfo(final String className) { - return memberClassNameToClassInfo.get(className); + return memberClassNameToClassInfo == null ? null : memberClassNameToClassInfo.get(className); } /** @@ -213,7 +213,8 @@ public ClassInfo getClassInfo(final String className) { * @return the {@link ClassInfo} objects for all classes that are members of this package. */ public ClassInfoList getClassInfo() { - return new ClassInfoList(new HashSet<>(memberClassNameToClassInfo.values()), /* sortByName = */ true); + return memberClassNameToClassInfo == null ? ClassInfoList.EMPTY_LIST + : new ClassInfoList(new HashSet<>(memberClassNameToClassInfo.values()), /* sortByName = */ true); } /** @@ -223,7 +224,9 @@ public ClassInfoList getClassInfo() { * the reachable class info */ private void obtainClassInfoRecursive(final Set reachableClassInfo) { - reachableClassInfo.addAll(memberClassNameToClassInfo.values()); + if (memberClassNameToClassInfo != null) { + reachableClassInfo.addAll(memberClassNameToClassInfo.values()); + } for (final PackageInfo subPackageInfo : getChildren()) { subPackageInfo.obtainClassInfoRecursive(reachableClassInfo); } From 4e561b99a3b9747b0144090591cd5249c30f2398 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 17 Aug 2019 03:35:07 -0600 Subject: [PATCH 0267/1778] Source > Cleanup --- .../io/github/classgraph/fastzipfilereader/FastZipEntry.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 c79b7495f..c47cd1581 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/FastZipEntry.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/FastZipEntry.java @@ -231,7 +231,7 @@ public boolean canGetAsSlice() throws IOException, InterruptedException { final long dataStartOffsetWithinPhysicalZipFile = getEntryDataStartOffsetWithinPhysicalZipFile(); return !isDeflated // && dataStartOffsetWithinPhysicalZipFile / FileUtils.MAX_BUFFER_SIZE // - == (dataStartOffsetWithinPhysicalZipFile + uncompressedSize) / FileUtils.MAX_BUFFER_SIZE; + == (dataStartOffsetWithinPhysicalZipFile + uncompressedSize) / FileUtils.MAX_BUFFER_SIZE; } /** From 1e0116329274262556294aff3e0ffa7e3102efcc Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 17 Aug 2019 03:36:00 -0600 Subject: [PATCH 0268/1778] [maven-release-plugin] prepare release classgraph-4.8.45 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index d96ca8dbc..00c86cb3c 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.45-SNAPSHOT + 4.8.45 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.45 From ce90450785f5365ae1e2d6d69b46c5b5b9e2566d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 17 Aug 2019 03:36:07 -0600 Subject: [PATCH 0269/1778] [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 00c86cb3c..07892c30f 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.45 + 4.8.46-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.45 + HEAD From ad72044c61697573a812b58c6a6eb8562018967c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 22 Aug 2019 01:37:00 -0600 Subject: [PATCH 0270/1778] Rename LICENSE to LICENSE-ClassGraph.txt to preserve license in fat jars --- LICENSE => LICENSE-ClassGraph.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename LICENSE => LICENSE-ClassGraph.txt (100%) diff --git a/LICENSE b/LICENSE-ClassGraph.txt similarity index 100% rename from LICENSE rename to LICENSE-ClassGraph.txt From 644e899fe551c1399281e4f40cf1096944f7c415 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 22 Aug 2019 02:18:03 -0600 Subject: [PATCH 0271/1778] Copy license file to built jar --- .classpath | 6 +++++- pom.xml | 52 +++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 50 insertions(+), 8 deletions(-) diff --git a/.classpath b/.classpath index 6ba1358a6..5576b4361 100644 --- a/.classpath +++ b/.classpath @@ -31,6 +31,10 @@ - + + + + + diff --git a/pom.xml b/pom.xml index 07892c30f..e017b9375 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,7 @@ - + 4.0.0 io.github.classgraph @@ -56,7 +59,7 @@ - + @@ -103,6 +106,10 @@ org.apache.maven.plugins maven-javadoc-plugin + + org.apache.maven.plugins + maven-resources-plugin + org.sonatype.plugins nexus-staging-maven-plugin @@ -137,7 +144,8 @@ - + org.codehaus.mojo.signature @@ -168,7 +176,7 @@ - + 7 7 @@ -246,14 +254,17 @@ - /** ${project.name}: ${project.description} ( ${project.url} ) */ + /** ${project.name}: ${project.description} ( ${project.url} ) + */ module io.github.classgraph { exports io.github.classgraph; - requires java.xml; + requires + java.xml; requires jdk.unsupported; requires java.management; - requires java.logging; + requires + java.logging; } @@ -283,6 +294,33 @@ + + + maven-resources-plugin + 3.1.0 + + + copy-license + process-resources + + copy-resources + + + ${basedir}/target/classes + + + ${basedir} + false + + LICENSE-ClassGraph.txt + + + + + + + + org.apache.maven.plugins From 88725470151e3ef0dbbaf78ab90bca683ada7f08 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 22 Aug 2019 02:20:51 -0600 Subject: [PATCH 0272/1778] Reorder plugins --- pom.xml | 113 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 57 insertions(+), 56 deletions(-) diff --git a/pom.xml b/pom.xml index e017b9375..9931cfe31 100644 --- a/pom.xml +++ b/pom.xml @@ -70,6 +70,10 @@ + + org.apache.maven.plugins + maven-resources-plugin + org.apache.maven.plugins maven-compiler-plugin @@ -106,10 +110,6 @@ org.apache.maven.plugins maven-javadoc-plugin - - org.apache.maven.plugins - maven-resources-plugin - org.sonatype.plugins nexus-staging-maven-plugin @@ -123,37 +123,28 @@ + - org.apache.maven.plugins - maven-enforcer-plugin - 3.0.0-M2 - - - org.codehaus.mojo - animal-sniffer-enforcer-rule - 1.17 - - + maven-resources-plugin + 3.1.0 - - check-signatures - test + copy-license + process-resources - enforce + copy-resources - - - - org.codehaus.mojo.signature - - java17 - 1.0 - - - + ${basedir}/target/classes + + + ${basedir} + false + + LICENSE-ClassGraph.txt + + + @@ -197,6 +188,43 @@ + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.0.0-M2 + + + org.codehaus.mojo + animal-sniffer-enforcer-rule + 1.17 + + + + + + check-signatures + test + + enforce + + + + + + org.codehaus.mojo.signature + + java17 + 1.0 + + + + + + + + org.apache.maven.plugins @@ -294,33 +322,6 @@ - - - maven-resources-plugin - 3.1.0 - - - copy-license - process-resources - - copy-resources - - - ${basedir}/target/classes - - - ${basedir} - false - - LICENSE-ClassGraph.txt - - - - - - - - org.apache.maven.plugins From b225b56d0eeee443c92b833bbf57e03a304169af Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 22 Aug 2019 02:21:30 -0600 Subject: [PATCH 0273/1778] [maven-release-plugin] prepare release classgraph-4.8.46 --- pom.xml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index 9931cfe31..39e488eca 100644 --- a/pom.xml +++ b/pom.xml @@ -1,12 +1,9 @@ - + 4.0.0 io.github.classgraph classgraph - 4.8.46-SNAPSHOT + 4.8.46 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 - HEAD + classgraph-4.8.46 @@ -210,8 +207,7 @@ - + org.codehaus.mojo.signature From 636833a5a5c48d3f4c209e52e6e2294e0d43f0a4 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 22 Aug 2019 02:21:37 -0600 Subject: [PATCH 0274/1778] [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 39e488eca..4a12c09cc 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.46 + 4.8.47-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.46 + HEAD From 8c267b885b2fb37575bcaea2b3d611612aee9467 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 22 Aug 2019 03:37:34 -0600 Subject: [PATCH 0275/1778] Also copy license file to javadoc --- pom.xml | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 4a12c09cc..feeb5d1df 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,7 @@ - + 4.0.0 io.github.classgraph @@ -126,7 +129,7 @@ 3.1.0 - copy-license + copy-license-to-target process-resources copy-resources @@ -144,6 +147,25 @@ + + copy-license-to-javadocs + process-resources + + copy-resources + + + ${basedir}/target/apidocs + + + ${basedir} + false + + LICENSE-ClassGraph.txt + + + + + @@ -207,7 +229,8 @@ - + org.codehaus.mojo.signature From 7baa692208a48ea9b47ba21fde076a863159c792 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 27 Aug 2019 15:24:48 -0600 Subject: [PATCH 0276/1778] Zip64 fixes (#367) --- .../fastzipfilereader/LogicalZipFile.java | 77 +++++++++++++------ 1 file changed, 52 insertions(+), 25 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 4d1e37abb..e9fdd7b89 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java @@ -454,12 +454,12 @@ private void readCentralDirectory(final ZipFileSliceReader zipFileSliceReader, f || numEnt != zipFileSliceReader.getShort(eocdPos + 10)) { throw new IOException("Multi-disk jarfiles not supported: " + getPath()); } - long cenSize = zipFileSliceReader.getInt(eocdPos + 12); + long cenSize = zipFileSliceReader.getInt(eocdPos + 12) & 0xffffffffL; if (cenSize > eocdPos) { throw new IOException( "Central directory size out of range: " + cenSize + " vs. " + eocdPos + ": " + getPath()); } - long cenOff = zipFileSliceReader.getInt(eocdPos + 16); + long cenOff = zipFileSliceReader.getInt(eocdPos + 16) & 0xffffffffL; long cenPos = eocdPos - cenSize; // Check for Zip64 End Of Central Directory Locator record @@ -479,29 +479,31 @@ private void readCentralDirectory(final ZipFileSliceReader zipFileSliceReader, f || numEnt64 != zipFileSliceReader.getLong(eocdPos64 + 32)) { throw new IOException("Multi-disk jarfiles not supported: " + getPath()); } - if (numEnt != numEnt64 && numEnt != 0xffff) { + if (numEnt == 0xffff) { + numEnt = numEnt64; + } else if (numEnt != numEnt64) { // Entry size mismatch -- trigger manual counting of entries numEnt = -1L; - } else { - numEnt = numEnt64; } final long cenSize64 = zipFileSliceReader.getLong(eocdPos64 + 40); - if (cenSize != cenSize64 && cenSize != 0xffffffff) { + if (cenSize == 0xffffffffL) { + cenSize = cenSize64; + } else if (cenSize != cenSize64) { throw new IOException( "Mismatch in central directory size: " + cenSize + " vs. " + cenSize64 + ": " + getPath()); } - cenSize = cenSize64; // Recalculate the central directory position cenPos = eocdPos64 - cenSize; final long cenOff64 = zipFileSliceReader.getLong(eocdPos64 + 48); - if (cenOff != cenOff64 && cenOff != 0xffffffff) { + if (cenOff == 0xffffffffL) { + cenOff = cenOff64; + } else if (cenOff != cenOff64) { throw new IOException( "Mismatch in central directory offset: " + cenOff + " vs. " + cenOff64 + ": " + getPath()); } - cenOff = cenOff64; } // Get offset of first local file header @@ -635,10 +637,10 @@ private void readCentralDirectory(final ZipFileSliceReader zipFileSliceReader, f lastModifiedCalendar.set(Calendar.MILLISECOND, 0); // Get compressed and uncompressed size - long compressedSize = entryBytes != null ? ZipFileSliceReader.getInt(entryBytes, entOff + 20) - : zipFileSliceReader.getInt(cenPos + entOff + 20); - long uncompressedSize = entryBytes != null ? ZipFileSliceReader.getInt(entryBytes, entOff + 24) - : zipFileSliceReader.getInt(cenPos + entOff + 24); + long compressedSize = (entryBytes != null ? ZipFileSliceReader.getInt(entryBytes, entOff + 20) + : zipFileSliceReader.getInt(cenPos + entOff + 20)) & 0xffffffffL; + long uncompressedSize = (entryBytes != null ? ZipFileSliceReader.getInt(entryBytes, entOff + 24) + : zipFileSliceReader.getInt(cenPos + entOff + 24)) & 0xffffffffL; // Get external file attributes final int externalFileAttributes = entryBytes != null @@ -683,6 +685,9 @@ private void readCentralDirectory(final ZipFileSliceReader zipFileSliceReader, f : zipFileSliceReader.getInt(cenPos + entOff + 42); // Check for Zip64 header in extra fields + // See: + // https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT + // https://github.com/LuaDist/zip/blob/master/proginfo/extrafld.txt if (extraFieldLen > 0) { for (int extraFieldOff = 0; extraFieldOff + 4 < extraFieldLen;) { final long tagOff = filenameEndOff + extraFieldOff; @@ -692,28 +697,50 @@ private void readCentralDirectory(final ZipFileSliceReader zipFileSliceReader, f : zipFileSliceReader.getShort(cenPos + tagOff + 2); if (extraFieldOff + 4 + size > extraFieldLen) { // Invalid size + if (log != null) { + log.log("Skipping zip entry with invalid extra field size: " + entryNameSanitized); + } break; } - if (tag == /* EXTID_ZIP64 */ 1 && size >= 24) { - final long uncompressedSizeL = entryBytes != null + if (tag == 1 && size >= 20) { + // Zip64 extended information extra field + final long uncompressedSize64 = entryBytes != null ? ZipFileSliceReader.getLong(entryBytes, tagOff + 4 + 0) : zipFileSliceReader.getLong(cenPos + tagOff + 4 + 0); - if (uncompressedSize == 0xffffffff) { - uncompressedSize = uncompressedSizeL; + if (uncompressedSize == 0xffffffffL) { + uncompressedSize = uncompressedSize64; + } else if (uncompressedSize != uncompressedSize64) { + throw new IOException("Mismatch in uncompressed size: " + uncompressedSize + " vs. " + + uncompressedSize64 + ": " + getPath()); } - final long compressedSizeL = entryBytes != null + final long compressedSize64 = entryBytes != null ? ZipFileSliceReader.getLong(entryBytes, tagOff + 4 + 8) : zipFileSliceReader.getLong(cenPos + tagOff + 4 + 8); - if (compressedSize == 0xffffffff) { - compressedSize = compressedSizeL; + if (compressedSize == 0xffffffffL) { + compressedSize = compressedSize64; + } else if (compressedSize != compressedSize64) { + throw new IOException("Mismatch in compressed size: " + compressedSize + " vs. " + + compressedSize64 + ": " + getPath()); } - final long posL = entryBytes != null - ? ZipFileSliceReader.getLong(entryBytes, tagOff + 4 + 16) - : zipFileSliceReader.getLong(cenPos + tagOff + 4 + 16); - if (pos == 0xffffffff) { - pos = posL; + // Only compressed size and uncompressed size are required fields + if (size >= 28) { + final long pos64 = entryBytes != null + ? ZipFileSliceReader.getLong(entryBytes, tagOff + 4 + 16) + : zipFileSliceReader.getLong(cenPos + tagOff + 4 + 16); + if (pos == 0xffffffffL) { + pos = pos64; + } else if (pos != pos64) { + throw new IOException( + "Mismatch in entry pos: " + pos + " vs. " + pos64 + ": " + getPath()); + } } break; + } else if (tag == 0x5455 && size >= 5) { + // Extended Unix timestamp + + } else if (tag == 0x7855 && size >= 0 /* TODO */) { + // Info-ZIP Unix + } extraFieldOff += 4 + size; } From 2db7598084dc42443384294f5ebce19e5f9f7c49 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 27 Aug 2019 15:33:38 -0600 Subject: [PATCH 0277/1778] Add index overflow checks --- .../fastzipfilereader/ZipFileSliceReader.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSliceReader.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSliceReader.java index cdc56fda9..51522a559 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSliceReader.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSliceReader.java @@ -152,6 +152,9 @@ int read(final long off, final byte[] buf, final int bufStart, final int numByte * the index out of bounds exception */ static int getShort(final byte[] arr, final long off) throws IndexOutOfBoundsException { + if (off > 0xffffffffL) { + throw new IndexOutOfBoundsException(); + } final int ioff = (int) off; if (ioff < 0 || ioff > arr.length - 2) { throw new IndexOutOfBoundsException(); @@ -192,6 +195,9 @@ int getShort(final long off) throws IOException, InterruptedException { * if an I/O exception occurs. */ static int getInt(final byte[] arr, final long off) throws IOException { + if (off > 0xffffffffL) { + throw new IndexOutOfBoundsException(); + } final int ioff = (int) off; if (ioff < 0 || ioff > arr.length - 4) { throw new IndexOutOfBoundsException(); @@ -238,6 +244,9 @@ int getInt(final long off) throws IOException, InterruptedException { * if an I/O exception occurs. */ static long getLong(final byte[] arr, final long off) throws IOException { + if (off > 0xffffffffL) { + throw new IndexOutOfBoundsException(); + } final int ioff = (int) off; if (ioff < 0 || ioff > arr.length - 8) { throw new IndexOutOfBoundsException(); @@ -294,6 +303,9 @@ long getLong(final long off) throws IOException, InterruptedException { * if an I/O exception occurs. */ static String getString(final byte[] arr, final long off, final int lenBytes) throws IOException { + if (off > 0xffffffffL) { + throw new IndexOutOfBoundsException(); + } final int ioff = (int) off; if (ioff < 0 || ioff > arr.length - lenBytes) { throw new IndexOutOfBoundsException(); From cdc4046e9df0d8d65091162eaee1fc2d9c3435fa Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 27 Aug 2019 15:44:22 -0600 Subject: [PATCH 0278/1778] remove .verbose() --- .../io/github/classgraph/issues/issue305/Issue305.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) 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 d849cdbbe..17ecba6e5 100644 --- a/src/test/java/io/github/classgraph/issues/issue305/Issue305.java +++ b/src/test/java/io/github/classgraph/issues/issue305/Issue305.java @@ -42,10 +42,9 @@ public void issue305() throws Exception { 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") })) - .verbose().scan()) { + try (ScanResult scanResult = new ClassGraph().overrideClassLoaders(new URLClassLoader( + new URL[] { Issue305.class.getClassLoader().getResource("class-path-manifest-entry.jar") })) + .scan()) { } final String systemErrMessages = new String(err.toByteArray()); From 117a73f860c1a35ff9977837602fd8bece3cfbad Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 27 Aug 2019 15:46:02 -0600 Subject: [PATCH 0279/1778] Revert previous change --- .../io/github/classgraph/issues/issue305/Issue305.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) 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 17ecba6e5..a44e0b3c2 100644 --- a/src/test/java/io/github/classgraph/issues/issue305/Issue305.java +++ b/src/test/java/io/github/classgraph/issues/issue305/Issue305.java @@ -42,9 +42,11 @@ public void issue305() throws Exception { 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") })) - .scan()) { + 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()); From 8fffeb0cadf5b0d86b22196757d4aa1248fbff7b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 27 Aug 2019 16:19:51 -0600 Subject: [PATCH 0280/1778] Support Unicode entry names --- .../fastzipfilereader/LogicalZipFile.java | 113 +++++++++++++----- .../fastzipfilereader/ZipFileSliceReader.java | 91 ++++++++++++++ 2 files changed, 172 insertions(+), 32 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 e9fdd7b89..b6fb39cff 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java @@ -32,6 +32,10 @@ import java.io.EOFException; import java.io.IOException; import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CodingErrorAction; import java.nio.charset.StandardCharsets; import java.nio.file.attribute.PosixFilePermission; import java.util.AbstractMap.SimpleEntry; @@ -556,6 +560,7 @@ private void readCentralDirectory(final ZipFileSliceReader zipFileSliceReader, f // Enumerate entries entries = new ArrayList<>((int) numEnt); FastZipEntry manifestZipEntry = null; + CharsetDecoder decoder = null; try { int entSize = 0; for (long entOff = 0; entOff + 46 <= cenSize; entOff += entSize) { @@ -585,8 +590,7 @@ private void readCentralDirectory(final ZipFileSliceReader zipFileSliceReader, f final String entryName = entryBytes != null ? ZipFileSliceReader.getString(entryBytes, filenameStartOff, filenameLen) : zipFileSliceReader.getString(cenPos + filenameStartOff, filenameLen); - final String entryNameSanitized = FileUtils.sanitizeEntryPath(entryName, - /* removeInitialSlash = */ true); + String entryNameSanitized = FileUtils.sanitizeEntryPath(entryName, /* removeInitialSlash = */ true); if (entryNameSanitized.isEmpty() || entryName.endsWith("/")) { // Skip directory entries continue; @@ -615,27 +619,6 @@ private void readCentralDirectory(final ZipFileSliceReader zipFileSliceReader, f } final boolean isDeflated = compressionMethod == /* deflated */ 8; - final int lastModifiedTime = entryBytes != null - ? ZipFileSliceReader.getShort(entryBytes, entOff + 12) - : zipFileSliceReader.getShort(cenPos + entOff + 12); - - final int lastModifiedDate = entryBytes != null - ? ZipFileSliceReader.getShort(entryBytes, entOff + 14) - : zipFileSliceReader.getShort(cenPos + entOff + 14); - - // MS-DOS Date & Time Format - final int lastModifiedSecond = (lastModifiedTime & 0b11111) * 2; - final int lastModifiedMinute = lastModifiedTime >> 5 & 0b111111; - final int lastModifiedHour = lastModifiedTime >> 11; - final int lastModifiedDay = lastModifiedDate & 0b11111; - final int lastModifiedMonth = (lastModifiedDate >> 5 & 0b111) - 1; - final int lastModifiedYear = (lastModifiedDate >> 9) + 1980; - - final Calendar lastModifiedCalendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - lastModifiedCalendar.set(lastModifiedYear, lastModifiedMonth, lastModifiedDay, lastModifiedHour, - lastModifiedMinute, lastModifiedSecond); - lastModifiedCalendar.set(Calendar.MILLISECOND, 0); - // Get compressed and uncompressed size long compressedSize = (entryBytes != null ? ZipFileSliceReader.getInt(entryBytes, entOff + 20) : zipFileSliceReader.getInt(cenPos + entOff + 20)) & 0xffffffffL; @@ -688,6 +671,7 @@ private void readCentralDirectory(final ZipFileSliceReader zipFileSliceReader, f // See: // https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT // https://github.com/LuaDist/zip/blob/master/proginfo/extrafld.txt + long lastModifiedMillis = 0L; if (extraFieldLen > 0) { for (int extraFieldOff = 0; extraFieldOff + 4 < extraFieldLen;) { final long tagOff = filenameEndOff + extraFieldOff; @@ -711,7 +695,7 @@ private void readCentralDirectory(final ZipFileSliceReader zipFileSliceReader, f uncompressedSize = uncompressedSize64; } else if (uncompressedSize != uncompressedSize64) { throw new IOException("Mismatch in uncompressed size: " + uncompressedSize + " vs. " - + uncompressedSize64 + ": " + getPath()); + + uncompressedSize64 + ": " + entryNameSanitized); } final long compressedSize64 = entryBytes != null ? ZipFileSliceReader.getLong(entryBytes, tagOff + 4 + 8) @@ -720,7 +704,7 @@ private void readCentralDirectory(final ZipFileSliceReader zipFileSliceReader, f compressedSize = compressedSize64; } else if (compressedSize != compressedSize64) { throw new IOException("Mismatch in compressed size: " + compressedSize + " vs. " - + compressedSize64 + ": " + getPath()); + + compressedSize64 + ": " + entryNameSanitized); } // Only compressed size and uncompressed size are required fields if (size >= 28) { @@ -730,22 +714,87 @@ private void readCentralDirectory(final ZipFileSliceReader zipFileSliceReader, f if (pos == 0xffffffffL) { pos = pos64; } else if (pos != pos64) { - throw new IOException( - "Mismatch in entry pos: " + pos + " vs. " + pos64 + ": " + getPath()); + throw new IOException("Mismatch in entry pos: " + pos + " vs. " + pos64 + ": " + + entryNameSanitized); } } break; + } else if (tag == 0x5455 && size >= 5) { // Extended Unix timestamp + final byte bits = entryBytes != null + ? ZipFileSliceReader.getByte(entryBytes, tagOff + 4 + 0) + : zipFileSliceReader.getByte(cenPos + tagOff + 4 + 0); + if ((bits & 1) == 1 && size >= 5 + 8) { + lastModifiedMillis = (entryBytes != null + ? ZipFileSliceReader.getLong(entryBytes, tagOff + 4 + 1) + : zipFileSliceReader.getLong(cenPos + tagOff + 4 + 1)) * 1000L; + } - } else if (tag == 0x7855 && size >= 0 /* TODO */) { - // Info-ZIP Unix - + } else if (tag == 0x5855 && size >= 20) { + // Unix extra field (deprecated) + lastModifiedMillis = (entryBytes != null + ? ZipFileSliceReader.getLong(entryBytes, tagOff + 4 + 8) + : zipFileSliceReader.getLong(cenPos + tagOff + 4 + 8)) * 1000L; + // There are also optional UID and GID fields in this extra field (currently ignored) + + } else if (tag == 0x7855) { + // Info-ZIP Unix UID and GID fields (currently ignored) + + } else if (tag == 0x7075) { + // Info-ZIP Unicode path extra field + final byte version = entryBytes != null + ? ZipFileSliceReader.getByte(entryBytes, tagOff + 4 + 0) + : zipFileSliceReader.getByte(cenPos + tagOff + 4 + 0); + if (version != 1) { + throw new IOException("Unknown Unicode entry name format " + version + + " in extra field: " + entryNameSanitized); + } else if (size > 9) { + byte[] utf8Bytes = (entryBytes != null + ? ZipFileSliceReader.getBytes(entryBytes, tagOff + 9, size - 9) + : zipFileSliceReader.getBytes(cenPos + tagOff + 9, size - 9)); + if (decoder == null) { + decoder = StandardCharsets.UTF_8.newDecoder(); + decoder.onMalformedInput(CodingErrorAction.REPORT) + .onUnmappableCharacter(CodingErrorAction.REPORT); + } + try { + // Replace non-Unicode entry name with Unicode version + entryNameSanitized = decoder.decode(ByteBuffer.wrap(utf8Bytes)).toString(); + } catch (CharacterCodingException e) { + throw new IOException("Malformed Unicode entry name: " + entryNameSanitized); + } + } } extraFieldOff += 4 + size; } } + if (lastModifiedMillis == 0L) { + // If Unix timestamp was not provided, convertr zip entry timestamp from MS-DOS format + final int lastModifiedTime = entryBytes != null + ? ZipFileSliceReader.getShort(entryBytes, entOff + 12) + : zipFileSliceReader.getShort(cenPos + entOff + 12); + + final int lastModifiedDate = entryBytes != null + ? ZipFileSliceReader.getShort(entryBytes, entOff + 14) + : zipFileSliceReader.getShort(cenPos + entOff + 14); + + // MS-DOS Date & Time Format + final int lastModifiedSecond = (lastModifiedTime & 0b11111) * 2; + final int lastModifiedMinute = lastModifiedTime >> 5 & 0b111111; + final int lastModifiedHour = lastModifiedTime >> 11; + final int lastModifiedDay = lastModifiedDate & 0b11111; + final int lastModifiedMonth = (lastModifiedDate >> 5 & 0b111) - 1; + final int lastModifiedYear = (lastModifiedDate >> 9) + 1980; + + final Calendar lastModifiedCalendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + lastModifiedCalendar.set(lastModifiedYear, lastModifiedMonth, lastModifiedDay, lastModifiedHour, + lastModifiedMinute, lastModifiedSecond); + lastModifiedCalendar.set(Calendar.MILLISECOND, 0); + lastModifiedMillis = lastModifiedCalendar.getTimeInMillis(); + } + if (compressedSize < 0 || pos < 0) { continue; } @@ -766,8 +815,8 @@ private void readCentralDirectory(final ZipFileSliceReader zipFileSliceReader, f // Add zip entry final FastZipEntry entry = new FastZipEntry(this, locHeaderPos, entryNameSanitized, isDeflated, - compressedSize, uncompressedSize, physicalZipFile.nestedJarHandler, - lastModifiedCalendar.getTimeInMillis(), perms); + compressedSize, uncompressedSize, physicalZipFile.nestedJarHandler, lastModifiedMillis, + perms); entries.add(entry); // Record manifest entry diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSliceReader.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSliceReader.java index 51522a559..641c62b08 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSliceReader.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSliceReader.java @@ -140,6 +140,97 @@ int read(final long off, final byte[] buf, final int bufStart, final int numByte return totBytesRead == 0 && numBytesToRead > 0 ? -1 : totBytesRead; } + /** + * Get a byte subarray from a byte array. + * + * @param arr + * the byte array + * @param off + * the offset to start reading from + * @param len + * the number of bytes to read + * @return the byte subarray + * @throws IndexOutOfBoundsException + * the index out of bounds exception + */ + static byte[] getBytes(final byte[] arr, final long off, final int len) throws IndexOutOfBoundsException { + if (off > 0xffffffffL) { + throw new IndexOutOfBoundsException(); + } + final int ioff = (int) off; + if (ioff < 0 || ioff > arr.length - len) { + throw new IndexOutOfBoundsException(); + } + return Arrays.copyOfRange(arr, ioff, ioff + len); + } + + /** + * Get a byte array from the zipfile slice. + * + * @param off + * the offset to start reading from + * @param len + * the number of bytes to read + * @return the byte array + * @throws IOException + * if an I/O exception occurs. + * @throws InterruptedException + * if the thread was interrupted. + */ + byte[] getBytes(final long off, final int len) throws IOException, InterruptedException { + if (off < 0 || off > zipFileSlice.len - len) { + throw new IndexOutOfBoundsException(); + } + byte[] bytes = new byte[len]; + if (read(off, bytes, 0, len) < len) { + throw new EOFException("Unexpected EOF"); + } + return bytes; + } + + /** + * Get a byte from a byte array. + * + * @param arr + * the byte array + * @param off + * the offset to start reading from + * @return the byte + * @throws IndexOutOfBoundsException + * the index out of bounds exception + */ + static byte getByte(final byte[] arr, final long off) throws IndexOutOfBoundsException { + if (off > 0xffffffffL) { + throw new IndexOutOfBoundsException(); + } + final int ioff = (int) off; + if (ioff < 0 || ioff > arr.length - 1) { + throw new IndexOutOfBoundsException(); + } + return (byte) (arr[ioff] & 0xff); + } + + /** + * Get a byte from the zipfile slice. + * + * @param off + * the offset to start reading from + * @return the byte + * @throws IOException + * if an I/O exception occurs. + * @throws InterruptedException + * if the thread was interrupted. + */ + byte getByte(final long off) throws IOException, InterruptedException { + if (off < 0 || off > zipFileSlice.len - 1) { + throw new IndexOutOfBoundsException(); + } + if (read(off, scratch, 0, 1) < 1) { + throw new EOFException("Unexpected EOF"); + } + return (byte) (scratch[0] & 0xff); + } + /** * Get a short from a byte array. * From 97c070da26ccb1f2dfde0220a424a349ef5982e2 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 27 Aug 2019 16:20:18 -0600 Subject: [PATCH 0281/1778] Source > Cleanup --- .../github/classgraph/fastzipfilereader/LogicalZipFile.java | 4 ++-- .../classgraph/fastzipfilereader/ZipFileSliceReader.java | 2 +- 2 files changed, 3 insertions(+), 3 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 b6fb39cff..35c2f8a5f 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java @@ -750,7 +750,7 @@ private void readCentralDirectory(final ZipFileSliceReader zipFileSliceReader, f throw new IOException("Unknown Unicode entry name format " + version + " in extra field: " + entryNameSanitized); } else if (size > 9) { - byte[] utf8Bytes = (entryBytes != null + final byte[] utf8Bytes = (entryBytes != null ? ZipFileSliceReader.getBytes(entryBytes, tagOff + 9, size - 9) : zipFileSliceReader.getBytes(cenPos + tagOff + 9, size - 9)); if (decoder == null) { @@ -761,7 +761,7 @@ private void readCentralDirectory(final ZipFileSliceReader zipFileSliceReader, f try { // Replace non-Unicode entry name with Unicode version entryNameSanitized = decoder.decode(ByteBuffer.wrap(utf8Bytes)).toString(); - } catch (CharacterCodingException e) { + } catch (final CharacterCodingException e) { throw new IOException("Malformed Unicode entry name: " + entryNameSanitized); } } diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSliceReader.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSliceReader.java index 641c62b08..d3679b64f 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSliceReader.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSliceReader.java @@ -181,7 +181,7 @@ byte[] getBytes(final long off, final int len) throws IOException, InterruptedEx if (off < 0 || off > zipFileSlice.len - len) { throw new IndexOutOfBoundsException(); } - byte[] bytes = new byte[len]; + final byte[] bytes = new byte[len]; if (read(off, bytes, 0, len) < len) { throw new EOFException("Unexpected EOF"); } From 979fb1992b8dff418ceb02e90fcc23d178685def Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 28 Aug 2019 03:59:10 -0600 Subject: [PATCH 0282/1778] [maven-release-plugin] prepare release classgraph-4.8.47 --- pom.xml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index feeb5d1df..07e533d28 100644 --- a/pom.xml +++ b/pom.xml @@ -1,12 +1,9 @@ - + 4.0.0 io.github.classgraph classgraph - 4.8.47-SNAPSHOT + 4.8.47 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 - HEAD + classgraph-4.8.47 @@ -229,8 +226,7 @@ - + org.codehaus.mojo.signature From 00851cb7a7e9feab1f272f08752f2275e148bc0f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 28 Aug 2019 03:59:33 -0600 Subject: [PATCH 0283/1778] [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 07e533d28..4760b9045 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.47 + 4.8.48-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.47 + HEAD From 2d0bd3b2dcad3e1d72be1b3719c0ca14f924cba5 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 28 Aug 2019 04:16:07 -0600 Subject: [PATCH 0284/1778] Require minimum Maven version of 3.6.1 --- pom.xml | 98 ++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 58 insertions(+), 40 deletions(-) diff --git a/pom.xml b/pom.xml index 4760b9045..b308c8d1b 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,7 @@ - + 4.0.0 io.github.classgraph @@ -69,15 +72,15 @@ org.apache.maven.plugins - maven-resources-plugin + maven-enforcer-plugin org.apache.maven.plugins - maven-compiler-plugin + maven-resources-plugin org.apache.maven.plugins - maven-enforcer-plugin + maven-compiler-plugin org.apache.maven.plugins @@ -120,6 +123,57 @@ + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.0.0-M2 + + + org.codehaus.mojo + animal-sniffer-enforcer-rule + 1.17 + + + + + + enforce-versions + + enforce + + + + + 3.6.1 + + + + + + + check-signatures + test + + enforce + + + + + + org.codehaus.mojo.signature + + java17 + 1.0 + + + + + + + + maven-resources-plugin @@ -204,42 +258,6 @@ - - - org.apache.maven.plugins - maven-enforcer-plugin - 3.0.0-M2 - - - org.codehaus.mojo - animal-sniffer-enforcer-rule - 1.17 - - - - - - check-signatures - test - - enforce - - - - - - org.codehaus.mojo.signature - - java17 - 1.0 - - - - - - - - org.apache.maven.plugins From d3b5aebd2d9bfc8b98539f7ce157775cc5af6e95 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 28 Aug 2019 04:53:15 -0600 Subject: [PATCH 0285/1778] Update some plugin versions --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index b308c8d1b..a41c12e9d 100644 --- a/pom.xml +++ b/pom.xml @@ -269,7 +269,7 @@ org.apache.felix maven-bundle-plugin - 4.1.0 + 4.2.1 true @@ -375,7 +375,7 @@ org.apache.maven.plugins maven-source-plugin - 3.0.1 + 3.1.0 attach-sources From b534fcbcc6fa6574d53304e2895a4ae624b4b7dd Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 18 Sep 2019 03:59:13 -0600 Subject: [PATCH 0286/1778] Fix serialization of class references (#368) --- .../github/classgraph/json/FieldTypeInfo.java | 31 ++++++--- .../classgraph/json/JSONDeserializer.java | 14 ++++- .../classgraph/json/JSONSerializer.java | 9 +++ .../io/github/classgraph/json/JSONUtils.java | 8 ++- .../issues/issue368/Issue368Test.java | 63 +++++++++++++++++++ 5 files changed, 111 insertions(+), 14 deletions(-) create mode 100644 src/test/java/io/github/classgraph/issues/issue368/Issue368Test.java 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 84eaaf076..f511d66c7 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/FieldTypeInfo.java +++ b/src/main/java/nonapi/io/github/classgraph/json/FieldTypeInfo.java @@ -79,24 +79,26 @@ class FieldTypeInfo { * The Enum PrimitiveType. */ private enum PrimitiveType { - /** The non primitive. */ + /** Non-primitive type. */ NON_PRIMITIVE, - /** The integer. */ + /** Integer type. */ INTEGER, - /** The long. */ + /** Long type. */ LONG, - /** The short. */ + /** Short type. */ SHORT, - /** The double. */ + /** Double type. */ DOUBLE, - /** The float. */ + /** Float type. */ FLOAT, - /** The boolean. */ + /** Boolean type. */ BOOLEAN, - /** The byte. */ + /** Byte type. */ BYTE, - /** The character. */ - CHARACTER; + /** Character type. */ + CHARACTER, + /** Class reference */ + CLASS_REF; } /** @@ -161,6 +163,8 @@ public FieldTypeInfo(final Field field, final Type fieldTypePartiallyResolved, this.primitiveType = PrimitiveType.BYTE; } else if (fieldRawType == Character.TYPE) { this.primitiveType = PrimitiveType.CHARACTER; + } else if (fieldRawType == Class.class) { + this.primitiveType = PrimitiveType.CLASS_REF; } else { this.primitiveType = PrimitiveType.NON_PRIMITIVE; } @@ -263,6 +267,13 @@ void setFieldValue(final Object containingObj, final Object value) { case NON_PRIMITIVE: field.set(containingObj, value); break; + case CLASS_REF: + if (!(value instanceof Class)) { + throw new IllegalArgumentException( + "Expected value of type Class; got " + value.getClass().getName()); + } + field.set(containingObj, value); + break; case INTEGER: if (!(value instanceof Integer)) { throw new IllegalArgumentException( 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 7332bd1e1..e4887bb3e 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/JSONDeserializer.java +++ b/src/main/java/nonapi/io/github/classgraph/json/JSONDeserializer.java @@ -75,8 +75,18 @@ private static Object jsonBasicValueToObject(final Object jsonVal, final Type ex throw ClassGraphException.newClassGraphException("Expected a basic value type"); } if (expectedType instanceof ParameterizedType) { - // TODO: add support for Class reference values, which may be parameterized - throw new IllegalArgumentException("Got illegal ParameterizedType: " + expectedType); + if (((ParameterizedType) expectedType).getRawType().getClass() == Class.class) { + final String str = jsonVal.toString(); + final int idx = str.indexOf('<'); + final String className = str.substring(0, idx < 0 ? str.length() : idx); + try { + return Class.forName(className); + } catch (final ClassNotFoundException e) { + throw new IllegalArgumentException("Could not deserialize class reference " + jsonVal, e); + } + } else { + throw new IllegalArgumentException("Got illegal ParameterizedType: " + expectedType); + } } else if (!(expectedType instanceof Class)) { throw new IllegalArgumentException("Got illegal basic value type: " + expectedType); } 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 da8a3941b..9ba140c02 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/JSONSerializer.java +++ b/src/main/java/nonapi/io/github/classgraph/json/JSONSerializer.java @@ -182,6 +182,10 @@ private static void convertVals(final Object[] convertedVals, needToConvert[i] = false; } } + // Special handling for class references: Convert to class name string + if (val instanceof Class) { + convertedVals[i] = ((Class) val).getName(); + } } // Pass 2: Recursively convert items in standard objects, maps, collections and arrays to JSON objects. for (int i = 0; i < convertedVals.length; i++) { @@ -247,6 +251,11 @@ private static Object toJSONGraph(final Object obj, final Set, JSONObject> objToJSONVal, final boolean onlySerializePublicFields) { + // For class references, return class name as a string + if (obj instanceof Class) { + return ((Class) obj).getName(); + } + // For null and basic value types, just return value if (JSONUtils.isBasicValueType(obj)) { return obj; 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 8ec1d3357..86601c069 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/JSONUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/json/JSONUtils.java @@ -212,6 +212,8 @@ static Object getFieldValue(final Object containingObj, final Field field) return field.getByte(containingObj); } else if (fieldType == Character.TYPE) { return field.getChar(containingObj); + } else if (fieldType == Class.class) { + return field.get(containingObj); } else { return field.get(containingObj); } @@ -237,7 +239,8 @@ static boolean isBasicValueType(final Class cls) { || cls == Byte.class || cls == Byte.TYPE // || cls == Character.class || cls == Character.TYPE // || cls == Boolean.class || cls == Boolean.TYPE // - || cls.isEnum(); + || cls.isEnum() // + || cls == Class.class; } /** @@ -267,7 +270,8 @@ static boolean isBasicValueType(final Type type) { static boolean isBasicValueType(final Object obj) { return obj == null || obj instanceof String || obj instanceof Integer || obj instanceof Boolean || obj instanceof Long || obj instanceof Float || obj instanceof Double || obj instanceof Short - || obj instanceof Byte || obj instanceof Character || obj.getClass().isEnum(); + || obj instanceof Byte || obj instanceof Character || obj.getClass().isEnum() + || obj instanceof Class; } /** diff --git a/src/test/java/io/github/classgraph/issues/issue368/Issue368Test.java b/src/test/java/io/github/classgraph/issues/issue368/Issue368Test.java new file mode 100644 index 000000000..ab2e05a7d --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue368/Issue368Test.java @@ -0,0 +1,63 @@ +/* + * 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.issues.issue368; + +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.json.JSONDeserializer; +import nonapi.io.github.classgraph.json.JSONSerializer; + +/** + * Issue368Test. + */ +public class Issue368Test { + + public static class InnerClass { + public Class innerClassField = Issue368Test.class; + } + + /** + * Issue 368 test. + */ + @Test + public void issue368Test() { + try (ScanResult scanResult = new ClassGraph().whitelistPackages(Issue368Test.class.getPackage().getName()) + .enableAllInfo().scan()) { + final String json = JSONSerializer.serializeObject(new InnerClass()); + assertThat(json) + .isEqualTo("{\"innerClassField\":\"io.github.classgraph.issues.issue368.Issue368Test\"}"); + final InnerClass deserialized = JSONDeserializer.deserializeObject(InnerClass.class, json); + assertThat(deserialized.innerClassField == Issue368Test.class); + } + } +} From 9e9bff4f25614afb59dbc88e1dd25c190eb5f8bb Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 18 Sep 2019 04:05:40 -0600 Subject: [PATCH 0287/1778] [maven-release-plugin] prepare release classgraph-4.8.48 --- pom.xml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index a41c12e9d..59c3f897a 100644 --- a/pom.xml +++ b/pom.xml @@ -1,12 +1,9 @@ - + 4.0.0 io.github.classgraph classgraph - 4.8.48-SNAPSHOT + 4.8.48 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 - HEAD + classgraph-4.8.48 @@ -159,8 +156,7 @@ - + org.codehaus.mojo.signature From 52e5f09f040371259c3b55a703aee4ad0d3e5ccc Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 18 Sep 2019 04:05:51 -0600 Subject: [PATCH 0288/1778] [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 59c3f897a..999961841 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.48 + 4.8.49-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.48 + HEAD From a01605a617694967bdec66418901e641e31a4a0f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 18 Sep 2019 04:50:51 -0600 Subject: [PATCH 0289/1778] Update maven-gpg-plugin configuration for newer GnuPG --- pom.xml | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 999961841..5dae3b5c3 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,7 @@ - + 4.0.0 io.github.classgraph @@ -156,7 +159,8 @@ - + org.codehaus.mojo.signature @@ -525,10 +529,17 @@ sign-artifacts - verify + deploy sign + + true + + --pinentry-mode + loopback + + From f69af633e9d73191a02fa381d196681b4e1680d8 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 23 Sep 2019 01:38:30 -0600 Subject: [PATCH 0290/1778] Comment out Maven version check --- pom.xml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 5dae3b5c3..1013a2458 100644 --- a/pom.xml +++ b/pom.xml @@ -137,7 +137,7 @@ - + check-signatures @@ -534,10 +534,11 @@ sign - true + ${gpg.keyname} + ${gpg.keyname} - --pinentry-mode - loopback + --pinentry-mode + loopback From eeb5be5ad9a9b89e22cd8414be887347532f5ea0 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 23 Sep 2019 01:45:03 -0600 Subject: [PATCH 0291/1778] Drop version number --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1013a2458..6ef0c7503 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.github.classgraph classgraph - 4.8.49-SNAPSHOT + 4.8.48-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From cb67ec3e79b693d1b8bd4fa9097b5418757c7a92 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 23 Sep 2019 01:46:39 -0600 Subject: [PATCH 0292/1778] [maven-release-plugin] prepare release classgraph-4.8.48 --- pom.xml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index 6ef0c7503..cfd1138a1 100644 --- a/pom.xml +++ b/pom.xml @@ -1,12 +1,9 @@ - + 4.0.0 io.github.classgraph classgraph - 4.8.48-SNAPSHOT + 4.8.48 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 - HEAD + classgraph-4.8.48 @@ -159,8 +156,7 @@ - + org.codehaus.mojo.signature From 693ca01ed2e62030b906eb1f9978d18b7b4d75a1 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 23 Sep 2019 01:46:49 -0600 Subject: [PATCH 0293/1778] [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 cfd1138a1..6b14a23c8 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.48 + 4.8.49-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.48 + HEAD From 2bbb6c92fa1eea2f86a8f9777d0dc5adb256a873 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 23 Sep 2019 02:05:02 -0600 Subject: [PATCH 0294/1778] Restore Maven version --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 6b14a23c8..fdfb1e5c3 100644 --- a/pom.xml +++ b/pom.xml @@ -134,7 +134,7 @@ - + check-signatures From 8dd828829ef3b9306f7dfa6447ed86e56219b3e6 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 23 Sep 2019 02:41:27 -0600 Subject: [PATCH 0295/1778] Switch to BinTray --- pom.xml | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/pom.xml b/pom.xml index fdfb1e5c3..1d59f21a7 100644 --- a/pom.xml +++ b/pom.xml @@ -1,9 +1,12 @@ - + 4.0.0 io.github.classgraph classgraph - 4.8.49-SNAPSHOT + 4.8.48-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -39,13 +42,9 @@ - - ossrh - https://oss.sonatype.org/content/repositories/snapshots - - ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ + bintray + https://api.bintray.com/maven/classgraph/classgraph/classgraph @@ -156,7 +155,8 @@ - + org.codehaus.mojo.signature @@ -508,7 +508,7 @@ - + From d6575b345a39be73a415434193c99cd98481a1be Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 23 Sep 2019 02:43:51 -0600 Subject: [PATCH 0296/1778] [maven-release-plugin] prepare release classgraph-4.8.48 --- pom.xml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index 1d59f21a7..6c3c3857b 100644 --- a/pom.xml +++ b/pom.xml @@ -1,12 +1,9 @@ - + 4.0.0 io.github.classgraph classgraph - 4.8.48-SNAPSHOT + 4.8.48 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 - HEAD + classgraph-4.8.48 @@ -155,8 +152,7 @@ - + org.codehaus.mojo.signature From 5bd0fe26805eedfb88ee58aaad058197ada94b81 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 23 Sep 2019 02:44:00 -0600 Subject: [PATCH 0297/1778] [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 6c3c3857b..09602a9f0 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.48 + 4.8.49-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.48 + HEAD From af2f52b888f0f4ca944ccb8eeccde133e375116b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 23 Sep 2019 02:47:18 -0600 Subject: [PATCH 0298/1778] Drop version back again --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 09602a9f0..7744750bb 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.49-SNAPSHOT + 4.8.48-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From c606cdc4eebc5fb6113d1dece584caff83ae0d7d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 23 Sep 2019 02:48:02 -0600 Subject: [PATCH 0299/1778] Get rid of Sonatype plugin --- pom.xml | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/pom.xml b/pom.xml index 7744750bb..efd694d46 100644 --- a/pom.xml +++ b/pom.xml @@ -103,10 +103,6 @@ org.apache.maven.plugins maven-javadoc-plugin - - org.sonatype.plugins - nexus-staging-maven-plugin - org.apache.maven.plugins maven-release-plugin @@ -398,21 +394,6 @@ - - - - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.8 - true - - ossrh - https://oss.sonatype.org/ - true - - - org.apache.maven.plugins From 7b9dc90a709099fe8510fadee15781a6ca44b39d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 23 Sep 2019 02:49:25 -0600 Subject: [PATCH 0300/1778] [maven-release-plugin] prepare release classgraph-4.8.48 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index efd694d46..4fa490858 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.48-SNAPSHOT + 4.8.48 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.48 From 5bf76c78872e6345b876161fe6a37829771ae36f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 23 Sep 2019 02:49:33 -0600 Subject: [PATCH 0301/1778] [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 4fa490858..5fb6a8a3f 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.48 + 4.8.49-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.48 + HEAD From 48cc20f7f57dcd6d9b2a7b6801ac6437ce4adb84 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 23 Sep 2019 02:51:38 -0600 Subject: [PATCH 0302/1778] BinTray changes --- pom.xml | 38 ++++++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 5fb6a8a3f..beeb03272 100644 --- a/pom.xml +++ b/pom.xml @@ -1,9 +1,12 @@ - + 4.0.0 io.github.classgraph classgraph - 4.8.49-SNAPSHOT + 4.8.48-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -148,7 +151,8 @@ - + org.codehaus.mojo.signature @@ -485,7 +489,7 @@ - - --> + From bbda013d0362495984234a5847148e4bab8a7253 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 23 Sep 2019 02:52:43 -0600 Subject: [PATCH 0303/1778] [maven-release-plugin] prepare release classgraph-4.8.48 --- pom.xml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index beeb03272..b8394d378 100644 --- a/pom.xml +++ b/pom.xml @@ -1,12 +1,9 @@ - + 4.0.0 io.github.classgraph classgraph - 4.8.48-SNAPSHOT + 4.8.48 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 - HEAD + classgraph-4.8.48 @@ -151,8 +148,7 @@ - + org.codehaus.mojo.signature From b8cd831d5eb2940c6713ff648f59ce2b5f4c9baf Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 23 Sep 2019 02:52:52 -0600 Subject: [PATCH 0304/1778] [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 b8394d378..d0d5aeed8 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.48 + 4.8.49-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.48 + HEAD From 738597f6fc7ddb0295a2e50858365c488e53eb61 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 23 Sep 2019 06:11:52 -0600 Subject: [PATCH 0305/1778] Drop version number back down --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d0d5aeed8..936644d47 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.49-SNAPSHOT + 4.8.48-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From ec3f4ff444a127d1d99fb7733163a77bd78d81a9 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 23 Sep 2019 06:13:07 -0600 Subject: [PATCH 0306/1778] [maven-release-plugin] prepare release classgraph-4.8.48 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 936644d47..b8394d378 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.48-SNAPSHOT + 4.8.48 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.48 From 22bfa3772f6f4a8e12386de9e2e25e3e45b5d1d0 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 23 Sep 2019 06:13:16 -0600 Subject: [PATCH 0307/1778] [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 b8394d378..d0d5aeed8 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.48 + 4.8.49-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.48 + HEAD From 28abbcf8753819562b2ad6502514cc4f11f7d60a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 23 Sep 2019 06:18:26 -0600 Subject: [PATCH 0308/1778] Bintray fix --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d0d5aeed8..3446eda1b 100644 --- a/pom.xml +++ b/pom.xml @@ -41,7 +41,7 @@ bintray - https://api.bintray.com/maven/classgraph/classgraph/classgraph + https://api.bintray.com/maven/lukehutch/classgraph/classgraph From e7e053bdaadb2ee61f245e21be4ff7e94b40cb45 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 23 Sep 2019 06:19:00 -0600 Subject: [PATCH 0309/1778] Bump version back again --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3446eda1b..92047b335 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.49-SNAPSHOT + 4.8.48-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 052890fc4d6a7eebbaee59cc638ffea1ce3615cb Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 23 Sep 2019 06:22:40 -0600 Subject: [PATCH 0310/1778] [maven-release-plugin] prepare release classgraph-4.8.48 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 92047b335..719eaa185 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.48-SNAPSHOT + 4.8.48 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.48 From d8248b82a46f23f34ad452fa5e8d1116eb090f9c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 23 Sep 2019 06:22:50 -0600 Subject: [PATCH 0311/1778] [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 719eaa185..3446eda1b 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.48 + 4.8.49-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.48 + HEAD From b5c0f959d7e81d4eaad924531611f6f849f600f1 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 23 Sep 2019 06:27:19 -0600 Subject: [PATCH 0312/1778] Fix bintray package name --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3446eda1b..4790cf8c3 100644 --- a/pom.xml +++ b/pom.xml @@ -41,7 +41,7 @@ bintray - https://api.bintray.com/maven/lukehutch/classgraph/classgraph + https://api.bintray.com/maven/lukehutch/classgraph/io.github.classgraph:classgraph From 3b40c842e1fdeb0ec966073fe24448f4196b9c29 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 23 Sep 2019 06:27:39 -0600 Subject: [PATCH 0313/1778] Drop version number back down --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4790cf8c3..f5530692a 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.49-SNAPSHOT + 4.8.48-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 936336f2a4b5b5934ac0658a76b44d9f9b189c30 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 18 Sep 2019 04:05:51 -0600 Subject: [PATCH 0314/1778] Prepare for pushing to bintray rather than sonatype --- pom.xml | 61 ++++++++++++++++++++++++++++++--------------------------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/pom.xml b/pom.xml index 59c3f897a..f5530692a 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.48 + 4.8.48-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.48 + HEAD @@ -39,13 +39,9 @@ - - ossrh - https://oss.sonatype.org/content/repositories/snapshots - - ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ + bintray + https://api.bintray.com/maven/lukehutch/classgraph/io.github.classgraph:classgraph @@ -107,10 +103,6 @@ org.apache.maven.plugins maven-javadoc-plugin - - org.sonatype.plugins - nexus-staging-maven-plugin - org.apache.maven.plugins maven-release-plugin @@ -402,21 +394,6 @@ - - - - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.8 - true - - ossrh - https://oss.sonatype.org/ - true - - - org.apache.maven.plugins @@ -519,19 +496,45 @@ + maven-source-plugin + + + attach-sources + + jar + + + + + + maven-javadoc-plugin + + + attach-javadocs + + jar + + + + + From a738ea5ca5c47a420c60259136686b7b9d4276ad Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 23 Sep 2019 06:43:51 -0600 Subject: [PATCH 0315/1778] [maven-release-plugin] prepare release classgraph-4.8.48 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index f5530692a..db02a5f3b 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.48-SNAPSHOT + 4.8.48 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.48 From 90dd6d01eae3db23e5c04bfc5bb9f6391b884f0c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 23 Sep 2019 06:44:00 -0600 Subject: [PATCH 0316/1778] [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 db02a5f3b..4790cf8c3 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.48 + 4.8.49-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.48 + HEAD From 14effff94485527ae274a47f0650dc569d8f5eaf Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 23 Sep 2019 07:07:37 -0600 Subject: [PATCH 0317/1778] Fix bintray URL --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 4790cf8c3..b9b77fcc4 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.49-SNAPSHOT + 4.8.48-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -40,8 +40,8 @@ - bintray - https://api.bintray.com/maven/lukehutch/classgraph/io.github.classgraph:classgraph + bintray-classgraph-classgraph + https://api.bintray.com/maven/classgraph/classgraph/classgraph/;publish=1 From 51a8c20a53dc00260bba394d99d07862575adaef Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 23 Sep 2019 07:08:45 -0600 Subject: [PATCH 0318/1778] [maven-release-plugin] prepare release classgraph-4.8.48 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index b9b77fcc4..669f314f7 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.48-SNAPSHOT + 4.8.48 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.48 From 7ac5cd002ea45c28884ede51271d13cdf5b05975 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 23 Sep 2019 07:08:53 -0600 Subject: [PATCH 0319/1778] [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 669f314f7..348f46817 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.48 + 4.8.49-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.48 + HEAD From a52bdd12ea8a5d6b00512fbfcbff812db36720f4 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 26 Sep 2019 08:30:49 -0600 Subject: [PATCH 0320/1778] Add unit test submitted by @torstenkuhnhenne (#370) --- .../issues/issue370/Issue370Test.java | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 src/test/java/io/github/classgraph/issues/issue370/Issue370Test.java diff --git a/src/test/java/io/github/classgraph/issues/issue370/Issue370Test.java b/src/test/java/io/github/classgraph/issues/issue370/Issue370Test.java new file mode 100644 index 000000000..100b92403 --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue370/Issue370Test.java @@ -0,0 +1,76 @@ +/* + * 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.issues.issue370; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +import io.github.classgraph.AnnotationInfo; +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ClassInfo; +import io.github.classgraph.MethodInfo; +import io.github.classgraph.ScanResult; + +/** + * Unit Test. + */ +public class Issue370Test { + + public static @interface ApiOperation { + String value(); + + String notes(); + } + + public static class ClassWithAnnotation { + @ApiOperation(value = "", notes = "${snippetclassifications.findById}") + public void doSometing() { + + } + } + + /** + * Unit test. + */ + @Test + public void issue370Test() { + try (ScanResult scanResult = new ClassGraph().enableAllInfo() + .whitelistPackages(Issue370Test.class.getPackage().getName()).scan()) { + 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 String value = annotationInfo.getParameterValues().get("notes").getValue().toString(); + // System.err.println(value); + assertThat(value).isNotNull(); + } + } + } +} From 2796a0d55f71b13cfabfdfa5bfcae4190c569b58 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 26 Sep 2019 08:34:06 -0600 Subject: [PATCH 0321/1778] Improve test --- .../java/io/github/classgraph/issues/issue370/Issue370Test.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 100b92403..1781e5bfe 100644 --- a/src/test/java/io/github/classgraph/issues/issue370/Issue370Test.java +++ b/src/test/java/io/github/classgraph/issues/issue370/Issue370Test.java @@ -69,7 +69,7 @@ public void issue370Test() { final AnnotationInfo annotationInfo = methodInfo.getAnnotationInfo(ApiOperation.class.getName()); final String value = annotationInfo.getParameterValues().get("notes").getValue().toString(); // System.err.println(value); - assertThat(value).isNotNull(); + assertThat(value).isEqualTo("${snippetclassifications.findById}"); } } } From c1d51e4af6e657a43a1d32cc2e9fe7f8cd0f00f0 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 27 Sep 2019 08:56:34 -0600 Subject: [PATCH 0322/1778] Bintray change --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index 348f46817..847ae687e 100644 --- a/pom.xml +++ b/pom.xml @@ -41,6 +41,7 @@ bintray-classgraph-classgraph + classgraph-classgraph https://api.bintray.com/maven/classgraph/classgraph/classgraph/;publish=1 From 2e7720b066188160660490324ab57fb9c8252814 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 27 Sep 2019 08:57:40 -0600 Subject: [PATCH 0323/1778] Fix #370 (extend scanning to array-typed annotation param default vals) --- .../io/github/classgraph/AnnotationInfo.java | 2 +- .../classgraph/AnnotationParameterValue.java | 8 ++- .../java/io/github/classgraph/Classfile.java | 52 +++++++++++++++++-- .../classgraph/ObjectTypedValueWrapper.java | 48 ++++++++++++----- .../issues/issue370/Issue370Test.java | 19 ++----- .../issue370/annotations/ApiOperation.java | 13 +++++ .../issue370/annotations/Extension.java | 18 +++++++ .../annotations/ExtensionProperty.java | 18 +++++++ .../issue370/impl/ClassWithAnnotation.java | 10 ++++ 9 files changed, 152 insertions(+), 36 deletions(-) create mode 100644 src/test/java/io/github/classgraph/issues/issue370/annotations/ApiOperation.java create mode 100644 src/test/java/io/github/classgraph/issues/issue370/annotations/Extension.java create mode 100644 src/test/java/io/github/classgraph/issues/issue370/annotations/ExtensionProperty.java create mode 100644 src/test/java/io/github/classgraph/issues/issue370/impl/ClassWithAnnotation.java diff --git a/src/main/java/io/github/classgraph/AnnotationInfo.java b/src/main/java/io/github/classgraph/AnnotationInfo.java index 444ee665d..feb7468d8 100644 --- a/src/main/java/io/github/classgraph/AnnotationInfo.java +++ b/src/main/java/io/github/classgraph/AnnotationInfo.java @@ -304,7 +304,7 @@ private static class AnnotationInvocationHandler implements InvocationHandler { // Instantiate the annotation parameter values (this loads and gets references for class literals, // enum constants, etc.) for (final AnnotationParameterValue apv : annotationInfo.getParameterValues()) { - final Object instantiatedValue = apv.instantiate(annotationInfo.getClassInfo()); + final Object instantiatedValue = apv.instantiate(annotationInfo.getClassInfo(), /* log = */ null); if (instantiatedValue == null) { // Annotations cannot contain null values throw new IllegalArgumentException("Got null value for annotation parameter " + apv.getName() diff --git a/src/main/java/io/github/classgraph/AnnotationParameterValue.java b/src/main/java/io/github/classgraph/AnnotationParameterValue.java index 66e3dff95..d602fe796 100644 --- a/src/main/java/io/github/classgraph/AnnotationParameterValue.java +++ b/src/main/java/io/github/classgraph/AnnotationParameterValue.java @@ -33,6 +33,8 @@ import java.util.Objects; import java.util.Set; +import nonapi.io.github.classgraph.utils.LogNode; + /** A wrapper used to pair annotation parameter names with annotation parameter values. */ public class AnnotationParameterValue extends ScanResultObject implements HasName, Comparable { @@ -171,10 +173,12 @@ void convertWrapperArraysToPrimitiveArrays(final ClassInfo annotationClassInfo) * * @param annotationClassInfo * the annotation class info + * @param log + * the log * @return the instance */ - Object instantiate(final ClassInfo annotationClassInfo) { - return value.instantiateOrGet(annotationClassInfo, name); + Object instantiate(final ClassInfo annotationClassInfo, final LogNode log) { + return value.instantiateOrGet(annotationClassInfo, name, log); } // ------------------------------------------------------------------------------------------------------------- diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index 24a8d8ffb..ff0a0a0df 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -29,6 +29,7 @@ package io.github.classgraph; import java.io.IOException; +import java.lang.reflect.Array; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; @@ -325,6 +326,39 @@ private void scheduleScanningIfExternalClass(final String className, final Strin } } + /** + * Check if scanning needs to be extended upwards from an annotation parameter value. + * + * @param the + * {@link AnnotationInfo} object for an annotation, or for an annotation parmaeter value. + * @param log + * the log + */ + private void extendScanningUpwardsFromAnnotationParameterValues(final Object annotationParamVal, + final LogNode log) { + if (annotationParamVal == null) { + // Should not be possible -- ignore + } else if (annotationParamVal instanceof AnnotationInfo) { + final var annotationInfo = (AnnotationInfo) annotationParamVal; + scheduleScanningIfExternalClass(annotationInfo.getClassName(), "annotation class", log); + for (final AnnotationParameterValue apv : annotationInfo.getParameterValues()) { + extendScanningUpwardsFromAnnotationParameterValues(apv.getValue(), log); + } + } else if (annotationParamVal instanceof AnnotationEnumValue) { + scheduleScanningIfExternalClass(((AnnotationEnumValue) annotationParamVal).getClassName(), "enum class", + log); + } else if (annotationParamVal instanceof AnnotationClassRef) { + scheduleScanningIfExternalClass(((AnnotationClassRef) annotationParamVal).getClassName(), "class ref", + log); + } else if (annotationParamVal.getClass().isArray()) { + for (int i = 0, n = Array.getLength(annotationParamVal); i < n; i++) { + extendScanningUpwardsFromAnnotationParameterValues(Array.get(annotationParamVal, i), log); + } + } else { + // String etc. -- ignore + } + } + /** * Check if scanning needs to be extended upwards to an external superclass, interface or annotation. * @@ -346,6 +380,13 @@ private void extendScanningUpwards(final LogNode log) { if (classAnnotations != null) { for (final AnnotationInfo annotationInfo : classAnnotations) { scheduleScanningIfExternalClass(annotationInfo.getName(), "class annotation", log); + extendScanningUpwardsFromAnnotationParameterValues(annotationInfo, log); + } + } + // Annotation default param values + if (annotationParamDefaultValues != null) { + for (final AnnotationParameterValue apv : annotationParamDefaultValues) { + extendScanningUpwardsFromAnnotationParameterValues(apv.getValue(), log); } } // Check method annotations and method parameter annotations @@ -354,14 +395,16 @@ private void extendScanningUpwards(final LogNode log) { if (methodInfo.annotationInfo != null) { for (final AnnotationInfo methodAnnotationInfo : methodInfo.annotationInfo) { scheduleScanningIfExternalClass(methodAnnotationInfo.getName(), "method annotation", log); + extendScanningUpwardsFromAnnotationParameterValues(methodAnnotationInfo, log); } if (methodInfo.parameterAnnotationInfo != null && methodInfo.parameterAnnotationInfo.length > 0) { - for (final AnnotationInfo[] paramAnns : methodInfo.parameterAnnotationInfo) { - if (paramAnns != null && paramAnns.length > 0) { - for (final AnnotationInfo paramAnn : paramAnns) { - scheduleScanningIfExternalClass(paramAnn.getName(), + for (final AnnotationInfo[] paramAnnInfoArr : methodInfo.parameterAnnotationInfo) { + if (paramAnnInfoArr != null && paramAnnInfoArr.length > 0) { + for (final AnnotationInfo paramAnnInfo : paramAnnInfoArr) { + scheduleScanningIfExternalClass(paramAnnInfo.getName(), "method parameter annotation", log); + extendScanningUpwardsFromAnnotationParameterValues(paramAnnInfo, log); } } } @@ -375,6 +418,7 @@ private void extendScanningUpwards(final LogNode log) { if (fieldInfo.annotationInfo != null) { for (final AnnotationInfo fieldAnnotationInfo : fieldInfo.annotationInfo) { scheduleScanningIfExternalClass(fieldAnnotationInfo.getName(), "field annotation", log); + extendScanningUpwardsFromAnnotationParameterValues(fieldAnnotationInfo, log); } } } diff --git a/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java b/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java index 5937fa00b..ca3a80bf7 100644 --- a/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java +++ b/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java @@ -34,6 +34,8 @@ import java.util.Objects; import java.util.Set; +import nonapi.io.github.classgraph.utils.LogNode; + /** A union type, used for typesafe serialization/deserialization to/from JSON. Only one field is ever set. */ class ObjectTypedValueWrapper extends ScanResultObject { // Parameter value is split into different fields by type, so that serialization and deserialization @@ -192,9 +194,11 @@ public ObjectTypedValueWrapper(final Object annotationParamValue) { * if non-null, instantiate this object as a parameter value of this annotation class. * @param paramName * if non-null, instantiate this object as a value of this named parameter. + * @param log + * the log. * @return The value wrapped by this wrapper class. */ - Object instantiateOrGet(final ClassInfo annotationClassInfo, final String paramName) { + Object instantiateOrGet(final ClassInfo annotationClassInfo, final String paramName, final LogNode log) { final boolean instantiate = annotationClassInfo != null; if (annotationEnumValue != null) { return instantiate ? annotationEnumValue.loadClassAndReturnEnumValue() : annotationEnumValue; @@ -251,7 +255,8 @@ Object instantiateOrGet(final ClassInfo annotationClassInfo, final String paramN for (int i = 0; i < objectArrayValue.length; i++) { if (objectArrayValue[i] != null) { // Get the element value (may also cause the element to be instantiated) - final Object eltValue = objectArrayValue[i].instantiateOrGet(annotationClassInfo, paramName); + final Object eltValue = objectArrayValue[i].instantiateOrGet(annotationClassInfo, paramName, + log); // Store the possibly-instantiated value in the array Array.set(annotationValueObjectArray, i, eltValue); } @@ -268,7 +273,7 @@ Object instantiateOrGet(final ClassInfo annotationClassInfo, final String paramN * @return The value wrapped by this wrapper class. */ public Object get() { - return instantiateOrGet(null, null); + return instantiateOrGet(null, null, null); } // ------------------------------------------------------------------------------------------------------------- @@ -287,13 +292,21 @@ public Object get() { */ private Object getArrayValueClassOrName(final ClassInfo annotationClassInfo, final String paramName, final boolean getClass) { + if (annotationClassInfo == null) { + // annotationClassInfo is null, cannot convert array values to correct type -- just return Object array. + // (Should not happen, because the Classfile class extends scanning recursively upwards from all + // annotation parameter values and annotation default values, so there should always be a ClassInfo + // object created for every annotation.) + return getClass ? Object.class : "java.lang.Object"; + } // Find the method in the annotation class with the same name as the annotation parameter. final MethodInfoList annotationMethodList = annotationClassInfo.methodInfo == null ? null : annotationClassInfo.methodInfo.get(paramName); if (annotationMethodList != null && annotationMethodList.size() > 1) { // There should only be one method with a given name in an annotation throw new IllegalArgumentException("Duplicated annotation parameter method " + paramName - + "() in annotation class " + annotationClassInfo.getName()); + + "() in annotation class " + + (annotationClassInfo == null ? "" : annotationClassInfo.getName())); } else if (annotationMethodList != null && annotationMethodList.size() == 1) { // Get the result type of the method with the same name as the annotation parameter final TypeSignature annotationMethodResultTypeSig = annotationMethodList.get(0) @@ -301,7 +314,8 @@ private Object getArrayValueClassOrName(final ClassInfo annotationClassInfo, fin // The result type has to be an array type if (!(annotationMethodResultTypeSig instanceof ArrayTypeSignature)) { throw new IllegalArgumentException("Annotation parameter " + paramName + " in annotation class " - + annotationClassInfo.getName() + + (annotationClassInfo == null ? "" + : annotationClassInfo.getName()) + " holds an array, but does not have an array type signature"); } final ArrayTypeSignature arrayTypeSig = (ArrayTypeSignature) annotationMethodResultTypeSig; @@ -389,7 +403,8 @@ void convertWrapperArraysToPrimitiveArrays(final ClassInfo annotationClassInfo, if (elt == null) { throw new IllegalArgumentException("Illegal null value for array of element type " + targetElementTypeName + " in parameter " + paramName + " of annotation class " - + annotationClassInfo.getName()); + + (annotationClassInfo == null ? "" + : annotationClassInfo.getName())); } intArrayValue[j] = objectArrayValue[j].integerValue; } @@ -402,7 +417,8 @@ void convertWrapperArraysToPrimitiveArrays(final ClassInfo annotationClassInfo, if (elt == null) { throw new IllegalArgumentException("Illegal null value for array of element type " + targetElementTypeName + " in parameter " + paramName + " of annotation class " - + annotationClassInfo.getName()); + + (annotationClassInfo == null ? "" + : annotationClassInfo.getName())); } longArrayValue[j] = objectArrayValue[j].longValue; } @@ -415,7 +431,8 @@ void convertWrapperArraysToPrimitiveArrays(final ClassInfo annotationClassInfo, if (elt == null) { throw new IllegalArgumentException("Illegal null value for array of element type " + targetElementTypeName + " in parameter " + paramName + " of annotation class " - + annotationClassInfo.getName()); + + (annotationClassInfo == null ? "" + : annotationClassInfo.getName())); } shortArrayValue[j] = objectArrayValue[j].shortValue; } @@ -428,7 +445,8 @@ void convertWrapperArraysToPrimitiveArrays(final ClassInfo annotationClassInfo, if (elt == null) { throw new IllegalArgumentException("Illegal null value for array of element type " + targetElementTypeName + " in parameter " + paramName + " of annotation class " - + annotationClassInfo.getName()); + + (annotationClassInfo == null ? "" + : annotationClassInfo.getName())); } charArrayValue[j] = objectArrayValue[j].characterValue; } @@ -441,7 +459,8 @@ void convertWrapperArraysToPrimitiveArrays(final ClassInfo annotationClassInfo, if (elt == null) { throw new IllegalArgumentException("Illegal null value for array of element type " + targetElementTypeName + " in parameter " + paramName + " of annotation class " - + annotationClassInfo.getName()); + + (annotationClassInfo == null ? "" + : annotationClassInfo.getName())); } floatArrayValue[j] = objectArrayValue[j].floatValue; } @@ -454,7 +473,8 @@ void convertWrapperArraysToPrimitiveArrays(final ClassInfo annotationClassInfo, if (elt == null) { throw new IllegalArgumentException("Illegal null value for array of element type " + targetElementTypeName + " in parameter " + paramName + " of annotation class " - + annotationClassInfo.getName()); + + (annotationClassInfo == null ? "" + : annotationClassInfo.getName())); } doubleArrayValue[j] = objectArrayValue[j].doubleValue; } @@ -467,7 +487,8 @@ void convertWrapperArraysToPrimitiveArrays(final ClassInfo annotationClassInfo, if (elt == null) { throw new IllegalArgumentException("Illegal null value for array of element type " + targetElementTypeName + " in parameter " + paramName + " of annotation class " - + annotationClassInfo.getName()); + + (annotationClassInfo == null ? "" + : annotationClassInfo.getName())); } booleanArrayValue[j] = objectArrayValue[j].booleanValue; } @@ -480,7 +501,8 @@ void convertWrapperArraysToPrimitiveArrays(final ClassInfo annotationClassInfo, if (elt == null) { throw new IllegalArgumentException("Illegal null value for array of element type " + targetElementTypeName + " in parameter " + paramName + " of annotation class " - + annotationClassInfo.getName()); + + (annotationClassInfo == null ? "" + : annotationClassInfo.getName())); } byteArrayValue[j] = objectArrayValue[j].byteValue; } 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 1781e5bfe..a67654a17 100644 --- a/src/test/java/io/github/classgraph/issues/issue370/Issue370Test.java +++ b/src/test/java/io/github/classgraph/issues/issue370/Issue370Test.java @@ -37,38 +37,25 @@ import io.github.classgraph.ClassInfo; import io.github.classgraph.MethodInfo; import io.github.classgraph.ScanResult; +import io.github.classgraph.issues.issue370.annotations.ApiOperation; +import io.github.classgraph.issues.issue370.impl.ClassWithAnnotation; /** * Unit Test. */ public class Issue370Test { - - public static @interface ApiOperation { - String value(); - - String notes(); - } - - public static class ClassWithAnnotation { - @ApiOperation(value = "", notes = "${snippetclassifications.findById}") - public void doSometing() { - - } - } - /** * Unit test. */ @Test public void issue370Test() { try (ScanResult scanResult = new ClassGraph().enableAllInfo() - .whitelistPackages(Issue370Test.class.getPackage().getName()).scan()) { + .whitelistPackages(ClassWithAnnotation.class.getPackage().getName()).scan()) { 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 String value = annotationInfo.getParameterValues().get("notes").getValue().toString(); - // System.err.println(value); assertThat(value).isEqualTo("${snippetclassifications.findById}"); } } diff --git a/src/test/java/io/github/classgraph/issues/issue370/annotations/ApiOperation.java b/src/test/java/io/github/classgraph/issues/issue370/annotations/ApiOperation.java new file mode 100644 index 000000000..f154cbd78 --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue370/annotations/ApiOperation.java @@ -0,0 +1,13 @@ +package io.github.classgraph.issues.issue370.annotations; + +public @interface ApiOperation { + String value(); + + String notes(); + + /** + * @return an optional array of extensions + */ + + Extension[] extensions() default @Extension(properties = @ExtensionProperty(name = "", value = "")); +} \ No newline at end of file diff --git a/src/test/java/io/github/classgraph/issues/issue370/annotations/Extension.java b/src/test/java/io/github/classgraph/issues/issue370/annotations/Extension.java new file mode 100644 index 000000000..aa93d24b7 --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue370/annotations/Extension.java @@ -0,0 +1,18 @@ +package io.github.classgraph.issues.issue370.annotations; + +public @interface Extension { + /** + * An option name for these extensions. + * + * @return an option name for these extensions - will be prefixed with "x-" + */ + String name() default ""; + + /** + * The extension properties. + * + * @return the actual extension properties + * @see ExtensionProperty + */ + ExtensionProperty[] properties(); +} \ No newline at end of file diff --git a/src/test/java/io/github/classgraph/issues/issue370/annotations/ExtensionProperty.java b/src/test/java/io/github/classgraph/issues/issue370/annotations/ExtensionProperty.java new file mode 100644 index 000000000..f33fcdf04 --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue370/annotations/ExtensionProperty.java @@ -0,0 +1,18 @@ +package io.github.classgraph.issues.issue370.annotations; + +public @interface ExtensionProperty { + + /** + * The name of the property. + * + * @return the name of the property + */ + String name(); + + /** + * The value of the property. + * + * @return the value of the property + */ + String value(); +} \ No newline at end of file diff --git a/src/test/java/io/github/classgraph/issues/issue370/impl/ClassWithAnnotation.java b/src/test/java/io/github/classgraph/issues/issue370/impl/ClassWithAnnotation.java new file mode 100644 index 000000000..7eb2ff1d6 --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue370/impl/ClassWithAnnotation.java @@ -0,0 +1,10 @@ +package io.github.classgraph.issues.issue370.impl; + +import io.github.classgraph.issues.issue370.annotations.ApiOperation; + +public class ClassWithAnnotation { + @ApiOperation(value = "", notes = "${snippetclassifications.findById}") + public void doSometing() { + + } +} \ No newline at end of file From 24ac3741b65cc4b2fc17a37b82556a269e784e15 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 27 Sep 2019 11:57:53 -0600 Subject: [PATCH 0324/1778] Remove unused parameter --- .../github/classgraph/AnnotationParameterValue.java | 2 +- src/main/java/io/github/classgraph/Classfile.java | 2 +- .../io/github/classgraph/ObjectTypedValueWrapper.java | 11 +++-------- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/github/classgraph/AnnotationParameterValue.java b/src/main/java/io/github/classgraph/AnnotationParameterValue.java index d602fe796..9d5675ab2 100644 --- a/src/main/java/io/github/classgraph/AnnotationParameterValue.java +++ b/src/main/java/io/github/classgraph/AnnotationParameterValue.java @@ -178,7 +178,7 @@ void convertWrapperArraysToPrimitiveArrays(final ClassInfo annotationClassInfo) * @return the instance */ Object instantiate(final ClassInfo annotationClassInfo, final LogNode log) { - return value.instantiateOrGet(annotationClassInfo, name, log); + return value.instantiateOrGet(annotationClassInfo, name); } // ------------------------------------------------------------------------------------------------------------- diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index ff0a0a0df..49354bdf2 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -383,7 +383,7 @@ private void extendScanningUpwards(final LogNode log) { extendScanningUpwardsFromAnnotationParameterValues(annotationInfo, log); } } - // Annotation default param values + // Check annotation default parameter values if (annotationParamDefaultValues != null) { for (final AnnotationParameterValue apv : annotationParamDefaultValues) { extendScanningUpwardsFromAnnotationParameterValues(apv.getValue(), log); diff --git a/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java b/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java index ca3a80bf7..a3fb78f73 100644 --- a/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java +++ b/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java @@ -34,8 +34,6 @@ import java.util.Objects; import java.util.Set; -import nonapi.io.github.classgraph.utils.LogNode; - /** A union type, used for typesafe serialization/deserialization to/from JSON. Only one field is ever set. */ class ObjectTypedValueWrapper extends ScanResultObject { // Parameter value is split into different fields by type, so that serialization and deserialization @@ -194,11 +192,9 @@ public ObjectTypedValueWrapper(final Object annotationParamValue) { * if non-null, instantiate this object as a parameter value of this annotation class. * @param paramName * if non-null, instantiate this object as a value of this named parameter. - * @param log - * the log. * @return The value wrapped by this wrapper class. */ - Object instantiateOrGet(final ClassInfo annotationClassInfo, final String paramName, final LogNode log) { + Object instantiateOrGet(final ClassInfo annotationClassInfo, final String paramName) { final boolean instantiate = annotationClassInfo != null; if (annotationEnumValue != null) { return instantiate ? annotationEnumValue.loadClassAndReturnEnumValue() : annotationEnumValue; @@ -255,8 +251,7 @@ Object instantiateOrGet(final ClassInfo annotationClassInfo, final String paramN for (int i = 0; i < objectArrayValue.length; i++) { if (objectArrayValue[i] != null) { // Get the element value (may also cause the element to be instantiated) - final Object eltValue = objectArrayValue[i].instantiateOrGet(annotationClassInfo, paramName, - log); + final Object eltValue = objectArrayValue[i].instantiateOrGet(annotationClassInfo, paramName); // Store the possibly-instantiated value in the array Array.set(annotationValueObjectArray, i, eltValue); } @@ -273,7 +268,7 @@ Object instantiateOrGet(final ClassInfo annotationClassInfo, final String paramN * @return The value wrapped by this wrapper class. */ public Object get() { - return instantiateOrGet(null, null, null); + return instantiateOrGet(null, null); } // ------------------------------------------------------------------------------------------------------------- From 8031edab7253f72a6a6197955e534f7c9c767862 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 27 Sep 2019 12:14:15 -0600 Subject: [PATCH 0325/1778] More fixes for #370 --- .../java/io/github/classgraph/Classfile.java | 2 +- .../classgraph/ObjectTypedValueWrapper.java | 34 +++++++++---------- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index 49354bdf2..3adc7ddc5 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -339,7 +339,7 @@ private void extendScanningUpwardsFromAnnotationParameterValues(final Object ann if (annotationParamVal == null) { // Should not be possible -- ignore } else if (annotationParamVal instanceof AnnotationInfo) { - final var annotationInfo = (AnnotationInfo) annotationParamVal; + final AnnotationInfo annotationInfo = (AnnotationInfo) annotationParamVal; scheduleScanningIfExternalClass(annotationInfo.getClassName(), "annotation class", log); for (final AnnotationParameterValue apv : annotationInfo.getParameterValues()) { extendScanningUpwardsFromAnnotationParameterValues(apv.getValue(), log); diff --git a/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java b/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java index a3fb78f73..2d640a276 100644 --- a/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java +++ b/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java @@ -287,22 +287,16 @@ public Object get() { */ private Object getArrayValueClassOrName(final ClassInfo annotationClassInfo, final String paramName, final boolean getClass) { - if (annotationClassInfo == null) { - // annotationClassInfo is null, cannot convert array values to correct type -- just return Object array. - // (Should not happen, because the Classfile class extends scanning recursively upwards from all - // annotation parameter values and annotation default values, so there should always be a ClassInfo - // object created for every annotation.) - return getClass ? Object.class : "java.lang.Object"; - } // Find the method in the annotation class with the same name as the annotation parameter. - final MethodInfoList annotationMethodList = annotationClassInfo.methodInfo == null ? null - : annotationClassInfo.methodInfo.get(paramName); - if (annotationMethodList != null && annotationMethodList.size() > 1) { - // There should only be one method with a given name in an annotation - throw new IllegalArgumentException("Duplicated annotation parameter method " + paramName - + "() in annotation class " - + (annotationClassInfo == null ? "" : annotationClassInfo.getName())); - } else if (annotationMethodList != null && annotationMethodList.size() == 1) { + final MethodInfoList annotationMethodList = annotationClassInfo == null + || annotationClassInfo.methodInfo == null ? null : annotationClassInfo.methodInfo.get(paramName); + if (annotationMethodList != null && annotationMethodList.size() > 0) { + if (annotationMethodList.size() > 1) { + // There should only be one method with a given name in an annotation + throw new IllegalArgumentException("Duplicated annotation parameter method " + paramName + + "() in annotation class " + (annotationClassInfo == null ? "" + : annotationClassInfo.getName())); + } // Get the result type of the method with the same name as the annotation parameter final TypeSignature annotationMethodResultTypeSig = annotationMethodList.get(0) .getTypeSignatureOrTypeDescriptor().getResultType(); @@ -330,9 +324,10 @@ private Object getArrayValueClassOrName(final ClassInfo annotationClassInfo, fin } } else { // Could not find a method with this name -- this is an external class. - // Find first non-null object in array, and use its type as the type of the array. + // Find first non-null object in array, and use its type as the element type of the array. for (final ObjectTypedValueWrapper elt : objectArrayValue) { if (elt != null) { + // Primitive typed arrays will be turned into arrays of boxed types return elt.integerValue != null ? (getClass ? Integer.class : "int") : elt.longValue != null ? (getClass ? Long.class : "long") : elt.shortValue != null ? (getClass ? Short.class : "short") @@ -345,11 +340,14 @@ private Object getArrayValueClassOrName(final ClassInfo annotationClassInfo, fin : elt.floatValue != null ? (getClass ? Float.class : "float") - : (getClass ? null : ""); + : (getClass ? elt.getClass() + : elt.getClass() + .getName()); } } } - return getClass ? null : ""; + // Could not determine the element type -- just use Object + return getClass ? Object.class : "java.lang.Object"; } /** From 35b10d8ac2fdccbc9a2791429255e72dd3fa01dc Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 27 Sep 2019 12:16:19 -0600 Subject: [PATCH 0326/1778] Enable testing on JDK 12 and 13 --- .travis.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index b2d6506fc..c5150591a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,10 @@ language: java jdk: - openjdk8 - - openjdk11 # LTS - - openjdk-ea # Early access + - openjdk11 + - openjdk12 + - openjdk13 + - openjdk-ea #ignore default install step install: true From 35242dc53b0e0247b0a44a69e1b45483b44e5f15 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 27 Sep 2019 12:30:05 -0600 Subject: [PATCH 0327/1778] Disable Java EA release, it is failing --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c5150591a..3a0a18254 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ jdk: - openjdk11 - openjdk12 - openjdk13 - - openjdk-ea + #- openjdk-ea #ignore default install step install: true From c5d80c5497eb6b912ce2a21815f558eca5f02deb Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 5 Oct 2019 06:14:02 -0600 Subject: [PATCH 0328/1778] Fix static analyzer warnings --- src/main/java/io/github/classgraph/Classfile.java | 4 ++-- .../java/io/github/classgraph/ObjectTypedValueWrapper.java | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index 3adc7ddc5..c1fb2a7c6 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -329,8 +329,8 @@ private void scheduleScanningIfExternalClass(final String className, final Strin /** * Check if scanning needs to be extended upwards from an annotation parameter value. * - * @param the - * {@link AnnotationInfo} object for an annotation, or for an annotation parmaeter value. + * @param annotationParamVal + * the {@link AnnotationInfo} object for an annotation, or for an annotation parameter value. * @param log * the log */ diff --git a/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java b/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java index 2d640a276..7950ee399 100644 --- a/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java +++ b/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java @@ -294,8 +294,7 @@ private Object getArrayValueClassOrName(final ClassInfo annotationClassInfo, fin if (annotationMethodList.size() > 1) { // There should only be one method with a given name in an annotation throw new IllegalArgumentException("Duplicated annotation parameter method " + paramName - + "() in annotation class " + (annotationClassInfo == null ? "" - : annotationClassInfo.getName())); + + "() in annotation class " + annotationClassInfo.getName()); } // Get the result type of the method with the same name as the annotation parameter final TypeSignature annotationMethodResultTypeSig = annotationMethodList.get(0) @@ -303,8 +302,7 @@ private Object getArrayValueClassOrName(final ClassInfo annotationClassInfo, fin // The result type has to be an array type if (!(annotationMethodResultTypeSig instanceof ArrayTypeSignature)) { throw new IllegalArgumentException("Annotation parameter " + paramName + " in annotation class " - + (annotationClassInfo == null ? "" - : annotationClassInfo.getName()) + + annotationClassInfo.getName() + " holds an array, but does not have an array type signature"); } final ArrayTypeSignature arrayTypeSig = (ArrayTypeSignature) annotationMethodResultTypeSig; From c2c7b71ae8e2d109061f7c0d18baf3269c0e0909 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 5 Oct 2019 06:38:21 -0600 Subject: [PATCH 0329/1778] Fix static analyzer warnings --- .../java/io/github/classgraph/AnnotationInfo.java | 2 +- .../classgraph/AnnotationParameterValue.java | 6 +----- .../github/classgraph/ObjectTypedValueWrapper.java | 2 +- .../classgraph/issues/issue368/Issue368Test.java | 4 ++++ .../issues/issue370/annotations/ApiOperation.java | 14 +++++++++++++- .../issues/issue370/annotations/Extension.java | 3 +++ .../issue370/annotations/ExtensionProperty.java | 3 +++ .../issues/issue370/impl/ClassWithAnnotation.java | 9 +++++++-- 8 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/github/classgraph/AnnotationInfo.java b/src/main/java/io/github/classgraph/AnnotationInfo.java index feb7468d8..444ee665d 100644 --- a/src/main/java/io/github/classgraph/AnnotationInfo.java +++ b/src/main/java/io/github/classgraph/AnnotationInfo.java @@ -304,7 +304,7 @@ private static class AnnotationInvocationHandler implements InvocationHandler { // Instantiate the annotation parameter values (this loads and gets references for class literals, // enum constants, etc.) for (final AnnotationParameterValue apv : annotationInfo.getParameterValues()) { - final Object instantiatedValue = apv.instantiate(annotationInfo.getClassInfo(), /* log = */ null); + final Object instantiatedValue = apv.instantiate(annotationInfo.getClassInfo()); if (instantiatedValue == null) { // Annotations cannot contain null values throw new IllegalArgumentException("Got null value for annotation parameter " + apv.getName() diff --git a/src/main/java/io/github/classgraph/AnnotationParameterValue.java b/src/main/java/io/github/classgraph/AnnotationParameterValue.java index 9d5675ab2..66e3dff95 100644 --- a/src/main/java/io/github/classgraph/AnnotationParameterValue.java +++ b/src/main/java/io/github/classgraph/AnnotationParameterValue.java @@ -33,8 +33,6 @@ import java.util.Objects; import java.util.Set; -import nonapi.io.github.classgraph.utils.LogNode; - /** A wrapper used to pair annotation parameter names with annotation parameter values. */ public class AnnotationParameterValue extends ScanResultObject implements HasName, Comparable { @@ -173,11 +171,9 @@ void convertWrapperArraysToPrimitiveArrays(final ClassInfo annotationClassInfo) * * @param annotationClassInfo * the annotation class info - * @param log - * the log * @return the instance */ - Object instantiate(final ClassInfo annotationClassInfo, final LogNode log) { + Object instantiate(final ClassInfo annotationClassInfo) { return value.instantiateOrGet(annotationClassInfo, name); } diff --git a/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java b/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java index 7950ee399..3e63fea8f 100644 --- a/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java +++ b/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java @@ -290,7 +290,7 @@ private Object getArrayValueClassOrName(final ClassInfo annotationClassInfo, fin // Find the method in the annotation class with the same name as the annotation parameter. final MethodInfoList annotationMethodList = annotationClassInfo == null || annotationClassInfo.methodInfo == null ? null : annotationClassInfo.methodInfo.get(paramName); - if (annotationMethodList != null && annotationMethodList.size() > 0) { + if (annotationMethodList != null && !annotationMethodList.isEmpty()) { if (annotationMethodList.size() > 1) { // There should only be one method with a given name in an annotation throw new IllegalArgumentException("Duplicated annotation parameter method " + paramName diff --git a/src/test/java/io/github/classgraph/issues/issue368/Issue368Test.java b/src/test/java/io/github/classgraph/issues/issue368/Issue368Test.java index ab2e05a7d..4a4f77c43 100644 --- a/src/test/java/io/github/classgraph/issues/issue368/Issue368Test.java +++ b/src/test/java/io/github/classgraph/issues/issue368/Issue368Test.java @@ -42,7 +42,11 @@ */ public class Issue368Test { + /** + * InnerClass. + */ public static class InnerClass { + @SuppressWarnings("javadoc") public Class innerClassField = Issue368Test.class; } diff --git a/src/test/java/io/github/classgraph/issues/issue370/annotations/ApiOperation.java b/src/test/java/io/github/classgraph/issues/issue370/annotations/ApiOperation.java index f154cbd78..7c51004b4 100644 --- a/src/test/java/io/github/classgraph/issues/issue370/annotations/ApiOperation.java +++ b/src/test/java/io/github/classgraph/issues/issue370/annotations/ApiOperation.java @@ -1,13 +1,25 @@ package io.github.classgraph.issues.issue370.annotations; +/** + * ApiOperation. + */ public @interface ApiOperation { + /** + * Value. + * + * @return the string + */ String value(); + /** + * Notes. + * + * @return the string + */ String notes(); /** * @return an optional array of extensions */ - Extension[] extensions() default @Extension(properties = @ExtensionProperty(name = "", value = "")); } \ No newline at end of file diff --git a/src/test/java/io/github/classgraph/issues/issue370/annotations/Extension.java b/src/test/java/io/github/classgraph/issues/issue370/annotations/Extension.java index aa93d24b7..253c3fc2f 100644 --- a/src/test/java/io/github/classgraph/issues/issue370/annotations/Extension.java +++ b/src/test/java/io/github/classgraph/issues/issue370/annotations/Extension.java @@ -1,5 +1,8 @@ package io.github.classgraph.issues.issue370.annotations; +/** + * Extension. + */ public @interface Extension { /** * An option name for these extensions. diff --git a/src/test/java/io/github/classgraph/issues/issue370/annotations/ExtensionProperty.java b/src/test/java/io/github/classgraph/issues/issue370/annotations/ExtensionProperty.java index f33fcdf04..9de61e244 100644 --- a/src/test/java/io/github/classgraph/issues/issue370/annotations/ExtensionProperty.java +++ b/src/test/java/io/github/classgraph/issues/issue370/annotations/ExtensionProperty.java @@ -1,5 +1,8 @@ package io.github.classgraph.issues.issue370.annotations; +/** + * ExtensionProperty. + */ public @interface ExtensionProperty { /** diff --git a/src/test/java/io/github/classgraph/issues/issue370/impl/ClassWithAnnotation.java b/src/test/java/io/github/classgraph/issues/issue370/impl/ClassWithAnnotation.java index 7eb2ff1d6..2d85404db 100644 --- a/src/test/java/io/github/classgraph/issues/issue370/impl/ClassWithAnnotation.java +++ b/src/test/java/io/github/classgraph/issues/issue370/impl/ClassWithAnnotation.java @@ -2,9 +2,14 @@ import io.github.classgraph.issues.issue370.annotations.ApiOperation; +/** + * ClassWithAnnotation. + */ public class ClassWithAnnotation { + /** + * Do something. + */ @ApiOperation(value = "", notes = "${snippetclassifications.findById}") - public void doSometing() { - + public void doSomething() { } } \ No newline at end of file From a8bbe83be85601e45ef10d78bfca928660c5864b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 5 Oct 2019 06:53:19 -0600 Subject: [PATCH 0330/1778] Fix static analyzer warnings --- .../io/github/classgraph/AnnotationInfo.java | 2 +- .../utils/InputStreamOrByteBufferAdapter.java | 26 ++++++++++--------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/main/java/io/github/classgraph/AnnotationInfo.java b/src/main/java/io/github/classgraph/AnnotationInfo.java index 444ee665d..01bcb1bda 100644 --- a/src/main/java/io/github/classgraph/AnnotationInfo.java +++ b/src/main/java/io/github/classgraph/AnnotationInfo.java @@ -328,7 +328,7 @@ public Object invoke(final Object proxy, final Method method, final Object[] arg + (args == null ? 0 : args.length) + ", expected " + paramTypes.length); } if (args != null && paramTypes.length == 1) { - if (methodName.equals("equals") && paramTypes[0] == Object.class) { + if ("equals".equals(methodName) && paramTypes[0] == Object.class) { // equals() needs to function the same as the JDK implementation // (see src/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java in the JDK) if (this == args[0]) { diff --git a/src/main/java/nonapi/io/github/classgraph/utils/InputStreamOrByteBufferAdapter.java b/src/main/java/nonapi/io/github/classgraph/utils/InputStreamOrByteBufferAdapter.java index d63962ec4..013951a13 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/InputStreamOrByteBufferAdapter.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/InputStreamOrByteBufferAdapter.java @@ -357,18 +357,17 @@ public String readString(final int strStart, final boolean replaceSlashWithDot, readMore(bufferUnderrunBytes); } final char[] chars = new char[utfLen]; - int c, c2, c3, c4; int byteIdx = 0; int charIdx = 0; for (; byteIdx < utfLen; byteIdx++) { - c = buf[utfStart + byteIdx] & 0xff; + final int c = buf[utfStart + byteIdx] & 0xff; if (c > 127) { break; } chars[charIdx++] = (char) (replaceSlashWithDot && c == '/' ? '.' : c); } while (byteIdx < utfLen) { - c = buf[utfStart + byteIdx] & 0xff; + final int c = buf[utfStart + byteIdx] & 0xff; switch (c >> 4) { case 0: case 1: @@ -377,36 +376,39 @@ public String readString(final int strStart, final boolean replaceSlashWithDot, case 4: case 5: case 6: - case 7: + case 7: { byteIdx++; chars[charIdx++] = (char) (replaceSlashWithDot && c == '/' ? '.' : c); break; + } case 12: - case 13: + case 13: { byteIdx += 2; if (byteIdx > utfLen) { throw new IOException("Bad modified UTF8"); } - c2 = buf[utfStart + byteIdx - 1]; + final int c2 = buf[utfStart + byteIdx - 1]; if ((c2 & 0xc0) != 0x80) { throw new IOException("Bad modified UTF8"); } - c4 = ((c & 0x1f) << 6) | (c2 & 0x3f); - chars[charIdx++] = (char) (replaceSlashWithDot && c4 == '/' ? '.' : c4); + final int c3 = ((c & 0x1f) << 6) | (c2 & 0x3f); + chars[charIdx++] = (char) (replaceSlashWithDot && c3 == '/' ? '.' : c3); break; - case 14: + } + case 14: { byteIdx += 3; if (byteIdx > utfLen) { throw new IOException("Bad modified UTF8"); } - c2 = buf[utfStart + byteIdx - 2]; - c3 = buf[utfStart + byteIdx - 1]; + final int c2 = buf[utfStart + byteIdx - 2]; + final int c3 = buf[utfStart + byteIdx - 1]; if ((c2 & 0xc0) != 0x80 || (c3 & 0xc0) != 0x80) { throw new IOException("Bad modified UTF8"); } - c4 = ((c & 0x0f) << 12) | ((c2 & 0x3f) << 6) | (c3 & 0x3f); + final int c4 = ((c & 0x0f) << 12) | ((c2 & 0x3f) << 6) | (c3 & 0x3f); chars[charIdx++] = (char) (replaceSlashWithDot && c4 == '/' ? '.' : c4); break; + } default: throw new IOException("Bad modified UTF8"); } From bde6bd8a38513388616c506c3f73137a580db7d4 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 6 Oct 2019 07:02:49 -0600 Subject: [PATCH 0331/1778] adjust version number --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 847ae687e..556ff11ad 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.49-SNAPSHOT + 4.8.48-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 69c47598fff6837c2d140065afa497eaa30fc5b1 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 6 Oct 2019 07:06:31 -0600 Subject: [PATCH 0332/1778] [maven-release-plugin] prepare release classgraph-4.8.48 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 556ff11ad..557b2f1d3 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.48-SNAPSHOT + 4.8.48 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.48 From 5a12ea885fcb5c9be015cc67a1963f7570a7c9ae Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 6 Oct 2019 07:06:54 -0600 Subject: [PATCH 0333/1778] [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 557b2f1d3..847ae687e 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.48 + 4.8.49-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.48 + HEAD From 1285560acc7f970a0cb78f49c94b667f906b4e0e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 6 Oct 2019 07:12:28 -0600 Subject: [PATCH 0334/1778] bintray fixes --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 847ae687e..f9476c2b4 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.49-SNAPSHOT + 4.8.48-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -42,7 +42,7 @@ bintray-classgraph-classgraph classgraph-classgraph - https://api.bintray.com/maven/classgraph/classgraph/classgraph/;publish=1 + https://api.bintray.com/maven/classgraph/classgraph/io.github.classgraph:classgraph/;publish=1 From ba961f80294e9462b32c70c640f21789ce1b114a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 6 Oct 2019 07:13:17 -0600 Subject: [PATCH 0335/1778] [maven-release-plugin] prepare release classgraph-4.8.48 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index f9476c2b4..a26de83a3 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.48-SNAPSHOT + 4.8.48 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.48 From 6aa1beae254ebc5832cc5ffdeed2668c2d13740c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 6 Oct 2019 07:13:23 -0600 Subject: [PATCH 0336/1778] [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 a26de83a3..573d80f86 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.48 + 4.8.49-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.48 + HEAD From 5819a748f369e6d53c5125a2080ce8e4874d14f1 Mon Sep 17 00:00:00 2001 From: "Sean C. Sullivan" Date: Sun, 6 Oct 2019 12:44:10 -0700 Subject: [PATCH 0337/1778] JUnit 5.5.2 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 573d80f86..f6bfd5f63 100644 --- a/pom.xml +++ b/pom.xml @@ -417,7 +417,7 @@ org.junit.jupiter junit-jupiter - 5.5.0 + 5.5.2 test From eb45931ee3c19d9273bd61078e2e0a43d4610e05 Mon Sep 17 00:00:00 2001 From: "Sean C. Sullivan" Date: Sun, 6 Oct 2019 12:47:40 -0700 Subject: [PATCH 0338/1778] add Guava [ClassPath] to README --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 094e32c22..96fdfefb0 100644 --- a/README.md +++ b/README.md @@ -155,18 +155,19 @@ Some other classpath scanning mechanisms include: * [Javassist](http://jboss-javassist.github.io/javassist/) * [ObjectWeb ASM](http://asm.ow2.org/) * [QDox](https://github.com/paul-hammant/qdox), a fast Java source parser and indexer -* [bndtools](https://github.com/bndtools/bnd), which is able to ["crawl"/parse the bytecode of class files](https://github.com/bndtools/bnd/blob/master/biz.aQute.bndlib/src/aQute/bnd/osgi/Clazz.java) to find all imports/dependencies, among other things. +* [bndtools](https://github.com/bndtools/bnd), which is able to ["crawl"/parse the bytecode of class files](https://github.com/bndtools/bnd/blob/master/biz.aQute.bndlib/src/aQute/bnd/osgi/Clazz.java) to find all imports/dependencies, among other things. * [coffea](https://github.com/sbilinski/coffea), a command line tool and Python library for analyzing static dependences in Java bytecode * [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) ## License **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. From 85d30c1af9c3d2148c7d3e959a02cf99bc06e0e6 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 12 Oct 2019 00:42:48 -0600 Subject: [PATCH 0339/1778] BinTray fixes --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index f6bfd5f63..bbb566c2c 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.49-SNAPSHOT + 4.8.48-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -42,7 +42,7 @@ bintray-classgraph-classgraph classgraph-classgraph - https://api.bintray.com/maven/classgraph/classgraph/io.github.classgraph:classgraph/;publish=1 + https://api.bintray.com/maven/classgraph/classgraph/classgraph/;publish=1 From 5a22aa03751165fff5f5a59f49b97afa98e208d9 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 12 Oct 2019 00:43:49 -0600 Subject: [PATCH 0340/1778] [maven-release-plugin] prepare release classgraph-4.8.48 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index bbb566c2c..8282b51d0 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.48-SNAPSHOT + 4.8.48 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.48 From 50700618af0e66a809ec98ec343b0acb04362107 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 12 Oct 2019 00:44:07 -0600 Subject: [PATCH 0341/1778] [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 8282b51d0..565c04b71 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.48 + 4.8.49-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.48 + HEAD From f958a0f5c6b2dccfc8b77b0b4c2040b09acda1a2 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 12 Oct 2019 00:46:18 -0600 Subject: [PATCH 0342/1778] BinTray fixes --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 565c04b71..83c985ef8 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.49-SNAPSHOT + 4.8.48-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -42,7 +42,7 @@ bintray-classgraph-classgraph classgraph-classgraph - https://api.bintray.com/maven/classgraph/classgraph/classgraph/;publish=1 + https://api.bintray.com/maven/classgraph/classgraph/io.github.classgraph:classgraph/;publish=1 From 48912df416aa85114731052ffabdda8bf4b350f5 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 12 Oct 2019 00:46:52 -0600 Subject: [PATCH 0343/1778] [maven-release-plugin] prepare release classgraph-4.8.48 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 83c985ef8..0a5e0e6f6 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.48-SNAPSHOT + 4.8.48 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.48 From 67551da08ae22108c37a567eb308183c76e02305 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 12 Oct 2019 00:46:59 -0600 Subject: [PATCH 0344/1778] [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 0a5e0e6f6..f6bfd5f63 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.48 + 4.8.49-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.48 + HEAD From 5614f04d87280fad5f57f52a04e3cd50ed90b52a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 12 Oct 2019 00:54:23 -0600 Subject: [PATCH 0345/1778] BinTray fixes --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index f6bfd5f63..89bf0c79b 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.49-SNAPSHOT + 4.8.48-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -42,7 +42,7 @@ bintray-classgraph-classgraph classgraph-classgraph - https://api.bintray.com/maven/classgraph/classgraph/io.github.classgraph:classgraph/;publish=1 + https://api.bintray.com/maven/classgraph/classgraph/io.github.classgraph/;publish=1 From 5dc059c31bf0fc3de9590333de899fdce62910f3 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 12 Oct 2019 00:55:17 -0600 Subject: [PATCH 0346/1778] [maven-release-plugin] prepare release classgraph-4.8.48 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 89bf0c79b..0dbb09e6c 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.48-SNAPSHOT + 4.8.48 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.48 From 6c6c91278c33efc409bc9b66fca278b939966f5a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 12 Oct 2019 00:55:24 -0600 Subject: [PATCH 0347/1778] [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 0dbb09e6c..20f63abe6 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.48 + 4.8.49-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.48 + HEAD From 1002ab216b1ed10dcaa2a46199c4444798000efc Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 12 Oct 2019 09:28:01 -0600 Subject: [PATCH 0348/1778] Add `.verbose(boolean)` to conditionally enable logging. --- src/main/java/io/github/classgraph/ClassGraph.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index 8e8061a35..20356484e 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -109,6 +109,20 @@ public ClassGraph verbose() { return this; } + /** + * Switches on verbose logging to System.err if verbose is true. + * + * @param verbose + * if true, enable verbose logging. + * @return this (for method chaining). + */ + public ClassGraph verbose(final boolean verbose) { + if (verbose) { + verbose(); + } + return this; + } + // ------------------------------------------------------------------------------------------------------------- /** From 0dced0fac098f74d0519806d6d5d522c00575d28 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 12 Oct 2019 09:34:51 -0600 Subject: [PATCH 0349/1778] Add link to module instructions --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 96fdfefb0..6d0325a54 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,8 @@ ClassGraph provides a number of important capabilities to the JVM ecosystem: ``` +See instructions for [use as a module](https://github.com/classgraph/classgraph/wiki#use-as-a-module). + ### 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 af1f1562d9ac9672effe3bac64b2a4b1da95b824 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 12 Oct 2019 10:11:39 -0600 Subject: [PATCH 0350/1778] Reorder scanning entries in log to correspond to classpath order --- pom.xml | 2 +- .../github/classgraph/ClasspathElement.java | 27 ++++++++++++++++--- .../classgraph/ClasspathElementDir.java | 18 ++++++++----- .../classgraph/ClasspathElementModule.java | 16 ++++++----- .../classgraph/ClasspathElementZip.java | 14 ++++++---- .../java/io/github/classgraph/Scanner.java | 22 +++++++-------- .../classgraph/concurrency/WorkQueue.java | 10 ++++--- 7 files changed, 72 insertions(+), 37 deletions(-) diff --git a/pom.xml b/pom.xml index 20f63abe6..89bf0c79b 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.49-SNAPSHOT + 4.8.48-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. diff --git a/src/main/java/io/github/classgraph/ClasspathElement.java b/src/main/java/io/github/classgraph/ClasspathElement.java index 07bceec28..89aa6cdc4 100644 --- a/src/main/java/io/github/classgraph/ClasspathElement.java +++ b/src/main/java/io/github/classgraph/ClasspathElement.java @@ -317,6 +317,23 @@ protected void finishScanPaths(final LogNode log) { // ------------------------------------------------------------------------------------------------------------- + /** + * Write entries to log in classpath / module path order. + * + * @param classpathElementIdx + * the classpath element idx + * @param msg + * the log message + * @param log + * the log + * @return the new {@link LogNode} + */ + protected LogNode log(final int classpathElementIdx, final String msg, final LogNode log) { + return log.log(String.format("%07d", classpathElementIdx), msg); + } + + // ------------------------------------------------------------------------------------------------------------- + /** * Determine if this classpath element is valid. If it is not valid, sets skipClasspathElement. For * {@link ClasspathElementZip}, may also open or extract inner jars, and also causes jarfile manifests to be @@ -325,22 +342,26 @@ protected void finishScanPaths(final LogNode log) { * * @param workQueue * the work queue + * @param classpathElementIdx + * the index of the classpath element within the classpath or module path. * @param log * the log * @throws InterruptedException * if the thread was interrupted while trying to open the classpath element. */ - abstract void open(final WorkQueue workQueue, final LogNode log) - throws InterruptedException; + abstract void open(final WorkQueue workQueue, final int classpathEltIdx, + final LogNode log) throws InterruptedException; /** * Scan paths in the classpath element for whitelist/blacklist criteria, creating Resource objects for * whitelisted and non-blacklisted resources and classfiles. * + * @param classpathElementIdx + * the index of the classpath element within the classpath or module path. * @param log * the log */ - abstract void scanPaths(final LogNode log); + abstract void scanPaths(final int classpathElementIdx, final LogNode log); /** * Get the {@link Resource} for a given relative path. diff --git a/src/main/java/io/github/classgraph/ClasspathElementDir.java b/src/main/java/io/github/classgraph/ClasspathElementDir.java index 51654a746..64fedef38 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementDir.java @@ -87,10 +87,11 @@ class ClasspathElementDir extends ClasspathElement { * nonapi.io.github.classgraph.concurrency.WorkQueue, nonapi.io.github.classgraph.utils.LogNode) */ @Override - void open(final WorkQueue workQueue, final LogNode log) { + void open(final WorkQueue workQueue, final int classpathElementIdx, final LogNode log) { if (!scanSpec.scanDirs) { if (log != null) { - log.log("Skipping classpath element, since dir scanning is disabled: " + classpathEltDir); + log(classpathElementIdx, + "Skipping classpath element, since dir scanning is disabled: " + classpathEltDir, log); } skipClasspathElement = true; return; @@ -109,7 +110,7 @@ void open(final WorkQueue workQueue, final LogNode log) for (final File file : listFiles) { if (file.isFile() && file.getName().endsWith(".jar")) { if (log != null) { - log.log("Found lib jar: " + file); + log(classpathElementIdx, "Found lib jar: " + file, log); } workQueue.addWorkUnit(new ClasspathEntryWorkUnit( /* rawClasspathEntry = */ // @@ -125,7 +126,7 @@ void open(final WorkQueue workQueue, final LogNode log) final File packageRootDir = new File(classpathEltDir, packageRootPrefix); if (FileUtils.canReadAndIsDir(packageRootDir)) { if (log != null) { - log.log("Found package root: " + packageRootDir); + log(classpathElementIdx, "Found package root: " + packageRootDir, log); } workQueue .addWorkUnit(new ClasspathEntryWorkUnit( @@ -137,7 +138,8 @@ void open(final WorkQueue workQueue, final LogNode log) } } catch (final SecurityException e) { if (log != null) { - log.log("Skipping classpath element, since dir cannot be accessed: " + classpathEltDir); + log(classpathElementIdx, + "Skipping classpath element, since dir cannot be accessed: " + classpathEltDir, log); } skipClasspathElement = true; return; @@ -452,11 +454,13 @@ private void scanDirRecursively(final File dir, final LogNode log) { /** * Hierarchically scan directory structure for classfiles and matching files. * + * @param classpathElementIdx + * the index of the classpath element within the classpath or module path. * @param log * the log */ @Override - void scanPaths(final LogNode log) { + void scanPaths(final int classpathElementIdx, final LogNode log) { if (skipClasspathElement) { return; } @@ -466,7 +470,7 @@ void scanPaths(final LogNode log) { } final LogNode subLog = log == null ? null - : log.log(classpathEltDir.getPath(), "Scanning directory classpath element " + classpathEltDir); + : log(classpathElementIdx, "Scanning directory classpath element " + classpathEltDir, log); scanDirRecursively(classpathEltDir, subLog); diff --git a/src/main/java/io/github/classgraph/ClasspathElementModule.java b/src/main/java/io/github/classgraph/ClasspathElementModule.java index 08c0e236e..4e09a166d 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementModule.java +++ b/src/main/java/io/github/classgraph/ClasspathElementModule.java @@ -89,11 +89,12 @@ class ClasspathElementModule extends ClasspathElement { * nonapi.io.github.classgraph.concurrency.WorkQueue, nonapi.io.github.classgraph.utils.LogNode) */ @Override - void open(final WorkQueue workQueueIgnored, final LogNode log) - throws InterruptedException { + void open(final WorkQueue workQueueIgnored, final int classpathElementIdx, + final LogNode log) throws InterruptedException { if (!scanSpec.scanModules) { if (log != null) { - log.log("Skipping module, since module scanning is disabled: " + getModuleName()); + log(classpathElementIdx, "Skipping module, since module scanning is disabled: " + getModuleName(), + log); } skipClasspathElement = true; return; @@ -103,7 +104,7 @@ void open(final WorkQueue workQueueIgnored, final LogNod /* ignored */ null); } catch (final IOException | NullSingletonException e) { if (log != null) { - log.log("Skipping invalid module " + getModuleName() + " : " + e); + log(classpathElementIdx, "Skipping invalid module " + getModuleName() + " : " + e, log); } skipClasspathElement = true; return; @@ -229,11 +230,13 @@ Resource getResource(final String relativePath) { /** * Scan for package matches within module. * + * @param classpathElementIdx + * the index of the classpath element within the classpath or module path. * @param log * the log */ @Override - void scanPaths(final LogNode log) { + void scanPaths(final int classpathElementIdx, final LogNode log) { if (skipClasspathElement) { return; } @@ -242,9 +245,8 @@ void scanPaths(final LogNode log) { throw new IllegalArgumentException("Already scanned classpath element " + toString()); } - final String moduleLocationStr = moduleRef.getLocationStr(); final LogNode subLog = log == null ? null - : log.log(moduleLocationStr, "Scanning module " + moduleRef.getName()); + : log(classpathElementIdx, "Scanning module " + moduleRef.getName(), log); try (RecycleOnClose moduleReaderProxyRecycleOnClose // = moduleReaderProxyRecycler.acquireRecycleOnClose()) { diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index 95c8434ae..227eeb1fb 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -105,15 +105,17 @@ class ClasspathElementZip extends ClasspathElement { * nonapi.io.github.classgraph.concurrency.WorkQueue, nonapi.io.github.classgraph.utils.LogNode) */ @Override - void open(final WorkQueue workQueue, final LogNode log) throws InterruptedException { + void open(final WorkQueue workQueue, final int classpathElementIdx, final LogNode log) + throws InterruptedException { if (!scanSpec.scanJars) { if (log != null) { - log.log("Skipping classpath element, since jar scanning is disabled: " + rawPath); + log(classpathElementIdx, "Skipping classpath element, since jar scanning is disabled: " + rawPath, + log); } skipClasspathElement = true; return; } - final LogNode subLog = log == null ? null : log.log("Opening jar: " + rawPath); + 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, plingIdx < 0 ? rawPath : rawPath.substring(0, plingIdx)); @@ -390,11 +392,13 @@ Resource getResource(final String relativePath) { /** * Scan for path matches within jarfile, and record ZipEntry objects of matching files. * + * @param classpathElementIdx + * the index of the classpath element within the classpath or module path. * @param log * the log */ @Override - void scanPaths(final LogNode log) { + void scanPaths(final int classpathElementIdx, final LogNode log) { if (logicalZipFile == null) { skipClasspathElement = true; } @@ -407,7 +411,7 @@ void scanPaths(final LogNode log) { } final LogNode subLog = log == null ? null - : log.log(getZipFilePath(), "Scanning jarfile classpath element " + getZipFilePath()); + : log(classpathElementIdx, "Scanning jarfile classpath element " + getZipFilePath(), log); Set loggedNestedClasspathRootPrefixes = null; String prevParentRelativePath = null; diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index f9fe9f61f..efd246f60 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -182,7 +182,7 @@ class Scanner implements Callable { systemModuleRef, defaultClassLoader, nestedJarHandler, scanSpec); moduleOrder.add(classpathElementModule); // Open the ClasspathElementModule - classpathElementModule.open(/* ignored */ null, classpathFinderLog); + classpathElementModule.open(/* ignored */ null, moduleOrder.size() - 1, classpathFinderLog); } else { if (classpathFinderLog != null) { classpathFinderLog @@ -204,7 +204,7 @@ class Scanner implements Callable { nonSystemModuleRef, defaultClassLoader, nestedJarHandler, scanSpec); moduleOrder.add(classpathElementModule); // Open the ClasspathElementModule - classpathElementModule.open(/* ignored */ null, classpathFinderLog); + classpathElementModule.open(/* ignored */ null, moduleOrder.size() - 1, classpathFinderLog); } else { if (classpathFinderLog != null) { classpathFinderLog.log("Skipping non-whitelisted or blacklisted module: " + moduleName); @@ -447,7 +447,7 @@ private WorkUnitProcessor newClasspathEntryWorkUnitProce final Queue> toplevelClasspathEltOrder) { return new WorkUnitProcessor() { @Override - public void processWorkUnit(final ClasspathEntryWorkUnit workUnit, + public void processWorkUnit(final ClasspathEntryWorkUnit workUnit, final int workUnitIdx, final WorkQueue workQueue, final LogNode log) throws InterruptedException { try { @@ -470,7 +470,7 @@ public void processWorkUnit(final ClasspathEntryWorkUnit workUnit, // 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, log); + classpathElt.open(workQueue, workUnitIdx, log); // Create a new tuple consisting of the order of the new classpath element // within its parent, and the new classpath element. @@ -581,7 +581,7 @@ public ClassfileScannerWorkUnitProcessor(final ScanSpec scanSpec, * java.lang.Object, nonapi.io.github.classgraph.concurrency.WorkQueue) */ @Override - public void processWorkUnit(final ClassfileScanWorkUnit workUnit, + public void processWorkUnit(final ClassfileScanWorkUnit workUnit, final int workUnitIdxIgnored, final WorkQueue workQueue, final LogNode log) throws InterruptedException { // Classfile scan log entries are listed inline below the entry that was added to the log // when the path of the corresponding resource was found, by using the LogNode stored in @@ -602,15 +602,15 @@ public void processWorkUnit(final ClassfileScanWorkUnit workUnit, } catch (final SkipClassException e) { if (subLog != null) { - subLog.log("Skipping classfile: " + e.getMessage()); + subLog.log(workUnit.classfileResource.getPath(), "Skipping classfile: " + e.getMessage()); } } catch (final ClassfileFormatException e) { if (subLog != null) { - subLog.log("Invalid classfile: " + e.getMessage()); + subLog.log(workUnit.classfileResource.getPath(), "Invalid classfile: " + e.getMessage()); } } catch (final IOException e) { if (subLog != null) { - subLog.log("Could not read classfile: " + e); + subLog.log(workUnit.classfileResource.getPath(), "Could not read classfile: " + e); } } finally { if (subLog != null) { @@ -949,10 +949,10 @@ private ScanResult openClasspathElementsThenScan() throws InterruptedException, new WorkUnitProcessor() { @Override public void processWorkUnit(final ClasspathElement classpathElement, - final WorkQueue workQueueIgnored, final LogNode pathScanLog) - throws InterruptedException { + final int classpathElementIdx, final WorkQueue workQueueIgnored, + final LogNode pathScanLog) throws InterruptedException { // Scan the paths within the classpath element - classpathElement.scanPaths(pathScanLog); + classpathElement.scanPaths(classpathElementIdx, pathScanLog); } }); 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 9d70802c0..1e52acb65 100644 --- a/src/main/java/nonapi/io/github/classgraph/concurrency/WorkQueue.java +++ b/src/main/java/nonapi/io/github/classgraph/concurrency/WorkQueue.java @@ -104,11 +104,14 @@ public WorkUnitWrapper(final T workUnit) { * The type of work unit to process. */ public interface WorkUnitProcessor { + /** * Process a work unit. * * @param workUnit * The work unit. + * @param workUnitIndex + * the index of the work unit from the original start of the work queue. * @param workQueue * The work queue. * @param log @@ -116,7 +119,8 @@ public interface WorkUnitProcessor { * @throws InterruptedException * If the worker thread is interrupted. */ - void processWorkUnit(T workUnit, WorkQueue workQueue, LogNode log) throws InterruptedException; + void processWorkUnit(T workUnit, int workUnitIndex, WorkQueue workQueue, LogNode log) + throws InterruptedException; } /** @@ -227,7 +231,7 @@ private void sendPoisonPills() { */ private void runWorkLoop() throws InterruptedException, ExecutionException { // Get next work unit from queue - for (;;) { + for (int workUnitIdx = 0;; workUnitIdx++) { // Check for interruption interruptionChecker.check(); @@ -242,7 +246,7 @@ private void runWorkLoop() throws InterruptedException, ExecutionException { // Process the work unit try { // Process the work unit (may throw InterruptedException) - workUnitProcessor.processWorkUnit(workUnitWrapper.workUnit, this, log); + workUnitProcessor.processWorkUnit(workUnitWrapper.workUnit, workUnitIdx, this, log); } catch (InterruptedException | OutOfMemoryError e) { // On InterruptedException or OutOfMemoryError, drain work queue, send poison pills, and re-throw From f108648e9b9fa9bed18b48ae591c54d62d234923 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 15 Oct 2019 21:24:46 -0600 Subject: [PATCH 0351/1778] BinTray fix --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 89bf0c79b..83c985ef8 100644 --- a/pom.xml +++ b/pom.xml @@ -42,7 +42,7 @@ bintray-classgraph-classgraph classgraph-classgraph - https://api.bintray.com/maven/classgraph/classgraph/io.github.classgraph/;publish=1 + https://api.bintray.com/maven/classgraph/classgraph/io.github.classgraph:classgraph/;publish=1 From 1603e4e83c7c781fc896b3630511ad2f4c0afe03 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 15 Oct 2019 21:27:59 -0600 Subject: [PATCH 0352/1778] BinTray fix --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 83c985ef8..fbdfdeceb 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.48-SNAPSHOT + 4.8.48 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 6d3c76f603575ed27dfed16a48239ae5f327a0a8 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 15 Oct 2019 21:33:36 -0600 Subject: [PATCH 0353/1778] BinTray fix --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fbdfdeceb..83c985ef8 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.48 + 4.8.48-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 668a44322f393c1032c8ddba1f8a53c5781af13c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 15 Oct 2019 21:34:25 -0600 Subject: [PATCH 0354/1778] [maven-release-plugin] prepare release classgraph-4.8.48 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 83c985ef8..0a5e0e6f6 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.48-SNAPSHOT + 4.8.48 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.48 From 12aacfe0c7343dba6b78a769dcf47e9a2d08a367 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 15 Oct 2019 21:34:32 -0600 Subject: [PATCH 0355/1778] [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 0a5e0e6f6..f6bfd5f63 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.48 + 4.8.49-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.48 + HEAD From 3f8def731713b0e5954be8a356b2fd521372d093 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 15 Oct 2019 21:36:15 -0600 Subject: [PATCH 0356/1778] BinTray fix --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f6bfd5f63..fbdfdeceb 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.49-SNAPSHOT + 4.8.48 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 95d8a486b21815bec20ed71ec369c97935e649ce Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 15 Oct 2019 21:43:27 -0600 Subject: [PATCH 0357/1778] Re-enable gpg signing --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index fbdfdeceb..7f42ab216 100644 --- a/pom.xml +++ b/pom.xml @@ -518,7 +518,7 @@ - + From 7fc6b90cae9fde76838d64021f2668ea015b4fcb Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 15 Oct 2019 21:51:33 -0600 Subject: [PATCH 0358/1778] Fix Javadoc issue --- src/main/java/io/github/classgraph/ClasspathElement.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElement.java b/src/main/java/io/github/classgraph/ClasspathElement.java index 89aa6cdc4..e5d82db4f 100644 --- a/src/main/java/io/github/classgraph/ClasspathElement.java +++ b/src/main/java/io/github/classgraph/ClasspathElement.java @@ -349,7 +349,7 @@ protected LogNode log(final int classpathElementIdx, final String msg, final Log * @throws InterruptedException * if the thread was interrupted while trying to open the classpath element. */ - abstract void open(final WorkQueue workQueue, final int classpathEltIdx, + abstract void open(final WorkQueue workQueue, final int classpathElementIdx, final LogNode log) throws InterruptedException; /** From 4212dda0ecedd280ee95a2aa44ff7f677d356a17 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 15 Oct 2019 21:53:48 -0600 Subject: [PATCH 0359/1778] Move jar signing plugin --- pom.xml | 54 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/pom.xml b/pom.xml index 7f42ab216..b32f74470 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,7 @@ - + 4.0.0 io.github.classgraph @@ -149,7 +152,8 @@ - + org.codehaus.mojo.signature @@ -395,7 +399,27 @@ - + + + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + + + sign-artifacts + verify + + sign + + + ${gpg.keyname} + ${gpg.keyname} + + + + + + org.apache.maven.plugins maven-release-plugin @@ -497,6 +521,7 @@ + org.apache.maven.plugins maven-source-plugin @@ -508,6 +533,7 @@ + org.apache.maven.plugins maven-javadoc-plugin @@ -518,24 +544,10 @@ - - org.apache.maven.plugins - maven-gpg-plugin - 1.6 - - - sign-artifacts - deploy - - sign - - - ${gpg.keyname} - ${gpg.keyname} - - - - + From c051eb8bdf1cc2722b4355f7550485eecdeb5a3c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 15 Oct 2019 22:45:25 -0600 Subject: [PATCH 0360/1778] Try Sonatype again --- pom.xml | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/pom.xml b/pom.xml index b32f74470..3fa7b86ef 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.github.classgraph classgraph - 4.8.48 + 4.8.48-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -42,10 +42,13 @@ + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + - bintray-classgraph-classgraph - classgraph-classgraph - https://api.bintray.com/maven/classgraph/classgraph/io.github.classgraph:classgraph/;publish=1 + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ @@ -107,6 +110,10 @@ org.apache.maven.plugins maven-javadoc-plugin + + org.apache.maven.plugins + maven-gpg-plugin + org.apache.maven.plugins maven-release-plugin @@ -404,6 +411,14 @@ org.apache.maven.plugins maven-gpg-plugin 1.6 + + + --pinentry-mode + loopback + + ${gpg.keyname} + ${gpg.keyname} + sign-artifacts @@ -411,10 +426,6 @@ sign - - ${gpg.keyname} - ${gpg.keyname} - @@ -544,10 +555,6 @@ - From c7714f9323afdccadff4dee46fc7ccc0abfd02c8 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 15 Oct 2019 22:46:07 -0600 Subject: [PATCH 0361/1778] [maven-release-plugin] prepare release classgraph-4.8.48 --- pom.xml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index 3fa7b86ef..225ded0b0 100644 --- a/pom.xml +++ b/pom.xml @@ -1,12 +1,9 @@ - + 4.0.0 io.github.classgraph classgraph - 4.8.48-SNAPSHOT + 4.8.48 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 - HEAD + classgraph-4.8.48 @@ -159,8 +156,7 @@ - + org.codehaus.mojo.signature From 4ac455d6bb9178b831f6459b3beb45bef1a77f99 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 15 Oct 2019 22:46:14 -0600 Subject: [PATCH 0362/1778] [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 225ded0b0..b2636d5de 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.48 + 4.8.49-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.48 + HEAD From fbe0d1c1d15cc23b0252e45154b471fe444ad6d4 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 15 Oct 2019 22:50:33 -0600 Subject: [PATCH 0363/1778] Add back Nexus staging plugin --- pom.xml | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index b2636d5de..3a363b74f 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,7 @@ - + 4.0.0 io.github.classgraph @@ -111,6 +114,10 @@ org.apache.maven.plugins maven-gpg-plugin + + org.sonatype.plugins + nexus-staging-maven-plugin + org.apache.maven.plugins maven-release-plugin @@ -156,7 +163,8 @@ - + org.codehaus.mojo.signature @@ -426,6 +434,21 @@ + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.8 + true + + ossrh + https://oss.sonatype.org/ + true + + + org.apache.maven.plugins From f97c675fb1750903edf9c6faefe117db03db30cf Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 15 Oct 2019 22:50:52 -0600 Subject: [PATCH 0364/1778] Bump version number back down --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3a363b74f..b9689d92d 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.github.classgraph classgraph - 4.8.49-SNAPSHOT + 4.8.48-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 459762ebb8986da95218e22bcf3c0d7a8ecf5e43 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 15 Oct 2019 22:51:42 -0600 Subject: [PATCH 0365/1778] [maven-release-plugin] prepare release classgraph-4.8.48 --- pom.xml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index b9689d92d..c878fafa9 100644 --- a/pom.xml +++ b/pom.xml @@ -1,12 +1,9 @@ - + 4.0.0 io.github.classgraph classgraph - 4.8.48-SNAPSHOT + 4.8.48 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 - HEAD + classgraph-4.8.48 @@ -163,8 +160,7 @@ - + org.codehaus.mojo.signature From 511e567c606d71bd2f36679d74a552e42b4cb30b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 15 Oct 2019 22:51:49 -0600 Subject: [PATCH 0366/1778] [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 c878fafa9..850f5d7eb 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.48 + 4.8.49-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.48 + HEAD From 91960539b3d14ef7c93ca6cef0a970db83458321 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 15 Oct 2019 22:59:34 -0600 Subject: [PATCH 0367/1778] Source > Format --- pom.xml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 850f5d7eb..3a363b74f 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,7 @@ - + 4.0.0 io.github.classgraph @@ -160,7 +163,8 @@ - + org.codehaus.mojo.signature From 2dae6aa12c2d0cfd541b50a4c6c6d8ffc418f619 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 15 Oct 2019 23:09:49 -0600 Subject: [PATCH 0368/1778] Move signing to release profile --- pom.xml | 47 +++++++++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/pom.xml b/pom.xml index 3a363b74f..62807a825 100644 --- a/pom.xml +++ b/pom.xml @@ -410,30 +410,6 @@ - - - org.apache.maven.plugins - maven-gpg-plugin - 1.6 - - - --pinentry-mode - loopback - - ${gpg.keyname} - ${gpg.keyname} - - - - sign-artifacts - verify - - sign - - - - - @@ -574,6 +550,29 @@ + + + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + + + --pinentry-mode + loopback + + ${gpg.keyname} + ${gpg.keyname} + + + + sign-artifacts + verify + + sign + + + + From b0845f985d40a2391e182056f1ed935a9dc5aa65 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 17 Oct 2019 12:14:25 -0600 Subject: [PATCH 0369/1778] Fix "getClassInfo() cannot be called here" in BaseTypeSignature (#375) --- src/main/java/io/github/classgraph/BaseTypeSignature.java | 5 ++--- src/main/java/io/github/classgraph/FieldInfo.java | 3 ++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/github/classgraph/BaseTypeSignature.java b/src/main/java/io/github/classgraph/BaseTypeSignature.java index af16b4fca..2dc219b16 100644 --- a/src/main/java/io/github/classgraph/BaseTypeSignature.java +++ b/src/main/java/io/github/classgraph/BaseTypeSignature.java @@ -236,8 +236,7 @@ static BaseTypeSignature parse(final Parser parser) { */ @Override protected String getClassName() { - // getClassInfo() is not valid for this type, so getClassName() does not need to be implemented - throw new IllegalArgumentException("getClassName() cannot be called here"); + return baseType; } /* (non-Javadoc) @@ -245,7 +244,7 @@ protected String getClassName() { */ @Override protected ClassInfo getClassInfo() { - throw new IllegalArgumentException("getClassInfo() cannot be called here"); + return null; } /** diff --git a/src/main/java/io/github/classgraph/FieldInfo.java b/src/main/java/io/github/classgraph/FieldInfo.java index 330d0c5c3..4a50d5d80 100644 --- a/src/main/java/io/github/classgraph/FieldInfo.java +++ b/src/main/java/io/github/classgraph/FieldInfo.java @@ -130,7 +130,8 @@ public String getName() { /** * Get the {@link ClassInfo} object for the declaring class (i.e. the class that declares this field). * - * @return The {@link ClassInfo} object for the declaring class (i.e. the class that declares this field). + * @return The {@link ClassInfo} object for the declaring class (i.e. the class that declares this field), or + * null if the class representing the type of the field was not encountered during scanning. */ @Override public ClassInfo getClassInfo() { From 40c1feb0b8ad946173e9733d9c406c25f8f6eebc Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 17 Oct 2019 12:15:18 -0600 Subject: [PATCH 0370/1778] [maven-release-plugin] prepare release classgraph-4.8.49 --- pom.xml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index 62807a825..792e8247c 100644 --- a/pom.xml +++ b/pom.xml @@ -1,12 +1,9 @@ - + 4.0.0 io.github.classgraph classgraph - 4.8.49-SNAPSHOT + 4.8.49 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 - HEAD + classgraph-4.8.49 @@ -163,8 +160,7 @@ - + org.codehaus.mojo.signature From 4507bf722fadc338071ce9005c2e491dfb222020 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 17 Oct 2019 12:15:25 -0600 Subject: [PATCH 0371/1778] [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 792e8247c..852c68c48 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.49 + 4.8.50-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.49 + HEAD From bce29d9236240bad8035b91ce5d69d523e7dd73e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 17 Oct 2019 12:30:17 -0600 Subject: [PATCH 0372/1778] Move plugin version numbers out of pluginManagement --- pom.xml | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/pom.xml b/pom.xml index 852c68c48..1d4424bc4 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,7 @@ - + 4.0.0 io.github.classgraph @@ -67,68 +70,80 @@ + org.apache.maven.plugins maven-enforcer-plugin + 3.0.0-M2 org.apache.maven.plugins maven-resources-plugin + 3.1.0 org.apache.maven.plugins maven-compiler-plugin + 3.8.1 org.apache.maven.plugins maven-surefire-plugin + 3.0.0-M3 org.apache.felix maven-bundle-plugin + 4.2.1 org.moditect moditect-maven-plugin + 1.0.0.Beta2 org.codehaus.mojo build-helper-maven-plugin + 3.0.0 org.apache.maven.plugins maven-jar-plugin + 3.1.1 org.apache.maven.plugins maven-source-plugin + 3.1.0 org.apache.maven.plugins maven-javadoc-plugin + 3.0.1 org.apache.maven.plugins maven-gpg-plugin + 1.6 org.sonatype.plugins nexus-staging-maven-plugin + 1.6.8 org.apache.maven.plugins maven-release-plugin + - org.apache.maven.plugins maven-enforcer-plugin - 3.0.0-M2 org.codehaus.mojo @@ -160,7 +175,8 @@ - + org.codehaus.mojo.signature @@ -176,8 +192,8 @@ + org.apache.maven.plugins maven-resources-plugin - 3.1.0 copy-license-to-target @@ -224,7 +240,6 @@ org.apache.maven.plugins maven-compiler-plugin - 3.8.1 UTF-8 @@ -262,14 +277,12 @@ org.apache.maven.plugins maven-surefire-plugin - 3.0.0-M3 org.apache.felix maven-bundle-plugin - 4.2.1 true @@ -296,7 +309,6 @@ org.moditect moditect-maven-plugin - 1.0.0.Beta2 add-module-infos @@ -338,7 +350,6 @@ org.codehaus.mojo build-helper-maven-plugin - 3.0.0 add-test-source @@ -359,7 +370,6 @@ org.apache.maven.plugins maven-jar-plugin - 3.1.1 ${project.build.outputDirectory}/META-INF/MANIFEST.MF @@ -375,7 +385,6 @@ org.apache.maven.plugins maven-source-plugin - 3.1.0 attach-sources @@ -390,7 +399,6 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.0.1 attach-javadocs @@ -412,7 +420,6 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.8 true ossrh @@ -425,7 +432,6 @@ org.apache.maven.plugins maven-release-plugin - 2.5.3 true false @@ -550,7 +556,6 @@ org.apache.maven.plugins maven-gpg-plugin - 1.6 --pinentry-mode From 87e813fd712f3b8457d831589063f1d6ca0035a1 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 17 Oct 2019 14:34:56 -0600 Subject: [PATCH 0373/1778] JavaDoc fixes --- src/main/java/io/github/classgraph/ScanResult.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index bcae16e9d..dec9d5d22 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -522,13 +522,13 @@ public ResourceList getResourcesWithPath(final String resourcePath) { /** * Get the list of all resources found in any classpath element, whether in whitelisted packages or not (as - * long as the resource is not blacklisted), that have the given path, relative to the package root of the + * long as the resource is not blacklisted), 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 any classpath element, whether in whitelisted packages or not (as - * long as the resource is not blacklisted), that have the given path, relative to the package root + * long as the resource is not blacklisted), 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 getResourcesWithPathIgnoringWhitelist(final String resourcePath) { From a09536906719da72615ac087fa8636d8a4d77c1e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 17 Oct 2019 14:37:12 -0600 Subject: [PATCH 0374/1778] JavaDoc fixes --- .../nonapi/io/github/classgraph/utils/CollectionUtils.java | 2 ++ .../java/nonapi/io/github/classgraph/utils/FileUtils.java | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) 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 68b896957..6134df200 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/CollectionUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/CollectionUtils.java @@ -67,6 +67,8 @@ public static > void sortIfNotEmpty(final List void sortIfNotEmpty(final List list, final Comparator comparator) { if (!list.isEmpty()) { 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 039ff153a..6296ee2c0 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -457,7 +457,7 @@ public static boolean canReadAndIsFile(final File file) { * * @param file * A {@link File}. - * @throw IOException if the file does not exist, is not a regular file, or cannot be read. + * @throws IOException if the file does not exist, is not a regular file, or cannot be read. */ public static void checkCanReadAndIsFile(final File file) throws IOException { try { @@ -495,7 +495,7 @@ public static boolean canReadAndIsDir(final File file) { * * @param file * A {@link File}. - * @throw IOException if the file does not exist, is not a directory, or cannot be read. + * @throws IOException if the file does not exist, is not a directory, or cannot be read. */ public static void checkCanReadAndIsDir(final File file) throws IOException { try { From 9f05d40e847144880bd9e7392fcb7b0f018680e7 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 17 Oct 2019 15:16:41 -0600 Subject: [PATCH 0375/1778] Fix deprecation warning --- .../classgraph/features/DeclaredVsNonDeclaredTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/io/github/classgraph/features/DeclaredVsNonDeclaredTest.java b/src/test/java/io/github/classgraph/features/DeclaredVsNonDeclaredTest.java index b6e0b676e..aa38c0020 100644 --- a/src/test/java/io/github/classgraph/features/DeclaredVsNonDeclaredTest.java +++ b/src/test/java/io/github/classgraph/features/DeclaredVsNonDeclaredTest.java @@ -5,8 +5,8 @@ import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.function.Function; -import org.assertj.core.api.iterable.Extractor; import org.junit.jupiter.api.Test; import io.github.classgraph.AnnotationInfo; @@ -140,9 +140,9 @@ public void declaredVsNonDeclaredMethods() { */ @Test public void annotationInfosShouldBeAbleToDifferentiateBetweenDirectAndReachable() { - final Extractor annotationNameExtractor = new Extractor() { + final Function annotationNameExtractor = new Function() { @Override - public Object extract(final AnnotationInfo input) { + public Object apply(AnnotationInfo input) { return input.getName(); } }; From f3ff2d9d6414ac29d740b2d4f353a174a4777f14 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 17 Oct 2019 15:17:23 -0600 Subject: [PATCH 0376/1778] Fix prev commit --- .../github/classgraph/features/DeclaredVsNonDeclaredTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/io/github/classgraph/features/DeclaredVsNonDeclaredTest.java b/src/test/java/io/github/classgraph/features/DeclaredVsNonDeclaredTest.java index aa38c0020..0d468cf0d 100644 --- a/src/test/java/io/github/classgraph/features/DeclaredVsNonDeclaredTest.java +++ b/src/test/java/io/github/classgraph/features/DeclaredVsNonDeclaredTest.java @@ -140,9 +140,9 @@ public void declaredVsNonDeclaredMethods() { */ @Test public void annotationInfosShouldBeAbleToDifferentiateBetweenDirectAndReachable() { - final Function annotationNameExtractor = new Function() { + final Function annotationNameExtractor = new Function() { @Override - public Object apply(AnnotationInfo input) { + public String apply(AnnotationInfo input) { return input.getName(); } }; From ae8f1ff7d621b1733a1f36b07a695b0b2675bcc6 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 17 Oct 2019 15:19:48 -0600 Subject: [PATCH 0377/1778] Use function reference --- .../features/DeclaredVsNonDeclaredTest.java | 22 ++++++------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/src/test/java/io/github/classgraph/features/DeclaredVsNonDeclaredTest.java b/src/test/java/io/github/classgraph/features/DeclaredVsNonDeclaredTest.java index 0d468cf0d..116b2b71e 100644 --- a/src/test/java/io/github/classgraph/features/DeclaredVsNonDeclaredTest.java +++ b/src/test/java/io/github/classgraph/features/DeclaredVsNonDeclaredTest.java @@ -5,7 +5,6 @@ import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.function.Function; import org.junit.jupiter.api.Test; @@ -140,13 +139,6 @@ public void declaredVsNonDeclaredMethods() { */ @Test public void annotationInfosShouldBeAbleToDifferentiateBetweenDirectAndReachable() { - final Function annotationNameExtractor = new Function() { - @Override - public String apply(AnnotationInfo input) { - return input.getName(); - } - }; - try (ScanResult scanResult = new ClassGraph().enableAllInfo() .whitelistPackages(DeclaredVsNonDeclaredTest.class.getPackage().getName()).scan()) { final ClassInfo A = scanResult.getClassInfo(A.class.getName()); @@ -156,31 +148,31 @@ public String apply(AnnotationInfo input) { final AnnotationInfoList annotationInfossOnA = A.getAnnotationInfo(); final AnnotationInfoList annotationsInfosOnB = B.getAnnotationInfo(); - assertThat(annotationInfossOnA).extracting(annotationNameExtractor).containsExactlyInAnyOrder( + assertThat(annotationInfossOnA).extracting(AnnotationInfo::getName).containsExactlyInAnyOrder( InheritedAnnotation.class.getName(), NormalAnnotation.class.getName(), InheritedMetaAnnotation.class.getName(), NonInheritedMetaAnnotation.class.getName()); - assertThat(annotationsInfosOnB).extracting(annotationNameExtractor).containsExactlyInAnyOrder( + assertThat(annotationsInfosOnB).extracting(AnnotationInfo::getName).containsExactlyInAnyOrder( InheritedAnnotation.class.getName(), InheritedMetaAnnotation.class.getName()); - assertThat(annotationInfossOnA.directOnly()).extracting(annotationNameExtractor) + assertThat(annotationInfossOnA.directOnly()).extracting(AnnotationInfo::getName) .containsExactlyInAnyOrder(NormalAnnotation.class.getName(), InheritedAnnotation.class.getName()); assertThat(annotationsInfosOnB.directOnly()).isEmpty(); - assertThat(C.getAnnotationInfo().directOnly()).extracting(annotationNameExtractor) + assertThat(C.getAnnotationInfo().directOnly()).extracting(AnnotationInfo::getName) .containsExactlyInAnyOrder(NormalAnnotation.class.getName()); final AnnotationInfoList annotationsOnAw = A.getMethodInfo().getSingleMethod("w").getAnnotationInfo(); - assertThat(annotationsOnAw).extracting(annotationNameExtractor).containsExactlyInAnyOrder( + assertThat(annotationsOnAw).extracting(AnnotationInfo::getName).containsExactlyInAnyOrder( InheritedAnnotation.class.getName(), InheritedMetaAnnotation.class.getName(), NonInheritedMetaAnnotation.class.getName()); final AnnotationInfoList annotationsOnBw = B.getMethodInfo().getSingleMethod("w").getAnnotationInfo(); - assertThat(annotationsOnBw).extracting(annotationNameExtractor).isEmpty(); + assertThat(annotationsOnBw).extracting(AnnotationInfo::getName).isEmpty(); // See note on inherited annotations on methods // https://docs.oracle.com/javase/8/docs/api/java/lang/annotation/Inherited.html // "Note that this (@Inherited) meta-annotation type has no effect if the annotated type is used to // annotate anything other than a class. Note also that this meta-annotation only causes annotations // to be inherited from superclasses; annotations on implemented interfaces have no effect." - assertThat(annotationsOnBw.directOnly()).extracting(annotationNameExtractor).isEmpty(); + assertThat(annotationsOnBw.directOnly()).extracting(AnnotationInfo::getName).isEmpty(); } } From cefbcf65cd924a5e2b34afde20cb85f0777d43ad Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 17 Oct 2019 15:24:08 -0600 Subject: [PATCH 0378/1778] Update dependency, plugin, and Maven versions --- .mvn/wrapper/maven-wrapper.properties | 2 +- pom.xml | 132 +++++++++++++------------- 2 files changed, 68 insertions(+), 66 deletions(-) diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index fa87ad7dd..7d59a01f2 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.1/apache-maven-3.6.1-bin.zip +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.2/apache-maven-3.6.2-bin.zip wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar diff --git a/pom.xml b/pom.xml index 1d4424bc4..e50130e5b 100644 --- a/pom.xml +++ b/pom.xml @@ -66,11 +66,70 @@ + + + + + org.junit.jupiter + junit-jupiter + 5.5.2 + test + + + org.openjdk.jmh + jmh-core + 1.21 + test + + + org.openjdk.jmh + jmh-generator-annprocess + 1.21 + test + + + org.assertj + assertj-core + 3.13.2 + test + + + javax.enterprise + cdi-api + 2.0.SP1 + test + + + org.ops4j.pax.url + pax-url-aether + 2.6.1 + test + + + org.slf4j + slf4j-api + 2.0.0-alpha1 + test + + + org.slf4j + slf4j-jdk14 + 2.0.0-alpha1 + test + + + org.hibernate.javax.persistence + hibernate-jpa-2.1-api + 1.0.2.Final + test + + + - + org.apache.maven.plugins maven-enforcer-plugin @@ -109,7 +168,7 @@ org.apache.maven.plugins maven-jar-plugin - 3.1.1 + 3.1.2 org.apache.maven.plugins @@ -119,7 +178,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.0.1 + 3.1.1 org.apache.maven.plugins @@ -134,7 +193,7 @@ org.apache.maven.plugins maven-release-plugin - + @@ -161,7 +220,7 @@ - 3.6.1 + [3.6.2,) @@ -406,9 +465,11 @@ jar - none + 8 ${javadoc.html.version} + all nonapi.* + public @@ -443,65 +504,6 @@ - - - - - org.junit.jupiter - junit-jupiter - 5.5.2 - test - - - org.openjdk.jmh - jmh-core - 1.21 - test - - - org.openjdk.jmh - jmh-generator-annprocess - 1.21 - test - - - org.assertj - assertj-core - 3.11.1 - test - - - javax.enterprise - cdi-api - 1.0-SP4 - test - - - org.ops4j.pax.url - pax-url-aether - 2.6.1 - test - - - org.slf4j - slf4j-api - 1.8.0-beta2 - test - - - org.slf4j - slf4j-jdk14 - 1.8.0-beta2 - test - - - org.hibernate.javax.persistence - hibernate-jpa-2.1-api - 1.0.2.Final - test - - - From 9bd72d919cc710ca95f6b2dcdda77fb4b8e04a87 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 17 Oct 2019 16:32:53 -0600 Subject: [PATCH 0379/1778] Fix Eclipse JDT nullability analysis warnings --- .../java/io/github/classgraph/Classfile.java | 10 +-- .../java/io/github/classgraph/InfoList.java | 4 +- .../github/classgraph/MappableInfoList.java | 16 +++-- .../classgraph/ObjectTypedValueWrapper.java | 10 +-- .../EquinoxClassLoaderHandler.java | 5 +- .../TomcatWebappClassLoaderBaseHandler.java | 69 ++++++++++--------- .../fastzipfilereader/NestedJarHandler.java | 9 +-- .../classgraph/json/JSONDeserializer.java | 38 +++++----- .../classgraph/json/JSONSerializer.java | 11 +-- .../classgraph/json/ReferenceEqualityKey.java | 4 +- .../io/github/classgraph/utils/FileUtils.java | 6 +- .../io/github/classgraph/utils/Join.java | 6 +- .../issues/issue223/Issue223Test.java | 10 +-- 13 files changed, 113 insertions(+), 85 deletions(-) diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index c1fb2a7c6..63617fe9d 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -1043,7 +1043,7 @@ private void readConstantPoolEntries() throws IOException { case 7: // Class reference (format is e.g. "java/lang/String") // Forward or backward indirect reference to a modified UTF8 entry indirectStringRefs[i] = inputStreamOrByteBuffer.readUnsignedShort(); - if (scanSpec.enableInterClassDependencies) { + if (classNameCpIdxs != null) { // If this is a class ref, and inter-class dependencies are enabled, record the dependency classNameCpIdxs.add(indirectStringRefs[i]); } @@ -1067,7 +1067,7 @@ private void readConstantPoolEntries() throws IOException { case 12: // name and type final int nameRef = inputStreamOrByteBuffer.readUnsignedShort(); final int typeRef = inputStreamOrByteBuffer.readUnsignedShort(); - if (scanSpec.enableInterClassDependencies) { + if (typeSignatureIdxs != null) { typeSignatureIdxs.add(typeRef); } indirectStringRefs[i] = (nameRef << 16) | typeRef; @@ -1101,7 +1101,7 @@ private void readConstantPoolEntries() throws IOException { // referenced as strings (tag 1) rather than classes (tag 7) or type signatures (part of tag 12). // Therefore, a hybrid approach needs to be applied of extracting these other class refs from // the ClassInfo graph, and combining them with class names extracted from the constant pool here. - if (scanSpec.enableInterClassDependencies) { + if (classNameCpIdxs != null) { refdClassNames = new HashSet<>(); // Get class names from direct class references in constant pool for (final int cpIdx : classNameCpIdxs) { @@ -1123,6 +1123,8 @@ private void readConstantPoolEntries() throws IOException { } } } + } + if (typeSignatureIdxs != null) { // Get class names from type signatures in "name and type" entries in constant pool for (final int cpIdx : typeSignatureIdxs) { final String typeSigStr = getConstantPoolString(cpIdx); @@ -1644,7 +1646,7 @@ private void readClassAttributes() throws IOException, ClassfileFormatException : log.log("Found " // + (isAnnotation ? "annotation class" : isInterface ? "interface class" : "class") // + " " + className); - if (log != null) { + if (subLog != null) { if (superclassName != null) { subLog.log( "Super" + (isInterface && !isAnnotation ? "interface" : "class") + ": " + superclassName); diff --git a/src/main/java/io/github/classgraph/InfoList.java b/src/main/java/io/github/classgraph/InfoList.java index 51d82ae98..9896d527d 100644 --- a/src/main/java/io/github/classgraph/InfoList.java +++ b/src/main/java/io/github/classgraph/InfoList.java @@ -95,7 +95,9 @@ public List getNames() { } else { final List names = new ArrayList<>(this.size()); for (final T i : this) { - names.add(i.getName()); + if (i != null) { + names.add(i.getName()); + } } return names; } diff --git a/src/main/java/io/github/classgraph/MappableInfoList.java b/src/main/java/io/github/classgraph/MappableInfoList.java index 9bead2843..e72ec4cf7 100644 --- a/src/main/java/io/github/classgraph/MappableInfoList.java +++ b/src/main/java/io/github/classgraph/MappableInfoList.java @@ -76,7 +76,9 @@ public class MappableInfoList extends InfoList { public Map asMap() { final Map nameToInfoObject = new HashMap<>(); for (final T i : this) { - nameToInfoObject.put(i.getName(), i); + if (i != null) { + nameToInfoObject.put(i.getName(), i); + } } return nameToInfoObject; } @@ -90,8 +92,10 @@ public Map asMap() { */ public boolean containsName(final String name) { for (final T i : this) { - if (i.getName().equals(name)) { - return true; + if (i != null) { + if (i.getName().equals(name)) { + return true; + } } } return false; @@ -106,8 +110,10 @@ public boolean containsName(final String name) { */ public T get(final String name) { for (final T i : this) { - if (i.getName().equals(name)) { - return i; + if (i != null) { + if (i.getName().equals(name)) { + return i; + } } } return null; diff --git a/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java b/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java index 3e63fea8f..472f7b5c8 100644 --- a/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java +++ b/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java @@ -293,16 +293,18 @@ private Object getArrayValueClassOrName(final ClassInfo annotationClassInfo, fin if (annotationMethodList != null && !annotationMethodList.isEmpty()) { if (annotationMethodList.size() > 1) { // There should only be one method with a given name in an annotation - throw new IllegalArgumentException("Duplicated annotation parameter method " + paramName - + "() in annotation class " + annotationClassInfo.getName()); + throw new IllegalArgumentException("Duplicated annotation parameter method " + paramName + "()" + + (annotationClassInfo == null ? "" + : " in annotation class " + annotationClassInfo.getName())); } // Get the result type of the method with the same name as the annotation parameter final TypeSignature annotationMethodResultTypeSig = annotationMethodList.get(0) .getTypeSignatureOrTypeDescriptor().getResultType(); // The result type has to be an array type if (!(annotationMethodResultTypeSig instanceof ArrayTypeSignature)) { - throw new IllegalArgumentException("Annotation parameter " + paramName + " in annotation class " - + annotationClassInfo.getName() + throw new IllegalArgumentException("Annotation parameter " + paramName + + (annotationClassInfo == null ? "" + : " in annotation class " + annotationClassInfo.getName()) + " holds an array, but does not have an array type signature"); } final ArrayTypeSignature arrayTypeSig = (ArrayTypeSignature) annotationMethodResultTypeSig; 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 0a52c6640..79bbb18dc 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxClassLoaderHandler.java @@ -107,8 +107,8 @@ private static void addBundleFile(final Object bundlefile, final Set pat boolean foundClassPathElement = false; for (final String fieldName : FIELD_NAMES) { final Object fieldVal = ReflectionUtils.getFieldVal(bundlefile, fieldName, false); - foundClassPathElement = fieldVal != null; - if (foundClassPathElement) { + if (fieldVal != null) { + foundClassPathElement = true; // We found the base file and a classpath element, e.g. "bin/" Object base = baseFile; String sep = "/"; @@ -128,7 +128,6 @@ private static void addBundleFile(final Object bundlefile, final Set pat break; } } - if (!foundClassPathElement) { // No classpath element found, just use basefile classpathOrderOut.addClasspathEntry(baseFile.toString(), 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 5c23581ed..16e9ca7e5 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java @@ -98,41 +98,42 @@ public static void findClasspathOrder(final ClassLoader classLoader, final Class // type WebResourceSet // {DirResourceSet, FileResourceSet, JarResourceSet, JarWarResourceSet, EmptyResourceSet} for (final Object webResourceSet : webResourceSetList) { - // 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); + 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); } - 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); + 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); + } } } } 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 0be1d675c..3ebd6e369 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java @@ -318,10 +318,6 @@ public Entry newInstance(final String nestedJarPathRaw, break; } } - if (!isDirectory) { - throw new IOException( - "Path " + childPath + " does not exist in jarfile " + parentLogicalZipFile); - } } // At this point, either isDirectory is true, or childZipEntry is non-null @@ -341,6 +337,11 @@ public Entry newInstance(final String nestedJarPathRaw, return new SimpleEntry<>(parentLogicalZipFile, childPath); } + if (childZipEntry == null /* i.e. if (!isDirectory) */) { + throw new IOException( + "Path " + childPath + " does not exist in jarfile " + parentLogicalZipFile); + } + // Do not extract nested jar, if nested jar scanning is disabled if (!scanSpec.scanNestedJars) { throw new IOException( 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 e4887bb3e..884469dd4 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/JSONDeserializer.java +++ b/src/main/java/nonapi/io/github/classgraph/json/JSONDeserializer.java @@ -324,8 +324,13 @@ private static void populateObjectFromJsonObject(final Object objectInstance, fi typeResolutions = null; mapKeyType = null; final Class objectResolvedCls = (Class) objectResolvedTypeGeneric; - arrayComponentType = isArray ? objectResolvedCls.getComponentType() : null; - is1DArray = isArray && !arrayComponentType.isArray(); + if (isArray) { + arrayComponentType = objectResolvedCls.getComponentType(); + is1DArray = !arrayComponentType.isArray(); + } else { + arrayComponentType = null; + is1DArray = false; + } commonResolvedValueType = null; } else if (objectResolvedTypeGeneric instanceof ParameterizedType) { // Get mapping from type variables to resolved types, by comparing the concrete type arguments @@ -380,20 +385,20 @@ private static void populateObjectFromJsonObject(final Object objectInstance, fi // Need to deserialize items in the same order as serialization: create all deserialized objects // at the current level in Pass 1, recording any ids that are found, then recurse into child nodes // in Pass 2 after objects at the current level have all been instantiated. - ArrayList itemsToRecurseToInPass2 = new ArrayList<>(); + ArrayList itemsToRecurseToInPass2 = null; // Pass 1: Convert JSON objects in JSONObject items into Java objects - final int numItems = isJsonObject ? jsonObject.items.size() - : isJsonArray ? jsonArray.items.size() : /* can't happen */ 0; + final int numItems = jsonObject != null ? jsonObject.items.size() + : jsonArray != null ? jsonArray.items.size() : /* can't happen */ 0; for (int i = 0; i < numItems; i++) { // Iterate through items of JSONObject or JSONArray (key is null for JSONArray) - final Entry jsonObjectItem = isJsonObject ? jsonObject.items.get(i) : null; final String itemJsonKey; final Object itemJsonValue; - if (isJsonObject) { + if (jsonObject != null) { + final Entry jsonObjectItem = jsonObject.items.get(i); itemJsonKey = jsonObjectItem.getKey(); itemJsonValue = jsonObjectItem.getValue(); - } else if (isJsonArray) { + } else if (jsonArray != null) { itemJsonKey = null; itemJsonValue = jsonArray.items.get(i); } else { @@ -408,7 +413,7 @@ private static void populateObjectFromJsonObject(final Object objectInstance, fi // If this is a standard object, look up the field info in the type cache FieldTypeInfo fieldTypeInfo; - if (isObj) { + if (classFields != null) { // Standard objects must interpret the key as a string, since field names are strings. // Look up field name directly, using the itemJsonKey string fieldTypeInfo = classFields.fieldNameToFieldTypeInfo.get(itemJsonKey); @@ -427,7 +432,7 @@ private static void populateObjectFromJsonObject(final Object objectInstance, fi // resolutions found by comparing the resolved type of the concrete containing object // with its generic type. (Fields were partially resolved before by substituting type // arguments of subclasses into type variables of superclasses.) - isObj ? fieldTypeInfo.getFullyResolvedFieldType(typeResolutions) + fieldTypeInfo != null ? fieldTypeInfo.getFullyResolvedFieldType(typeResolutions) // For arrays, the item type is the array component type : isArray ? arrayComponentType // For collections and maps, the value type is the same for all items @@ -500,8 +505,9 @@ private static void populateObjectFromJsonObject(final Object objectInstance, fi // Call the appropriate constructor for the item, whether its type is array, Collection, // Map or other class type. For collections and Maps, call the size hint constructor // for speed when adding items. - final int numSubItems = itemJsonValueIsJsonObject ? itemJsonValueJsonObject.items.size() - : itemJsonValueIsJsonArray ? itemJsonValueJsonArray.items.size() + final int numSubItems = itemJsonValueJsonObject != null + ? itemJsonValueJsonObject.items.size() + : itemJsonValueJsonArray != null ? itemJsonValueJsonArray.items.size() : /* can't happen */ 0; if ((resolvedItemValueType instanceof Class && ((Class) resolvedItemValueType).isArray())) { @@ -523,7 +529,7 @@ private static void populateObjectFromJsonObject(final Object objectInstance, fi : commonValueDefaultConstructor != null ? commonValueDefaultConstructor.newInstance() : /* can't happen */ null; - } else if (isObj) { + } else if (fieldTypeInfo != null) { // For object types, each field has its own constructor, and the constructor can // vary if the field type is completely generic (e.g. "T field"). final Constructor valueConstructorWithSizeHint = fieldTypeInfo @@ -568,9 +574,9 @@ private static void populateObjectFromJsonObject(final Object objectInstance, fi } // Add instantiated items to parent object - if (isObj) { + if (fieldTypeInfo != null) { fieldTypeInfo.setFieldValue(objectInstance, instantiatedItemObject); - } else if (isMap) { + } else if (mapInstance != null) { // For maps, key type should be deserialized from strings, to support e.g. Integer as a key type. // This only works for basic object types though (String, Integer, Enum, etc.) final Object mapKey = jsonBasicValueToObject(itemJsonKey, mapKeyType, @@ -578,7 +584,7 @@ private static void populateObjectFromJsonObject(final Object objectInstance, fi mapInstance.put(mapKey, instantiatedItemObject); } else if (isArray) { Array.set(objectInstance, i, instantiatedItemObject); - } else if (isCollection) { + } else if (collectionInstance != null) { // Can't add partially-deserialized item objects to Collections yet, since their // hashCode() and equals() methods may depend upon fields that have not yet been set. collectionElementAdders.add(new Runnable() { 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 9ba140c02..666cbc790 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/JSONSerializer.java +++ b/src/main/java/nonapi/io/github/classgraph/json/JSONSerializer.java @@ -280,6 +280,7 @@ private static Object toJSONGraph(final Object obj, final Set cls = obj.getClass(); + final boolean isArray = cls.isArray(); if (Map.class.isAssignableFrom(cls)) { final Map map = (Map) obj; @@ -302,12 +303,12 @@ private static Object toJSONGraph(final Object obj, final Set list = isList ? (List) obj : null; - final int n = isList ? list.size() : Array.getLength(obj); + final int n = list != null ? list.size() : isArray ? Array.getLength(obj) : 0; // Convert list items to JSON values final Object[] convertedVals = new Object[n]; for (int i = 0; i < n; i++) { - convertedVals[i] = isList ? list.get(i) : Array.get(obj, i); + convertedVals[i] = list != null ? list.get(i) : isArray ? Array.get(obj, i) : 0; } convertVals(convertedVals, visitedOnPath, standardObjectVisited, classFieldCache, objToJSONVal, onlySerializePublicFields); diff --git a/src/main/java/nonapi/io/github/classgraph/json/ReferenceEqualityKey.java b/src/main/java/nonapi/io/github/classgraph/json/ReferenceEqualityKey.java index 28d21f714..146b68108 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/ReferenceEqualityKey.java +++ b/src/main/java/nonapi/io/github/classgraph/json/ReferenceEqualityKey.java @@ -55,7 +55,7 @@ public ReferenceEqualityKey(final K wrappedKey) { */ @Override public int hashCode() { - return wrappedKey.hashCode(); + return wrappedKey == null ? 0 : wrappedKey.hashCode(); } /* (non-Javadoc) @@ -76,6 +76,6 @@ public boolean equals(final Object obj) { */ @Override public String toString() { - return wrappedKey.toString(); + return wrappedKey == null ? "null" : wrappedKey.toString(); } } \ 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 6296ee2c0..946bd675d 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -457,7 +457,8 @@ public static boolean canReadAndIsFile(final File file) { * * @param file * A {@link File}. - * @throws IOException if the file does not exist, is not a regular file, or cannot be read. + * @throws IOException + * if the file does not exist, is not a regular file, or cannot be read. */ public static void checkCanReadAndIsFile(final File file) throws IOException { try { @@ -495,7 +496,8 @@ public static boolean canReadAndIsDir(final File file) { * * @param file * A {@link File}. - * @throws IOException if the file does not exist, is not a directory, or cannot be read. + * @throws IOException + * if the file does not exist, is not a directory, or cannot be read. */ public static void checkCanReadAndIsDir(final File file) throws IOException { try { diff --git a/src/main/java/nonapi/io/github/classgraph/utils/Join.java b/src/main/java/nonapi/io/github/classgraph/utils/Join.java index 8e3583797..907c1d4b6 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/Join.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/Join.java @@ -64,7 +64,11 @@ public static void join(final StringBuilder buf, final String addAtBeginning, fi } else { buf.append(sep); } - buf.append(item.toString()); + if (item == null) { + buf.append("null"); + } else { + buf.append(item.toString()); + } } if (!addAtEnd.isEmpty()) { buf.append(addAtEnd); diff --git a/src/test/java/io/github/classgraph/issues/issue223/Issue223Test.java b/src/test/java/io/github/classgraph/issues/issue223/Issue223Test.java index 354da2f22..22c2a9b85 100644 --- a/src/test/java/io/github/classgraph/issues/issue223/Issue223Test.java +++ b/src/test/java/io/github/classgraph/issues/issue223/Issue223Test.java @@ -72,10 +72,12 @@ public boolean accept(final ClassInfo ci) { } } assertThat(innerInterface).isNotNull(); - assertThat(innerInterface.getName()).isEqualTo(InnerInterface.class.getName()); - assertThat(innerInterface.isInterface()).isTrue(); - final Class innerClassRef = innerInterface.loadClass(); - assertThat(innerClassRef).isNotNull(); + if (innerInterface != null) { + assertThat(innerInterface.getName()).isEqualTo(InnerInterface.class.getName()); + assertThat(innerInterface.isInterface()).isTrue(); + final Class innerClassRef = innerInterface.loadClass(); + assertThat(innerClassRef).isNotNull(); + } } } } From 05bca0773a33b643aa067d9114018dd51d8d96f8 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 17 Oct 2019 16:34:28 -0600 Subject: [PATCH 0380/1778] [maven-release-plugin] prepare release classgraph-4.8.50 --- pom.xml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index e50130e5b..6e92a36c7 100644 --- a/pom.xml +++ b/pom.xml @@ -1,12 +1,9 @@ - + 4.0.0 io.github.classgraph classgraph - 4.8.50-SNAPSHOT + 4.8.50 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 - HEAD + classgraph-4.8.50 @@ -234,8 +231,7 @@ - + org.codehaus.mojo.signature From b0f20c814e4a8fa2fc3f98b9179e3cc891dd9136 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 17 Oct 2019 16:34:34 -0600 Subject: [PATCH 0381/1778] [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 6e92a36c7..f67ff82de 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.50 + 4.8.51-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.50 + HEAD From 2c369f3996bd3ba3b18754face4c9f9d1f0503b0 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 17 Oct 2019 16:44:25 -0600 Subject: [PATCH 0382/1778] containsExactlyInAnyOrder -> containsOnly (to fix Scrutinizer build) --- src/test/java/com/xyz/MetaAnnotationTest.java | 55 +++++++++---------- .../features/DeclaredVsNonDeclaredTest.java | 21 ++++--- .../github/classgraph/issues/IssuesTest.java | 10 ++-- .../issues/issue100/Issue100Test.java | 4 +- .../issues/issue101/Issue101Test.java | 6 +- .../issues/issue107/Issue107Test.java | 2 +- .../issues/issue128/Issue128Test.java | 3 +- .../issues/issue153/Issue153Test.java | 2 +- .../issues/issue166/Issue166Test.java | 2 +- .../issues/issue175/Issue175Test.java | 18 +++--- .../issues/issue209/Issue209Test.java | 2 +- .../issues/issue216/Issue216Test.java | 2 +- .../issues/issue245/Issue245Test.java | 2 +- .../issues/issue261/Issue261Test.java | 2 +- .../classgraph/issues/issue329/Issue329.java | 4 +- .../classgraph/issues/issue340/Issue340.java | 4 +- .../classgraph/issues/issue350/Issue350.java | 8 +-- .../issues/issue364/Issue364Test.java | 10 ++-- .../issues/issue38/Issue38Test.java | 2 +- .../issues/issue46/Issue46Test.java | 2 +- .../issues/issue74/Issue74Test.java | 6 +- .../issues/issue78/Issue78Test.java | 3 +- .../classgraph/issues/issue93/Issue93.java | 6 +- .../issues/issue99/Issue99Test.java | 2 +- .../classgraph/test/ClassGraphTest.java | 2 +- .../github/classgraph/test/ClassInfoTest.java | 14 ++--- .../FieldAndMethodAnnotationTest.java | 4 +- .../test/fieldinfo/FieldInfoTest.java | 4 +- .../test/internal/InternalExternalTest.java | 18 +++--- .../MethodAnnotationTest.java | 2 +- .../TestMethodMetaAnnotation.java | 8 +-- .../test/methodinfo/MethodInfoTest.java | 6 +- 32 files changed, 116 insertions(+), 120 deletions(-) diff --git a/src/test/java/com/xyz/MetaAnnotationTest.java b/src/test/java/com/xyz/MetaAnnotationTest.java index a1dfdf74d..f7bb8589a 100644 --- a/src/test/java/com/xyz/MetaAnnotationTest.java +++ b/src/test/java/com/xyz/MetaAnnotationTest.java @@ -68,11 +68,11 @@ static void tearDown() { @Test void oneLevel() { assertThat(scanResult.getClassesWithAnnotation("com.xyz.meta.E").directOnly().getNames()) - .containsExactlyInAnyOrder("com.xyz.meta.B"); + .containsOnly("com.xyz.meta.B"); assertThat(scanResult.getClassesWithAnnotation("com.xyz.meta.F").directOnly().getNames()) - .containsExactlyInAnyOrder("com.xyz.meta.B", "com.xyz.meta.A"); + .containsOnly("com.xyz.meta.B", "com.xyz.meta.A"); assertThat(scanResult.getClassesWithAnnotation("com.xyz.meta.G").directOnly().getNames()) - .containsExactlyInAnyOrder("com.xyz.meta.C"); + .containsOnly("com.xyz.meta.C"); } /** @@ -80,8 +80,8 @@ void oneLevel() { */ @Test void twoLevels() { - assertThat(scanResult.getClassesWithAnnotation("com.xyz.meta.J").getNames()) - .containsExactlyInAnyOrder("com.xyz.meta.F", "com.xyz.meta.E", "com.xyz.meta.B", "com.xyz.meta.A"); + assertThat(scanResult.getClassesWithAnnotation("com.xyz.meta.J").getNames()).containsOnly("com.xyz.meta.F", + "com.xyz.meta.E", "com.xyz.meta.B", "com.xyz.meta.A"); } /** @@ -89,8 +89,8 @@ void twoLevels() { */ @Test void threeLevels() { - assertThat(scanResult.getClassesWithAnnotation("com.xyz.meta.L").getNames()) - .containsExactlyInAnyOrder("com.xyz.meta.I", "com.xyz.meta.E", "com.xyz.meta.B", "com.xyz.meta.H"); + assertThat(scanResult.getClassesWithAnnotation("com.xyz.meta.L").getNames()).containsOnly("com.xyz.meta.I", + "com.xyz.meta.E", "com.xyz.meta.B", "com.xyz.meta.H"); } /** @@ -99,19 +99,19 @@ void threeLevels() { @Test void acrossCycle() { assertThat(scanResult.getClassesWithAnnotation("com.xyz.meta.H").directOnly().getNames()) - .containsExactlyInAnyOrder("com.xyz.meta.I"); - assertThat(scanResult.getAnnotationsOnClass("com.xyz.meta.H").directOnly().getNames()) - .containsExactlyInAnyOrder("com.xyz.meta.I", "com.xyz.meta.K", "java.lang.annotation.Retention", - "java.lang.annotation.Target"); + .containsOnly("com.xyz.meta.I"); + assertThat(scanResult.getAnnotationsOnClass("com.xyz.meta.H").directOnly().getNames()).containsOnly( + "com.xyz.meta.I", "com.xyz.meta.K", "java.lang.annotation.Retention", + "java.lang.annotation.Target"); assertThat(scanResult.getClassesWithAnnotation("com.xyz.meta.I").directOnly().getNames()) - .containsExactlyInAnyOrder("com.xyz.meta.E", "com.xyz.meta.H"); - assertThat(scanResult.getAnnotationsOnClass("com.xyz.meta.I").directOnly().getNames()) - .containsExactlyInAnyOrder("com.xyz.meta.L", "com.xyz.meta.H", "java.lang.annotation.Retention", - "java.lang.annotation.Target"); + .containsOnly("com.xyz.meta.E", "com.xyz.meta.H"); + assertThat(scanResult.getAnnotationsOnClass("com.xyz.meta.I").directOnly().getNames()).containsOnly( + "com.xyz.meta.L", "com.xyz.meta.H", "java.lang.annotation.Retention", + "java.lang.annotation.Target"); assertThat(scanResult.getClassesWithAnnotation("com.xyz.meta.K").directOnly().getNames()) - .containsExactlyInAnyOrder("com.xyz.meta.H"); + .containsOnly("com.xyz.meta.H"); assertThat(scanResult.getClassesWithAnnotation("com.xyz.meta.D").directOnly().getNames()) - .containsExactlyInAnyOrder("com.xyz.meta.K"); + .containsOnly("com.xyz.meta.K"); } /** @@ -119,8 +119,8 @@ void acrossCycle() { */ @Test void cycleAnnotatesSelf() { - assertThat(scanResult.getClassesWithAnnotation("com.xyz.meta.I").getNames()) - .containsExactlyInAnyOrder("com.xyz.meta.E", "com.xyz.meta.B", "com.xyz.meta.H", "com.xyz.meta.I"); + assertThat(scanResult.getClassesWithAnnotation("com.xyz.meta.I").getNames()).containsOnly("com.xyz.meta.E", + "com.xyz.meta.B", "com.xyz.meta.H", "com.xyz.meta.I"); } /** @@ -128,10 +128,9 @@ void cycleAnnotatesSelf() { */ @Test void namesOfMetaAnnotations() { - assertThat(scanResult.getAnnotationsOnClass("com.xyz.meta.A").getNames()) - .containsExactlyInAnyOrder("com.xyz.meta.J", "com.xyz.meta.F"); - assertThat(scanResult.getAnnotationsOnClass("com.xyz.meta.C").getNames()) - .containsExactlyInAnyOrder("com.xyz.meta.G"); + assertThat(scanResult.getAnnotationsOnClass("com.xyz.meta.A").getNames()).containsOnly("com.xyz.meta.J", + "com.xyz.meta.F"); + assertThat(scanResult.getAnnotationsOnClass("com.xyz.meta.C").getNames()).containsOnly("com.xyz.meta.G"); } /** @@ -141,14 +140,14 @@ void namesOfMetaAnnotations() { void union() { assertThat(scanResult.getClassesWithAnnotation("com.xyz.meta.J") .union(scanResult.getClassesWithAnnotation("com.xyz.meta.G")).directOnly().getNames()) - .containsExactlyInAnyOrder("com.xyz.meta.E", "com.xyz.meta.F", "com.xyz.meta.C"); + .containsOnly("com.xyz.meta.E", "com.xyz.meta.F", "com.xyz.meta.C"); assertThat(scanResult.getClassesWithAnnotation("com.xyz.meta.I") - .union(scanResult.getClassesWithAnnotation("com.xyz.meta.J")).getNames()).containsExactlyInAnyOrder( + .union(scanResult.getClassesWithAnnotation("com.xyz.meta.J")).getNames()).containsOnly( "com.xyz.meta.A", "com.xyz.meta.B", "com.xyz.meta.F", "com.xyz.meta.E", "com.xyz.meta.H", "com.xyz.meta.I"); assertThat(scanResult.getClassesWithAnnotation("com.xyz.meta.I") .union(scanResult.getClassesWithAnnotation("com.xyz.meta.J")).directOnly().getNames()) - .containsExactlyInAnyOrder("com.xyz.meta.F", "com.xyz.meta.E", "com.xyz.meta.H"); + .containsOnly("com.xyz.meta.F", "com.xyz.meta.E", "com.xyz.meta.H"); } /** @@ -158,9 +157,9 @@ void union() { void intersect() { assertThat(scanResult.getClassesWithAnnotation("com.xyz.meta.I") .intersect(scanResult.getClassesWithAnnotation("com.xyz.meta.J")).getNames()) - .containsExactlyInAnyOrder("com.xyz.meta.E", "com.xyz.meta.B"); + .containsOnly("com.xyz.meta.E", "com.xyz.meta.B"); assertThat(scanResult.getClassesWithAnnotation("com.xyz.meta.I") .intersect(scanResult.getClassesWithAnnotation("com.xyz.meta.J")).directOnly().getNames()) - .containsExactlyInAnyOrder("com.xyz.meta.E"); + .containsOnly("com.xyz.meta.E"); } } diff --git a/src/test/java/io/github/classgraph/features/DeclaredVsNonDeclaredTest.java b/src/test/java/io/github/classgraph/features/DeclaredVsNonDeclaredTest.java index 116b2b71e..83900464f 100644 --- a/src/test/java/io/github/classgraph/features/DeclaredVsNonDeclaredTest.java +++ b/src/test/java/io/github/classgraph/features/DeclaredVsNonDeclaredTest.java @@ -148,20 +148,19 @@ public void annotationInfosShouldBeAbleToDifferentiateBetweenDirectAndReachable( final AnnotationInfoList annotationInfossOnA = A.getAnnotationInfo(); final AnnotationInfoList annotationsInfosOnB = B.getAnnotationInfo(); - assertThat(annotationInfossOnA).extracting(AnnotationInfo::getName).containsExactlyInAnyOrder( + assertThat(annotationInfossOnA).extracting(AnnotationInfo::getName).containsOnly( InheritedAnnotation.class.getName(), NormalAnnotation.class.getName(), InheritedMetaAnnotation.class.getName(), NonInheritedMetaAnnotation.class.getName()); - assertThat(annotationsInfosOnB).extracting(AnnotationInfo::getName).containsExactlyInAnyOrder( - InheritedAnnotation.class.getName(), InheritedMetaAnnotation.class.getName()); + assertThat(annotationsInfosOnB).extracting(AnnotationInfo::getName) + .containsOnly(InheritedAnnotation.class.getName(), InheritedMetaAnnotation.class.getName()); assertThat(annotationInfossOnA.directOnly()).extracting(AnnotationInfo::getName) - .containsExactlyInAnyOrder(NormalAnnotation.class.getName(), - InheritedAnnotation.class.getName()); + .containsOnly(NormalAnnotation.class.getName(), InheritedAnnotation.class.getName()); assertThat(annotationsInfosOnB.directOnly()).isEmpty(); assertThat(C.getAnnotationInfo().directOnly()).extracting(AnnotationInfo::getName) - .containsExactlyInAnyOrder(NormalAnnotation.class.getName()); + .containsOnly(NormalAnnotation.class.getName()); final AnnotationInfoList annotationsOnAw = A.getMethodInfo().getSingleMethod("w").getAnnotationInfo(); - assertThat(annotationsOnAw).extracting(AnnotationInfo::getName).containsExactlyInAnyOrder( + assertThat(annotationsOnAw).extracting(AnnotationInfo::getName).containsOnly( InheritedAnnotation.class.getName(), InheritedMetaAnnotation.class.getName(), NonInheritedMetaAnnotation.class.getName()); @@ -189,11 +188,11 @@ public void annotationsShouldBeAbleToDifferentiateBetweenDirectAndReachable() { final ClassInfoList annotationsOnA = A.getAnnotations(); final ClassInfoList annotationsOnB = B.getAnnotations(); - assertThat(annotationsOnA.loadClasses()).containsExactlyInAnyOrder(NormalAnnotation.class, - InheritedAnnotation.class, InheritedMetaAnnotation.class, NonInheritedMetaAnnotation.class); - assertThat(annotationsOnB.loadClasses()).containsExactlyInAnyOrder(InheritedAnnotation.class, + assertThat(annotationsOnA.loadClasses()).containsOnly(NormalAnnotation.class, InheritedAnnotation.class, + InheritedMetaAnnotation.class, NonInheritedMetaAnnotation.class); + assertThat(annotationsOnB.loadClasses()).containsOnly(InheritedAnnotation.class, InheritedMetaAnnotation.class); - assertThat(annotationsOnA.directOnly().loadClasses()).containsExactlyInAnyOrder(NormalAnnotation.class, + assertThat(annotationsOnA.directOnly().loadClasses()).containsOnly(NormalAnnotation.class, InheritedAnnotation.class); assertThat(annotationsOnB.directOnly()).isEmpty(); } diff --git a/src/test/java/io/github/classgraph/issues/IssuesTest.java b/src/test/java/io/github/classgraph/issues/IssuesTest.java index ff92c32de..2db103cf3 100644 --- a/src/test/java/io/github/classgraph/issues/IssuesTest.java +++ b/src/test/java/io/github/classgraph/issues/IssuesTest.java @@ -35,7 +35,7 @@ public void issue70EnableExternalClasses() { .enableExternalClasses().scan()) { assertThat(scanResult.getSubclasses(Object.class.getName()).getNames()).contains(Impl1.class.getName()); assertThat(scanResult.getSuperclasses(Impl1Sub.class.getName()).getNames()) - .containsExactlyInAnyOrder(Impl1.class.getName()); + .containsOnly(Impl1.class.getName()); } } @@ -47,7 +47,7 @@ public void extendsExternal() { try (ScanResult scanResult = new ClassGraph() .whitelistPackages(InternalExtendsExternal.class.getPackage().getName()).scan()) { assertThat(scanResult.getSuperclasses(InternalExtendsExternal.class.getName()).getNames()) - .containsExactlyInAnyOrder(ExternalSuperclass.class.getName()); + .containsOnly(ExternalSuperclass.class.getName()); } } @@ -60,7 +60,7 @@ public void extendsExternalWithEnableExternal() { .whitelistPackages(InternalExtendsExternal.class.getPackage().getName()).enableExternalClasses() .scan()) { assertThat(scanResult.getSuperclasses(InternalExtendsExternal.class.getName()).getNames()) - .containsExactlyInAnyOrder(ExternalSuperclass.class.getName()); + .containsOnly(ExternalSuperclass.class.getName()); } } @@ -72,7 +72,7 @@ public void extendsExternalSubclass() { try (ScanResult scanResult = new ClassGraph() .whitelistPackages(InternalExtendsExternal.class.getPackage().getName()).scan()) { assertThat(scanResult.getSubclasses(ExternalSuperclass.class.getName()).getNames()) - .containsExactlyInAnyOrder(InternalExtendsExternal.class.getName()); + .containsOnly(InternalExtendsExternal.class.getName()); } } @@ -85,7 +85,7 @@ public void nonStrictExtendsExternalSubclass() { .whitelistPackages(InternalExtendsExternal.class.getPackage().getName()).enableExternalClasses() .scan()) { assertThat(scanResult.getSubclasses(ExternalSuperclass.class.getName()).getNames()) - .containsExactlyInAnyOrder(InternalExtendsExternal.class.getName()); + .containsOnly(InternalExtendsExternal.class.getName()); } } } diff --git a/src/test/java/io/github/classgraph/issues/issue100/Issue100Test.java b/src/test/java/io/github/classgraph/issues/issue100/Issue100Test.java index c324fd0ea..a8886c079 100644 --- a/src/test/java/io/github/classgraph/issues/issue100/Issue100Test.java +++ b/src/test/java/io/github/classgraph/issues/issue100/Issue100Test.java @@ -68,7 +68,7 @@ public void issue100Test() { } } } - assertThat(fieldNames1).containsExactlyInAnyOrder("a"); + assertThat(fieldNames1).containsOnly("a"); // However, if "...b.jar" is specifically whitelisted, "...a.jar" should not be visible. Originally, the // version of the class in "...a.jar" was supposed to mask the same class in "...b.jar" (#100). However, @@ -84,6 +84,6 @@ public void issue100Test() { } } } - assertThat(fieldNames2).containsExactlyInAnyOrder("b"); + assertThat(fieldNames2).containsOnly("b"); } } 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 44bdbc1b4..34caca80f 100644 --- a/src/test/java/io/github/classgraph/issues/issue101/Issue101Test.java +++ b/src/test/java/io/github/classgraph/issues/issue101/Issue101Test.java @@ -47,7 +47,7 @@ public void nonInheritedAnnotation() { try (ScanResult scanResult = new ClassGraph().whitelistPackages(Issue101Test.class.getPackage().getName()) .enableAllInfo().scan()) { assertThat(scanResult.getClassesWithAnnotation(NonInheritedAnnotation.class.getName()).getNames()) - .containsExactlyInAnyOrder(AnnotatedClass.class.getName()); + .containsOnly(AnnotatedClass.class.getName()); } } @@ -59,7 +59,7 @@ public void inheritedMetaAnnotation() { try (ScanResult scanResult = new ClassGraph().whitelistPackages(Issue101Test.class.getPackage().getName()) .enableAllInfo().scan()) { assertThat(scanResult.getClassesWithAnnotation(InheritedMetaAnnotation.class.getName()) - .getStandardClasses().getNames()).containsExactlyInAnyOrder(AnnotatedClass.class.getName(), + .getStandardClasses().getNames()).containsOnly(AnnotatedClass.class.getName(), NonAnnotatedSubclass.class.getName()); } } @@ -72,7 +72,7 @@ public void inheritedAnnotation() { try (ScanResult scanResult = new ClassGraph().whitelistPackages(Issue101Test.class.getPackage().getName()) .enableAllInfo().scan()) { assertThat(scanResult.getClassesWithAnnotation(InheritedAnnotation.class.getName()).getNames()) - .containsExactlyInAnyOrder(AnnotatedClass.class.getName(), NonAnnotatedSubclass.class.getName(), + .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 c56388451..bd3129994 100644 --- a/src/test/java/io/github/classgraph/issues/issue107/Issue107Test.java +++ b/src/test/java/io/github/classgraph/issues/issue107/Issue107Test.java @@ -56,7 +56,7 @@ public void issue107Test() { assertThat(scanResult.getPackageInfo().getNames()).containsAll(Arrays.asList("", "io", "io.github", "io.github.classgraph", Issue107Test.class.getPackage().getName())); assertThat(scanResult.getPackageInfo(Issue107Test.class.getPackage().getName()).getAnnotationInfo() - .getNames()).containsExactlyInAnyOrder(PackageAnnotation.class.getName()); + .getNames()).containsOnly(PackageAnnotation.class.getName()); } } } diff --git a/src/test/java/io/github/classgraph/issues/issue128/Issue128Test.java b/src/test/java/io/github/classgraph/issues/issue128/Issue128Test.java index 71e034115..0c24f17be 100644 --- a/src/test/java/io/github/classgraph/issues/issue128/Issue128Test.java +++ b/src/test/java/io/github/classgraph/issues/issue128/Issue128Test.java @@ -79,8 +79,7 @@ public void issue128Test() throws Exception { + Issue128Test.class.getName() + ": " + e); } } else { - assertThat(filesInsideLevel3).containsExactlyInAnyOrder("com/test/Test.java", - "com/test/Test.class"); + assertThat(filesInsideLevel3).containsOnly("com/test/Test.java", "com/test/Test.class"); } } } diff --git a/src/test/java/io/github/classgraph/issues/issue153/Issue153Test.java b/src/test/java/io/github/classgraph/issues/issue153/Issue153Test.java index e8b5d4a89..89e2be04f 100644 --- a/src/test/java/io/github/classgraph/issues/issue153/Issue153Test.java +++ b/src/test/java/io/github/classgraph/issues/issue153/Issue153Test.java @@ -218,7 +218,7 @@ public void classAnnotationParameters() { // Read class annotation parameters assertThat(classInfo.getAnnotationInfo().getAsStrings()) // - .containsExactlyInAnyOrder("@" + StringAnnotation.class.getName() + "(\"classlabel\")", // + .containsOnly("@" + StringAnnotation.class.getName() + "(\"classlabel\")", // "@" + TwoParamAnnotation.class.getName() + "(value1='x', value2={1, 2, 3})", // "@" + EnumAnnotation.class.getName() + "(" + FruitEnum.class.getName() + ".BANANA" + ")", // diff --git a/src/test/java/io/github/classgraph/issues/issue166/Issue166Test.java b/src/test/java/io/github/classgraph/issues/issue166/Issue166Test.java index eb49d80f6..5a78d935e 100644 --- a/src/test/java/io/github/classgraph/issues/issue166/Issue166Test.java +++ b/src/test/java/io/github/classgraph/issues/issue166/Issue166Test.java @@ -48,7 +48,7 @@ public class Issue166Test { public void issue166Test() { final URL jarURL = Issue166Test.class.getClassLoader().getResource("issue166-jar-without-extension"); try (ScanResult scanResult = new ClassGraph().overrideClasspath(jarURL).scan()) { - assertThat(scanResult.getAllResources().getPaths()).containsExactlyInAnyOrder("Issue166.txt"); + assertThat(scanResult.getAllResources().getPaths()).containsOnly("Issue166.txt"); } } 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 a019d938d..f960293ff 100644 --- a/src/test/java/io/github/classgraph/issues/issue175/Issue175Test.java +++ b/src/test/java/io/github/classgraph/issues/issue175/Issue175Test.java @@ -66,7 +66,7 @@ public void testSynthetic() { methods.add(method.toString()); } } - assertThat(methods).containsExactlyInAnyOrder( // + assertThat(methods).containsOnly( // "protected (synthetic java.lang.String $enum$name, synthetic int $enum$ordinal)", "public static net.corda.core.contracts.ComponentGroupEnum[] values()", "public static net.corda.core.contracts.ComponentGroupEnum valueOf(java.lang.String)"); @@ -92,7 +92,7 @@ public void testMandated() { methods.add(method.toString()); } } - assertThat(methods).containsExactlyInAnyOrder( + assertThat(methods).containsOnly( "@org.jetbrains.annotations.NotNull public static final rx.Observable toObservable(@org.jetbrains.annotations.NotNull mandated net.corda.core.concurrent.CordaFuture $receiver)", "@org.jetbrains.annotations.NotNull public static final net.corda.core.concurrent.CordaFuture toFuture(@org.jetbrains.annotations.NotNull mandated rx.Observable $receiver)"); } @@ -117,7 +117,7 @@ public void testMismatchedTypes() { methods.add(method.toString()); } } - assertThat(methods).containsExactlyInAnyOrder( + assertThat(methods).containsOnly( "public static final W match(@org.jetbrains.annotations.NotNull mandated java.util.concurrent.Future $receiver, @org.jetbrains.annotations.NotNull kotlin.jvm.functions.Function1 success, @org.jetbrains.annotations.NotNull kotlin.jvm.functions.Function1 failure)", "@org.jetbrains.annotations.NotNull public static final net.corda.core.concurrent.CordaFuture firstOf(@org.jetbrains.annotations.NotNull net.corda.core.concurrent.CordaFuture[] futures, @org.jetbrains.annotations.NotNull kotlin.jvm.functions.Function1, ? extends W> handler)", "public static synthetic void shortCircuitedTaskFailedMessage$annotations()", @@ -144,7 +144,7 @@ public void testResultTypesNotReconciled1() { methods.add(method.toString()); } } - assertThat(methods).containsExactlyInAnyOrder("private final java.lang.String commandDataToString()", + assertThat(methods).containsOnly("private final java.lang.String commandDataToString()", "@org.jetbrains.annotations.NotNull public java.lang.String toString()", "@org.jetbrains.annotations.NotNull public final T getValue()", "@org.jetbrains.annotations.NotNull public final java.util.List getSigners()", @@ -178,7 +178,7 @@ public void testResultTypesNotReconciled2() { methods.add(method.toString()); } } - assertThat(methods).containsExactlyInAnyOrder("public final int getNextNodeId()", + 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()", @@ -240,7 +240,7 @@ public void testAttributeParameterMismatch() { methods.add(method.toString()); } } - assertThat(methods).containsExactlyInAnyOrder( // + 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)", @@ -267,7 +267,7 @@ public void testResultTypeReconciliationIssue() { methods.add(method.toString()); } } - assertThat(methods).containsExactlyInAnyOrder( + assertThat(methods).containsOnly( "@org.jetbrains.annotations.NotNull protected final com.google.common.collect.Multimap getMethodMap()", "@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)", @@ -303,7 +303,7 @@ public void testParameterArityMismatch() { methods.add(method.toString()); } } - assertThat(methods).containsExactlyInAnyOrder( + 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)"); } } @@ -327,7 +327,7 @@ public void testBareTypeIssue() { methods.add(method.toString()); } } - assertThat(methods).containsExactlyInAnyOrder( + assertThat(methods).containsOnly( "@org.jetbrains.annotations.Nullable public final java.lang.Object invoke()", "@org.jetbrains.annotations.Nullable public java.lang.Object call()", "@org.jetbrains.annotations.NotNull public final java.lang.reflect.Method getMethod()", diff --git a/src/test/java/io/github/classgraph/issues/issue209/Issue209Test.java b/src/test/java/io/github/classgraph/issues/issue209/Issue209Test.java index 744e822e2..2862042c1 100644 --- a/src/test/java/io/github/classgraph/issues/issue209/Issue209Test.java +++ b/src/test/java/io/github/classgraph/issues/issue209/Issue209Test.java @@ -52,7 +52,7 @@ public void testSpringBootJarWithLibJars() { .overrideClassLoaders(new URLClassLoader( new URL[] { Issue209Test.class.getClassLoader().getResource("issue209.jar") })) // .scan()) { - assertThat(result.getAllClasses().getNames()).containsExactlyInAnyOrder( + assertThat(result.getAllClasses().getNames()).containsOnly( // Test reading from / "org.springframework.boot.loader.util.SystemPropertyUtils", // Test reading from /BOOT-INF/classes 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 6d90441c6..5567c7ee1 100644 --- a/src/test/java/io/github/classgraph/issues/issue216/Issue216Test.java +++ b/src/test/java/io/github/classgraph/issues/issue216/Issue216Test.java @@ -56,7 +56,7 @@ public void testSpringBootJarWithLibJars() { public boolean accept(final ClassInfo ci) { return ci.hasAnnotation(Entity.class.getName()); } - }).getNames()).containsExactlyInAnyOrder(Issue216Test.class.getName()); + }).getNames()).containsOnly(Issue216Test.class.getName()); } } } diff --git a/src/test/java/io/github/classgraph/issues/issue245/Issue245Test.java b/src/test/java/io/github/classgraph/issues/issue245/Issue245Test.java index 29067e010..69edc1644 100644 --- a/src/test/java/io/github/classgraph/issues/issue245/Issue245Test.java +++ b/src/test/java/io/github/classgraph/issues/issue245/Issue245Test.java @@ -55,7 +55,7 @@ public void testCustomPackageRoot() { .whitelistPaths("org.springframework/gs-spring-boot") // .disableNestedJarScanning() // .scan()) { - assertThat(scanResult.getAllResources().getPaths()).containsExactlyInAnyOrder( + assertThat(scanResult.getAllResources().getPaths()).containsOnly( "org.springframework/gs-spring-boot/pom.xml", "org.springframework/gs-spring-boot/pom.properties"); } 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 a5cf8c23d..ea6ff8596 100644 --- a/src/test/java/io/github/classgraph/issues/issue261/Issue261Test.java +++ b/src/test/java/io/github/classgraph/issues/issue261/Issue261Test.java @@ -66,7 +66,7 @@ public void issue261Test() { try (ScanResult scanResult = new ClassGraph().whitelistClasses(Cls.class.getName()).enableAllInfo() .scan()) { assertThat(scanResult.getSubclasses(SuperSuperCls.class.getName()).getNames()) - .containsExactlyInAnyOrder(SuperCls.class.getName(), Cls.class.getName()); + .containsOnly(SuperCls.class.getName(), Cls.class.getName()); } } } diff --git a/src/test/java/io/github/classgraph/issues/issue329/Issue329.java b/src/test/java/io/github/classgraph/issues/issue329/Issue329.java index 97e790db0..4c3314d7a 100644 --- a/src/test/java/io/github/classgraph/issues/issue329/Issue329.java +++ b/src/test/java/io/github/classgraph/issues/issue329/Issue329.java @@ -30,8 +30,8 @@ public void test() { try (ScanResult scanResult = new ClassGraph().enableAllInfo().enableInterClassDependencies() .enableExternalClasses().whitelistClasses(Foo.class.getName()).scan()) { final ClassInfo classInfo = scanResult.getClassInfo(Foo.class.getName()); - assertThat(classInfo.getClassDependencies().getNames()) - .containsExactlyInAnyOrder(Issue329.class.getName(), Bar.class.getName()); + assertThat(classInfo.getClassDependencies().getNames()).containsOnly(Issue329.class.getName(), + Bar.class.getName()); } } } diff --git a/src/test/java/io/github/classgraph/issues/issue340/Issue340.java b/src/test/java/io/github/classgraph/issues/issue340/Issue340.java index c4642613c..b22a35c36 100644 --- a/src/test/java/io/github/classgraph/issues/issue340/Issue340.java +++ b/src/test/java/io/github/classgraph/issues/issue340/Issue340.java @@ -34,8 +34,8 @@ public void test() { // jar2 and jar4 also have an invalid Class-Path entry that tries to escape the parent jar root. // jar1 and jar2 are deflated, jar3 and jar4 are stored. assertThat(scanResult.getAllResources().stream().map(Resource::getPath) - .filter(path -> path.startsWith("file")).collect(Collectors.toList())) - .containsExactlyInAnyOrder("file1", "file2", "file3", "file4"); + .filter(path -> path.startsWith("file")).collect(Collectors.toList())).containsOnly("file1", + "file2", "file3", "file4"); } } } 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 a9554527a..8e1118699 100644 --- a/src/test/java/io/github/classgraph/issues/issue350/Issue350.java +++ b/src/test/java/io/github/classgraph/issues/issue350/Issue350.java @@ -57,17 +57,17 @@ public void test() { try (ScanResult scanResult = new ClassGraph().whitelistPackages(Issue350.class.getPackage().getName()) .enableClassInfo().enableFieldInfo().enableMethodInfo().enableAnnotationInfo().scan()) { assertThat(scanResult.getClassesWithFieldAnnotation(SuperclassAnnotation.class.getName()).getNames()) - .containsExactlyInAnyOrder(Pub.class.getName(), PubSub.class.getName()); + .containsOnly(Pub.class.getName(), PubSub.class.getName()); assertThat(scanResult.getClassesWithMethodAnnotation(SuperclassAnnotation.class.getName()).getNames()) - .containsExactlyInAnyOrder(Pub.class.getName(), PubSub.class.getName()); + .containsOnly(Pub.class.getName(), PubSub.class.getName()); } try (ScanResult scanResult = new ClassGraph().whitelistPackages(Issue350.class.getPackage().getName()) .enableClassInfo().enableFieldInfo().enableMethodInfo().enableAnnotationInfo() .ignoreFieldVisibility().ignoreMethodVisibility().scan()) { assertThat(scanResult.getClassesWithFieldAnnotation(SuperclassAnnotation.class.getName()).getNames()) - .containsExactlyInAnyOrder(Pub.class.getName(), PubSub.class.getName(), Priv.class.getName()); + .containsOnly(Pub.class.getName(), PubSub.class.getName(), Priv.class.getName()); assertThat(scanResult.getClassesWithMethodAnnotation(SuperclassAnnotation.class.getName()).getNames()) - .containsExactlyInAnyOrder(Pub.class.getName(), PubSub.class.getName(), Priv.class.getName()); + .containsOnly(Pub.class.getName(), PubSub.class.getName(), Priv.class.getName()); } } } diff --git a/src/test/java/io/github/classgraph/issues/issue364/Issue364Test.java b/src/test/java/io/github/classgraph/issues/issue364/Issue364Test.java index d669164e5..5458f9ef9 100644 --- a/src/test/java/io/github/classgraph/issues/issue364/Issue364Test.java +++ b/src/test/java/io/github/classgraph/issues/issue364/Issue364Test.java @@ -96,7 +96,7 @@ public void testPermissions() { assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/all") .get(0).getLastModified()).isEqualTo(1434543812000L); assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/all") - .get(0).getPosixFilePermissions()).containsExactlyInAnyOrder(PosixFilePermission.OWNER_READ, + .get(0).getPosixFilePermissions()).containsOnly(PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE, PosixFilePermission.OWNER_EXECUTE, PosixFilePermission.GROUP_READ, PosixFilePermission.GROUP_WRITE, PosixFilePermission.GROUP_EXECUTE, PosixFilePermission.OTHERS_READ, @@ -105,7 +105,7 @@ public void testPermissions() { assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/execute") .get(0).getLastModified()).isEqualTo(1434557130000L); assertThat(result.getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/execute") - .get(0).getPosixFilePermissions()).containsExactlyInAnyOrder(PosixFilePermission.OWNER_READ, + .get(0).getPosixFilePermissions()).containsOnly(PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE, PosixFilePermission.OWNER_EXECUTE, PosixFilePermission.GROUP_READ, PosixFilePermission.GROUP_EXECUTE, PosixFilePermission.OTHERS_READ, PosixFilePermission.OTHERS_EXECUTE); @@ -115,7 +115,7 @@ public void testPermissions() { .get(0).getLastModified()).isEqualTo(1434557162000L); assertThat(result .getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/groupreadwrite") - .get(0).getPosixFilePermissions()).containsExactlyInAnyOrder(PosixFilePermission.OWNER_READ, + .get(0).getPosixFilePermissions()).containsOnly(PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE, PosixFilePermission.GROUP_READ, PosixFilePermission.GROUP_WRITE); @@ -124,14 +124,14 @@ public void testPermissions() { .get(0).getLastModified()).isEqualTo(1434557152000L); assertThat(result .getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/owneronlyread") - .get(0).getPosixFilePermissions()).containsExactlyInAnyOrder(PosixFilePermission.OWNER_READ); + .get(0).getPosixFilePermissions()).containsOnly(PosixFilePermission.OWNER_READ); assertThat(result .getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/ownerreadwrite") .get(0).getLastModified()).isEqualTo(1434543812000L); assertThat(result .getResourcesWithPath("META-INF/resources/webjars/permissions-jar/1.0.0/bin/ownerreadwrite") - .get(0).getPosixFilePermissions()).containsExactlyInAnyOrder(PosixFilePermission.OWNER_READ, + .get(0).getPosixFilePermissions()).containsOnly(PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE, PosixFilePermission.GROUP_READ, PosixFilePermission.OTHERS_READ); } 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 4459e3d01..c3be28ba3 100644 --- a/src/test/java/io/github/classgraph/issues/issue38/Issue38Test.java +++ b/src/test/java/io/github/classgraph/issues/issue38/Issue38Test.java @@ -30,7 +30,7 @@ void testImplementsSuppressWarnings() { try (ScanResult scanResult = new ClassGraph().whitelistPackages(Issue38Test.class.getPackage().getName()) .scan()) { assertThat(scanResult.getClassesImplementing(SuppressWarnings.class.getName()).getNames()) - .containsExactlyInAnyOrder(ImplementsSuppressWarnings.class.getName()); + .containsOnly(ImplementsSuppressWarnings.class.getName()); } } } 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 8c8bb3d77..bad44d667 100644 --- a/src/test/java/io/github/classgraph/issues/issue46/Issue46Test.java +++ b/src/test/java/io/github/classgraph/issues/issue46/Issue46Test.java @@ -47,7 +47,7 @@ public void issue46Test() { final String jarPath = Issue46Test.class.getClassLoader().getResource("nested-jars-level1.zip").getPath() + "!level2.jar!level3.jar!classpath1/classpath2"; try (ScanResult scanResult = new ClassGraph().overrideClasspath(jarPath).enableClassInfo().scan()) { - assertThat(scanResult.getAllClasses().getNames()).containsExactlyInAnyOrder("com.test.Test"); + assertThat(scanResult.getAllClasses().getNames()).containsOnly("com.test.Test"); } } } 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 66491f978..65f14e5e1 100644 --- a/src/test/java/io/github/classgraph/issues/issue74/Issue74Test.java +++ b/src/test/java/io/github/classgraph/issues/issue74/Issue74Test.java @@ -42,9 +42,9 @@ public class ImplementsFunction implements Function { public void issue74() { try (ScanResult scanResult = new ClassGraph().whitelistPackages(Issue74Test.class.getPackage().getName()) .scan()) { - assertThat(scanResult.getClassesImplementing(Function.class.getName()).getNames()) - .containsExactlyInAnyOrder(FunctionAdapter.class.getName(), ImplementsFunction.class.getName(), - ExtendsFunctionAdapter.class.getName()); + assertThat(scanResult.getClassesImplementing(Function.class.getName()).getNames()).containsOnly( + FunctionAdapter.class.getName(), ImplementsFunction.class.getName(), + ExtendsFunctionAdapter.class.getName()); } } } diff --git a/src/test/java/io/github/classgraph/issues/issue78/Issue78Test.java b/src/test/java/io/github/classgraph/issues/issue78/Issue78Test.java index 83daadbb0..3b3bd2e75 100644 --- a/src/test/java/io/github/classgraph/issues/issue78/Issue78Test.java +++ b/src/test/java/io/github/classgraph/issues/issue78/Issue78Test.java @@ -17,8 +17,7 @@ public class Issue78Test { @Test public void issue78() { try (ScanResult scanResult = new ClassGraph().whitelistClasses(Issue78Test.class.getName()).scan()) { - assertThat(scanResult.getAllClasses().getNames()) - .containsExactlyInAnyOrder(Issue78Test.class.getName()); + assertThat(scanResult.getAllClasses().getNames()).containsOnly(Issue78Test.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 2ffdb81b9..8996946d9 100644 --- a/src/test/java/io/github/classgraph/issues/issue93/Issue93.java +++ b/src/test/java/io/github/classgraph/issues/issue93/Issue93.java @@ -51,9 +51,9 @@ public void classRetentionIsDefault() { try (ScanResult scanResult = new ClassGraph().whitelistPackages(PKG).enableAnnotationInfo() .ignoreClassVisibility().scan()) { assertThat(scanResult.getClassesWithAnnotation(RetentionClass.class.getName()).getNames()) - .containsExactlyInAnyOrder(RetentionClassAnnotated.class.getName()); + .containsOnly(RetentionClassAnnotated.class.getName()); assertThat(scanResult.getClassesWithAnnotation(RetentionRuntime.class.getName()).getNames()) - .containsExactlyInAnyOrder(RetentionRuntimeAnnotated.class.getName()); + .containsOnly(RetentionRuntimeAnnotated.class.getName()); } } @@ -67,7 +67,7 @@ public void classRetentionIsNotVisibleWithRetentionPolicyRUNTIME() { .ignoreClassVisibility().disableRuntimeInvisibleAnnotations().scan()) { assertThat(scanResult.getClassesWithAnnotation(RetentionClass.class.getName()).getNames()).isEmpty(); assertThat(scanResult.getClassesWithAnnotation(RetentionRuntime.class.getName()).getNames()) - .containsExactlyInAnyOrder(RetentionRuntimeAnnotated.class.getName()); + .containsOnly(RetentionRuntimeAnnotated.class.getName()); } } } diff --git a/src/test/java/io/github/classgraph/issues/issue99/Issue99Test.java b/src/test/java/io/github/classgraph/issues/issue99/Issue99Test.java index cf3c73cc2..ec1b83d41 100644 --- a/src/test/java/io/github/classgraph/issues/issue99/Issue99Test.java +++ b/src/test/java/io/github/classgraph/issues/issue99/Issue99Test.java @@ -49,7 +49,7 @@ public class Issue99Test { @Test public void testWithoutBlacklist() { try (ScanResult scanResult = new ClassGraph().overrideClasspath(jarPath).enableClassInfo().scan()) { - assertThat(scanResult.getAllClasses().getNames()).containsExactlyInAnyOrder("com.test.Test"); + assertThat(scanResult.getAllClasses().getNames()).containsOnly("com.test.Test"); } } diff --git a/src/test/java/io/github/classgraph/test/ClassGraphTest.java b/src/test/java/io/github/classgraph/test/ClassGraphTest.java index c920abb10..e74813dda 100644 --- a/src/test/java/io/github/classgraph/test/ClassGraphTest.java +++ b/src/test/java/io/github/classgraph/test/ClassGraphTest.java @@ -280,7 +280,7 @@ public void testNonWhitelistedAnnotationReturnedWithoutStrictWhitelist() { try (ScanResult scanResult = new ClassGraph().whitelistPackages(WHITELIST_PACKAGE).enableAnnotationInfo() .enableExternalClasses().scan()) { assertThat(scanResult.getAnnotationsOnClass(Whitelisted.class.getName()).getNames()) - .containsExactlyInAnyOrder(BlacklistedAnnotation.class.getName()); + .containsOnly(BlacklistedAnnotation.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 91b5f1ef0..be84df9df 100644 --- a/src/test/java/io/github/classgraph/test/ClassInfoTest.java +++ b/src/test/java/io/github/classgraph/test/ClassInfoTest.java @@ -68,7 +68,7 @@ public void filter() { public boolean accept(final ClassInfo ci) { return ci.getName().contains("ClsSub"); } - }).getNames()).containsExactlyInAnyOrder(ClsSub.class.getName(), ClsSubSub.class.getName()); + }).getNames()).containsOnly(ClsSub.class.getName(), ClsSubSub.class.getName()); } /** @@ -81,7 +81,7 @@ public void streamHasSuperInterfaceDirect() { public boolean accept(final ClassInfo ci) { return ci.getInterfaces().directOnly().getNames().contains(Iface.class.getName()); } - }).getNames()).containsExactlyInAnyOrder(IfaceSub.class.getName(), Impl2.class.getName()); + }).getNames()).containsOnly(IfaceSub.class.getName(), Impl2.class.getName()); } /** @@ -94,8 +94,8 @@ public void streamHasSuperInterface() { public boolean accept(final ClassInfo ci) { return ci.getInterfaces().getNames().contains(Iface.class.getName()); } - }).getNames()).containsExactlyInAnyOrder(IfaceSub.class.getName(), IfaceSubSub.class.getName(), - Impl2.class.getName(), Impl2Sub.class.getName(), Impl2SubSub.class.getName(), Impl1.class.getName(), + }).getNames()).containsOnly(IfaceSub.class.getName(), IfaceSubSub.class.getName(), Impl2.class.getName(), + Impl2Sub.class.getName(), Impl2SubSub.class.getName(), Impl1.class.getName(), Impl1Sub.class.getName(), Impl1SubSub.class.getName()); } @@ -105,7 +105,7 @@ public boolean accept(final ClassInfo ci) { @Test public void implementsInterfaceDirect() { assertThat(scanResult.getClassesImplementing(Iface.class.getName()).directOnly().getNames()) - .containsExactlyInAnyOrder(IfaceSub.class.getName(), Impl2.class.getName()); + .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()).containsExactlyInAnyOrder( + assertThat(scanResult.getClassesImplementing(Iface.class.getName()).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()); @@ -130,6 +130,6 @@ public boolean accept(final ClassInfo ci) { return ci.getInterfaces().getNames().contains(Iface.class.getName()) && ci.getSuperclasses().getNames().contains(Impl1.class.getName()); } - }).getNames()).containsExactlyInAnyOrder(Impl1Sub.class.getName(), Impl1SubSub.class.getName()); + }).getNames()).containsOnly(Impl1Sub.class.getName(), Impl1SubSub.class.getName()); } } 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 a35d59b9d..b8c3a7745 100644 --- a/src/test/java/io/github/classgraph/test/fieldannotation/FieldAndMethodAnnotationTest.java +++ b/src/test/java/io/github/classgraph/test/fieldannotation/FieldAndMethodAnnotationTest.java @@ -76,7 +76,7 @@ public void testGetNamesOfClassesWithFieldAnnotationIgnoringVisibility() { .ignoreFieldVisibility().enableAnnotationInfo().scan()) { final List testClasses = scanResult .getClassesWithFieldAnnotation(ExternalAnnotation.class.getName()).getNames(); - assertThat(testClasses).containsExactlyInAnyOrder(FieldAndMethodAnnotationTest.class.getName()); + assertThat(testClasses).containsOnly(FieldAndMethodAnnotationTest.class.getName()); } } @@ -91,7 +91,7 @@ public void testGetNamesOfClassesWithMethodAnnotation() { .enableAnnotationInfo().scan()) { final List testClasses = scanResult .getClassesWithMethodAnnotation(ExternalAnnotation.class.getName()).getNames(); - assertThat(testClasses).containsExactlyInAnyOrder(FieldAndMethodAnnotationTest.class.getName()); + assertThat(testClasses).containsOnly(FieldAndMethodAnnotationTest.class.getName()); } } } diff --git a/src/test/java/io/github/classgraph/test/fieldinfo/FieldInfoTest.java b/src/test/java/io/github/classgraph/test/fieldinfo/FieldInfoTest.java index 011907f33..da0628e09 100644 --- a/src/test/java/io/github/classgraph/test/fieldinfo/FieldInfoTest.java +++ b/src/test/java/io/github/classgraph/test/fieldinfo/FieldInfoTest.java @@ -76,7 +76,7 @@ public void testGetFieldInfo() { .scan()) { final List fieldInfoStrs = scanResult.getClassInfo(FieldInfoTest.class.getName()).getFieldInfo() .getAsStrings(); - assertThat(fieldInfoStrs).containsExactlyInAnyOrder( + assertThat(fieldInfoStrs).containsOnly( "@" + ExternalAnnotation.class.getName() + " public static final int publicFieldWithAnnotation = 3", "public int fieldWithoutAnnotation"); @@ -93,7 +93,7 @@ public void testGetFieldInfoIgnoringVisibility() { .ignoreFieldVisibility().scan()) { final List fieldInfoStrs = scanResult.getClassInfo(FieldInfoTest.class.getName()).getFieldInfo() .getAsStrings(); - assertThat(fieldInfoStrs).containsExactlyInAnyOrder( + assertThat(fieldInfoStrs).containsOnly( "@" + ExternalAnnotation.class.getName() + " public static final int publicFieldWithAnnotation = 3", "@" + ExternalAnnotation.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 716dc45da..2e1752505 100644 --- a/src/test/java/io/github/classgraph/test/internal/InternalExternalTest.java +++ b/src/test/java/io/github/classgraph/test/internal/InternalExternalTest.java @@ -21,7 +21,7 @@ public class InternalExternalTest { public void testWhitelistingExternalClasses() { try (ScanResult scanResult = new ClassGraph().whitelistPackages( InternalExternalTest.class.getPackage().getName(), ExternalAnnotation.class.getName()).scan()) { - assertThat(scanResult.getAllStandardClasses().getNames()).containsExactlyInAnyOrder( + assertThat(scanResult.getAllStandardClasses().getNames()).containsOnly( InternalExternalTest.class.getName(), InternalExtendsExternal.class.getName(), InternalImplementsExternal.class.getName(), InternalAnnotatedByExternal.class.getName()); } @@ -36,7 +36,7 @@ public void testEnableExternalClasses() { .whitelistPackages(InternalExternalTest.class.getPackage().getName(), ExternalAnnotation.class.getName()) .enableExternalClasses().scan()) { - assertThat(scanResult.getAllStandardClasses().getNames()).containsExactlyInAnyOrder( + assertThat(scanResult.getAllStandardClasses().getNames()).containsOnly( ExternalSuperclass.class.getName(), InternalExternalTest.class.getName(), InternalExtendsExternal.class.getName(), InternalImplementsExternal.class.getName(), InternalAnnotatedByExternal.class.getName()); @@ -52,17 +52,17 @@ public void testWhitelistingExternalClassesWithoutEnablingExternalClasses() { .whitelistPackages(InternalExternalTest.class.getPackage().getName(), ExternalAnnotation.class.getName()) .enableAllInfo().scan()) { - assertThat(scanResult.getAllStandardClasses().getNames()).containsExactlyInAnyOrder( + assertThat(scanResult.getAllStandardClasses().getNames()).containsOnly( InternalExternalTest.class.getName(), InternalExtendsExternal.class.getName(), InternalImplementsExternal.class.getName(), InternalAnnotatedByExternal.class.getName()); assertThat(scanResult.getSubclasses(ExternalSuperclass.class.getName()).getNames()) - .containsExactlyInAnyOrder(InternalExtendsExternal.class.getName()); + .containsOnly(InternalExtendsExternal.class.getName()); assertThat(scanResult.getAllInterfaces()).isEmpty(); assertThat(scanResult.getClassesImplementing(ExternalInterface.class.getName()).getNames()) - .containsExactlyInAnyOrder(InternalImplementsExternal.class.getName()); + .containsOnly(InternalImplementsExternal.class.getName()); assertThat(scanResult.getAllAnnotations().getNames()).isEmpty(); assertThat(scanResult.getClassesWithAnnotation(ExternalAnnotation.class.getName()).getNames()) - .containsExactlyInAnyOrder(InternalAnnotatedByExternal.class.getName()); + .containsOnly(InternalAnnotatedByExternal.class.getName()); } } @@ -76,14 +76,14 @@ public void testIncludeReferencedClasses() { assertThat(scanResult.getAllStandardClasses().getNames()) .doesNotContain(ExternalSuperclass.class.getName()); assertThat(scanResult.getSubclasses(ExternalSuperclass.class.getName()).getNames()) - .containsExactlyInAnyOrder(InternalExtendsExternal.class.getName()); + .containsOnly(InternalExtendsExternal.class.getName()); assertThat(scanResult.getAllInterfaces().getNames()).doesNotContain(ExternalInterface.class.getName()); assertThat(scanResult.getClassesImplementing(ExternalInterface.class.getName()).getNames()) - .containsExactlyInAnyOrder(InternalImplementsExternal.class.getName()); + .containsOnly(InternalImplementsExternal.class.getName()); assertThat(scanResult.getAllAnnotations().getNames()) .doesNotContain(ExternalAnnotation.class.getName()); assertThat(scanResult.getClassesWithAnnotation(ExternalAnnotation.class.getName()).getNames()) - .containsExactlyInAnyOrder(InternalAnnotatedByExternal.class.getName()); + .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 e1580f080..c34a05bb5 100644 --- a/src/test/java/io/github/classgraph/test/methodannotation/MethodAnnotationTest.java +++ b/src/test/java/io/github/classgraph/test/methodannotation/MethodAnnotationTest.java @@ -70,7 +70,7 @@ public void testGetNamesOfClassesWithMethodAnnotationIgnoringVisibility() { final ClassInfoList classesWithMethodAnnotation = scanResult .getClassesWithMethodAnnotation(ExternalAnnotation.class.getName()); final List testClasses = classesWithMethodAnnotation.getNames(); - assertThat(testClasses).containsExactlyInAnyOrder(MethodAnnotationTest.class.getName()); + assertThat(testClasses).containsOnly(MethodAnnotationTest.class.getName()); boolean found = false; for (final ClassInfo ci : classesWithMethodAnnotation) { for (final MethodInfo mi : ci.getMethodInfo()) { 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 3a0ad3565..cf245d303 100644 --- a/src/test/java/io/github/classgraph/test/methodannotation2/TestMethodMetaAnnotation.java +++ b/src/test/java/io/github/classgraph/test/methodannotation2/TestMethodMetaAnnotation.java @@ -109,8 +109,8 @@ public void testMetaAnnotation() { .scan()) { final List testClasses = scanResult.getClassesWithAnnotation(MetaAnnotation.class.getName()) .getNames(); - assertThat(testClasses).containsExactlyInAnyOrder(MethodAnnotation.class.getName(), - ClassAnnotation.class.getName(), MetaAnnotatedClass.class.getName()); + assertThat(testClasses).containsOnly(MethodAnnotation.class.getName(), ClassAnnotation.class.getName(), + MetaAnnotatedClass.class.getName()); } } @@ -125,7 +125,7 @@ public void testMetaAnnotationStandardClassesOnly() { .scan()) { final List testClasses = scanResult.getClassesWithAnnotation(MetaAnnotation.class.getName()) .getStandardClasses().getNames(); - assertThat(testClasses).containsExactlyInAnyOrder(MetaAnnotatedClass.class.getName()); + assertThat(testClasses).containsOnly(MetaAnnotatedClass.class.getName()); } } @@ -140,7 +140,7 @@ public void testMethodMetaAnnotation() { .enableAnnotationInfo().scan()) { final List testClasses = scanResult .getClassesWithMethodAnnotation(MetaAnnotation.class.getName()).getNames(); - assertThat(testClasses).containsExactlyInAnyOrder(ClassWithMetaAnnotatedMethod.class.getName()); + assertThat(testClasses).containsOnly(ClassWithMetaAnnotatedMethod.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 7f28b9197..905ef8c53 100644 --- a/src/test/java/io/github/classgraph/test/methodinfo/MethodInfoTest.java +++ b/src/test/java/io/github/classgraph/test/methodinfo/MethodInfoTest.java @@ -125,7 +125,7 @@ public boolean accept(final MethodInfo methodInfo) { return !methodInfo.getName().equals("$closeResource") && !methodInfo.getName().equals("lambda$0") && !methodInfo.isSynthetic(); } - }).getAsStrings()).containsExactlyInAnyOrder( // + }).getAsStrings()).containsOnly( // "@" + ExternalAnnotation.class.getName() // + " public final int publicMethodWithArgs" + "(java.lang.String, char, long, float[], byte[][], " @@ -147,7 +147,7 @@ public void testGetConstructorInfo() { try (ScanResult scanResult = new ClassGraph().whitelistPackages(MethodInfoTest.class.getPackage().getName()) .enableMethodInfo().scan()) { assertThat(scanResult.getClassInfo(MethodInfoTest.class.getName()).getConstructorInfo().getAsStrings()) - .containsExactlyInAnyOrder("public ()"); + .containsOnly("public ()"); } } @@ -166,7 +166,7 @@ public boolean accept(final MethodInfo methodInfo) { return !methodInfo.getName().equals("$closeResource") && !methodInfo.getName().equals("lambda$0") && !methodInfo.isSynthetic(); } - }).getAsStrings()).containsExactlyInAnyOrder( // + }).getAsStrings()).containsOnly( // "@" + ExternalAnnotation.class.getName() // + " public final int publicMethodWithArgs" + "(java.lang.String, char, long, float[], byte[][], " From 37baf6bc1ba68eb361d76f850b8c90d94975550b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 17 Oct 2019 16:47:12 -0600 Subject: [PATCH 0383/1778] Simplify code --- src/main/java/nonapi/io/github/classgraph/utils/Join.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/utils/Join.java b/src/main/java/nonapi/io/github/classgraph/utils/Join.java index 907c1d4b6..e1946e223 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/Join.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/Join.java @@ -64,11 +64,7 @@ public static void join(final StringBuilder buf, final String addAtBeginning, fi } else { buf.append(sep); } - if (item == null) { - buf.append("null"); - } else { - buf.append(item.toString()); - } + buf.append(item == null ? "null" : item.toString()); } if (!addAtEnd.isEmpty()) { buf.append(addAtEnd); From 7052f0f1ae052eb523973c3bec0965807c83382f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 17 Oct 2019 23:10:25 -0600 Subject: [PATCH 0384/1778] Update org info --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index f67ff82de..53700d22d 100644 --- a/pom.xml +++ b/pom.xml @@ -22,8 +22,8 @@ Luke Hutchison luke.hutch@gmail.com - -- - https://github.com/lukehutch + ClassGraph + https://github.com/classgraph From 910c44ad81c037f87cf0773c3044f42ef42c81d2 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 17 Oct 2019 23:14:19 -0600 Subject: [PATCH 0385/1778] Update docs --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6d0325a54..009cff764 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ ClassGraph (formerly **FastClasspathScanner**) is an uber-fast, ultra-lightweigh ### ClassGraph vs. Java Introspection -ClassGraph has the ability to "invert" the Java class and/or reflection API, or has the ability to index classes and resources. For example, the Java class and reflection API can tell you the interfaces implemented by a given class, or can give you the list of annotations on a class; ClassGraph can find **all classes that implement a given interface**, or can find **all classes that are annotated with a given annotation**. The Java API can load the content of a resource file with a specific path in a specific ClassLoader, but ClassGraph can find and load **all resources in all classloaders with paths matching a given pattern**. +ClassGraph has the ability to "invert" the Java class and/or reflection API, or has the ability to index classes and resources. For example, the Java class and reflection API can tell you the superclass of a given class, or the interfaces implemented by a given class, or can give you the list of annotations on a class; ClassGraph can find **all classes that extend a given class** (all subclasses of a given class), or **all classes that implement a given interface**, or **all classes that are annotated with a given annotation**. The Java API can load the content of a resource file with a specific path in a specific ClassLoader, but ClassGraph can find and load **all resources in all classloaders with paths matching a given pattern**. ### Examples From e068161c52dbea03cd601375ad7f1ff80d9200aa Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 17 Oct 2019 23:19:41 -0600 Subject: [PATCH 0386/1778] Update docs --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 009cff764..d1e6577e5 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ClassGraph Logo       Duke Award Logo -ClassGraph (formerly **FastClasspathScanner**) is an uber-fast, ultra-lightweight, parallelized classpath scanner and module scanner for Java, Scala, Kotlin and other JVM languages. +ClassGraph is an uber-fast, ultra-lightweight, 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. | |-----------------------------| From b42548cf3d913e618665b4abb13bf4030e7129f6 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 18 Oct 2019 09:33:37 -0600 Subject: [PATCH 0387/1778] Don't hold reference to context classloader in shutdown hook (#376) --- .../java/io/github/classgraph/ScanResult.java | 42 +++++++++++++++++-- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index dec9d5d22..030e54843 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -35,6 +35,7 @@ import java.net.URI; import java.net.URL; import java.nio.ByteBuffer; +import java.nio.MappedByteBuffer; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -45,6 +46,7 @@ import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -194,10 +196,10 @@ public SerializationFormat(final String serializationFormatStr, final ScanSpec s // Shutdown hook static { - // Add runtime shutdown hook to remove temporary files on Ctrl-C or System.exit(). - Runtime.getRuntime().addShutdownHook(new Thread() { + final Thread hookThread = new Thread() { @Override public void run() { + // Close all ScanResult instances that have not yet been closed for (final WeakReference nonClosedWeakReference : new ArrayList<>( nonClosedWeakReferences)) { final ScanResult scanResult = nonClosedWeakReference.get(); @@ -207,7 +209,15 @@ public void run() { nonClosedWeakReferences.remove(nonClosedWeakReference); } } - }); + }; + + // Set thread classloader to system classloader, so that a reference is not held to the caller's + // classloader, e.g. servlet container classloader (#376) + final ClassLoader systemClassLoader = MappedByteBuffer.class.getClassLoader(); + hookThread.setContextClassLoader(systemClassLoader); + + // Add runtime shutdown hook to remove temporary files on Ctrl-C or System.exit(). + Runtime.getRuntime().addShutdownHook(hookThread); // 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. Otherwise, the classloader may be @@ -215,7 +225,31 @@ public void run() { // fail, which will throw an exception and leave resources open (#331). // 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); + final Semaphore wait = new Semaphore(0); + final Thread preloadClassesThread = new Thread() { + @Override + public void run() { + try { + // Warm up the classloader, caching the classes necessary to close direct byte buffers + FileUtils.closeDirectByteBuffer(ByteBuffer.allocateDirect(32), /* log = */ null); + } catch (final Exception e) { + // Should not happen + throw new RuntimeException(e); + } + // Allow code below to continue + wait.release(); + } + }; + // Run this thread with the same context classloader as the shutdown hook thread (the system classloader) + preloadClassesThread.setContextClassLoader(systemClassLoader); + preloadClassesThread.start(); + try { + // Wait for preloadClassesThread to finish running + wait.acquire(); + } catch (final InterruptedException e) { + // Should not happen + throw new RuntimeException(e); + } } // ------------------------------------------------------------------------------------------------------------- From ea0fc7e5b643abe7bfcdeb964002bde0c8dc4ed7 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 18 Oct 2019 09:37:22 -0600 Subject: [PATCH 0388/1778] Drop another possible reference to context classloader from shutdown hook (#376) --- .../java/io/github/classgraph/ScanResult.java | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index 030e54843..4b1b70a91 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -128,8 +128,7 @@ public final class ScanResult implements Closeable, AutoCloseable { * The set of WeakReferences to non-closed ScanResult objects. Uses WeakReferences so that garbage collection is * not blocked. (Bug #233) */ - private static final Set> nonClosedWeakReferences = Collections - .newSetFromMap(new ConcurrentHashMap, Boolean>()); + private static Set> nonClosedWeakReferences; // ------------------------------------------------------------------------------------------------------------- @@ -200,13 +199,15 @@ public SerializationFormat(final String serializationFormatStr, final ScanSpec s @Override public void run() { // Close all ScanResult instances that have not yet been closed - for (final WeakReference nonClosedWeakReference : new ArrayList<>( - nonClosedWeakReferences)) { - final ScanResult scanResult = nonClosedWeakReference.get(); - if (scanResult != null) { - scanResult.close(); + if (nonClosedWeakReferences != null) { + for (final WeakReference nonClosedWeakReference : new ArrayList<>( + nonClosedWeakReferences)) { + final ScanResult scanResult = nonClosedWeakReference.get(); + if (scanResult != null) { + scanResult.close(); + } + nonClosedWeakReferences.remove(nonClosedWeakReference); } - nonClosedWeakReferences.remove(nonClosedWeakReference); } } }; @@ -229,6 +230,9 @@ public void run() { final Thread preloadClassesThread = new Thread() { @Override public void run() { + // Create the new ConcurrentHashMap from the system classloader + nonClosedWeakReferences = Collections + .newSetFromMap(new ConcurrentHashMap, Boolean>()); try { // Warm up the classloader, caching the classes necessary to close direct byte buffers FileUtils.closeDirectByteBuffer(ByteBuffer.allocateDirect(32), /* log = */ null); @@ -334,8 +338,13 @@ public void run() { this.classGraphClassLoader = new ClassGraphClassLoader(this); // Provide the shutdown hook with a weak reference to this ScanResult - this.weakReference = new WeakReference<>(this); - nonClosedWeakReferences.add(this.weakReference); + if (nonClosedWeakReferences != null) { + this.weakReference = new WeakReference<>(this); + nonClosedWeakReferences.add(this.weakReference); + } else { + // Should not happen + throw new RuntimeException("nonClosedWeakReferences should not be null"); + } } /** Index {@link Resource} and {@link ClassInfo} objects. */ @@ -1433,7 +1442,9 @@ public void close() { classGraphClassLoader = null; classLoaderOrderRespectingParentDelegation = null; // Remove WeakReference to this ScanResult, so shutdown hook does not try to close this - nonClosedWeakReferences.remove(weakReference); + if (nonClosedWeakReferences != null) { + nonClosedWeakReferences.remove(weakReference); + } // Flush log on exit, in case additional log entries were generated after scan() completed if (topLevelLog != null) { topLevelLog.flush(); From 9050d0bd4848102c64ad34d901dcda0739077516 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 18 Oct 2019 09:50:07 -0600 Subject: [PATCH 0389/1778] Fix Scrutinizer warning --- .../io/github/classgraph/ObjectTypedValueWrapper.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java b/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java index 472f7b5c8..7c6bf3c73 100644 --- a/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java +++ b/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java @@ -290,21 +290,19 @@ private Object getArrayValueClassOrName(final ClassInfo annotationClassInfo, fin // Find the method in the annotation class with the same name as the annotation parameter. final MethodInfoList annotationMethodList = annotationClassInfo == null || annotationClassInfo.methodInfo == null ? null : annotationClassInfo.methodInfo.get(paramName); - if (annotationMethodList != null && !annotationMethodList.isEmpty()) { + if (annotationClassInfo != null && annotationMethodList != null && !annotationMethodList.isEmpty()) { if (annotationMethodList.size() > 1) { // There should only be one method with a given name in an annotation throw new IllegalArgumentException("Duplicated annotation parameter method " + paramName + "()" - + (annotationClassInfo == null ? "" - : " in annotation class " + annotationClassInfo.getName())); + + " in annotation class " + annotationClassInfo.getName()); } // Get the result type of the method with the same name as the annotation parameter final TypeSignature annotationMethodResultTypeSig = annotationMethodList.get(0) .getTypeSignatureOrTypeDescriptor().getResultType(); // The result type has to be an array type if (!(annotationMethodResultTypeSig instanceof ArrayTypeSignature)) { - throw new IllegalArgumentException("Annotation parameter " + paramName - + (annotationClassInfo == null ? "" - : " in annotation class " + annotationClassInfo.getName()) + throw new IllegalArgumentException("Annotation parameter " + paramName + " in annotation class " + + annotationClassInfo.getName() + " holds an array, but does not have an array type signature"); } final ArrayTypeSignature arrayTypeSig = (ArrayTypeSignature) annotationMethodResultTypeSig; From b6e640c4098ce8f3f82f37f82de49f0edf9273a1 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 18 Oct 2019 10:01:11 -0600 Subject: [PATCH 0390/1778] Run shutdown hook init thread in new ExecutorService --- src/main/java/io/github/classgraph/ClassGraph.java | 5 +++++ src/main/java/io/github/classgraph/ScanResult.java | 12 +++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index 20356484e..ec70f5963 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -79,6 +79,11 @@ public class ClassGraph { /** If non-null, log while scanning. */ private LogNode topLevelLog; + // Run shutdown hook init code + static { + ScanResult.init(); + } + // ------------------------------------------------------------------------------------------------------------- /** Construct a ClassGraph instance. */ diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index 4b1b70a91..e176ad5cc 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -46,6 +46,8 @@ import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Matcher; @@ -192,9 +194,9 @@ public SerializationFormat(final String serializationFormatStr, final ScanSpec s } // ------------------------------------------------------------------------------------------------------------- - // Shutdown hook + // Shutdown hook init code - static { + static void init() { final Thread hookThread = new Thread() { @Override public void run() { @@ -230,9 +232,11 @@ public void run() { final Thread preloadClassesThread = new Thread() { @Override public void run() { + System.out.println("running"); // Create the new ConcurrentHashMap from the system classloader nonClosedWeakReferences = Collections .newSetFromMap(new ConcurrentHashMap, Boolean>()); + System.out.println("ended"); try { // Warm up the classloader, caching the classes necessary to close direct byte buffers FileUtils.closeDirectByteBuffer(ByteBuffer.allocateDirect(32), /* log = */ null); @@ -246,7 +250,8 @@ public void run() { }; // Run this thread with the same context classloader as the shutdown hook thread (the system classloader) preloadClassesThread.setContextClassLoader(systemClassLoader); - preloadClassesThread.start(); + ExecutorService executor = Executors.newFixedThreadPool(1); + executor.execute(preloadClassesThread); try { // Wait for preloadClassesThread to finish running wait.acquire(); @@ -254,6 +259,7 @@ public void run() { // Should not happen throw new RuntimeException(e); } + executor.shutdown(); } // ------------------------------------------------------------------------------------------------------------- From 0ac74347a8337664e612d880cdbad42fbf3b16af Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 18 Oct 2019 10:08:42 -0600 Subject: [PATCH 0391/1778] Remove debug statements --- src/main/java/io/github/classgraph/ScanResult.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index e176ad5cc..35b7c6265 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -232,11 +232,9 @@ public void run() { final Thread preloadClassesThread = new Thread() { @Override public void run() { - System.out.println("running"); // Create the new ConcurrentHashMap from the system classloader nonClosedWeakReferences = Collections .newSetFromMap(new ConcurrentHashMap, Boolean>()); - System.out.println("ended"); try { // Warm up the classloader, caching the classes necessary to close direct byte buffers FileUtils.closeDirectByteBuffer(ByteBuffer.allocateDirect(32), /* log = */ null); From 9c5c775e607ed0b1b70a6f10c15f7c6ebacca543 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 18 Oct 2019 10:13:53 -0600 Subject: [PATCH 0392/1778] Use AutoCloseableExecutorService --- .../java/io/github/classgraph/ScanResult.java | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index 35b7c6265..c71bcccf5 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -46,13 +46,12 @@ import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Matcher; import java.util.regex.Pattern; +import nonapi.io.github.classgraph.concurrency.AutoCloseableExecutorService; import nonapi.io.github.classgraph.fastzipfilereader.NestedJarHandler; import nonapi.io.github.classgraph.json.JSONDeserializer; import nonapi.io.github.classgraph.json.JSONSerializer; @@ -248,16 +247,16 @@ public void run() { }; // Run this thread with the same context classloader as the shutdown hook thread (the system classloader) preloadClassesThread.setContextClassLoader(systemClassLoader); - ExecutorService executor = Executors.newFixedThreadPool(1); - executor.execute(preloadClassesThread); - try { - // Wait for preloadClassesThread to finish running - wait.acquire(); - } catch (final InterruptedException e) { - // Should not happen - throw new RuntimeException(e); + try (AutoCloseableExecutorService executorService = new AutoCloseableExecutorService(1)) { + executorService.execute(preloadClassesThread); + try { + // Wait for preloadClassesThread to finish running + wait.acquire(); + } catch (final InterruptedException e) { + // Should not happen + throw new RuntimeException(e); + } } - executor.shutdown(); } // ------------------------------------------------------------------------------------------------------------- From e6d383fb73aa5a802e2f02471dd1bbea79334ed3 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 18 Oct 2019 10:16:53 -0600 Subject: [PATCH 0393/1778] Add shutdown hook only after initialization is complete --- src/main/java/io/github/classgraph/ScanResult.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index c71bcccf5..e8a06fade 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -218,9 +218,6 @@ public void run() { final ClassLoader systemClassLoader = MappedByteBuffer.class.getClassLoader(); hookThread.setContextClassLoader(systemClassLoader); - // Add runtime shutdown hook to remove temporary files on Ctrl-C or System.exit(). - Runtime.getRuntime().addShutdownHook(hookThread); - // 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. Otherwise, the classloader may be // closed by its own shutdown hook before ClassGraph's shutdown hook can run, and classloading can @@ -257,6 +254,9 @@ public void run() { throw new RuntimeException(e); } } + + // Add runtime shutdown hook to remove temporary files on Ctrl-C or System.exit(). + Runtime.getRuntime().addShutdownHook(hookThread); } // ------------------------------------------------------------------------------------------------------------- From f5b4fe93ff39d1a07c33c4cf70a8a3c16b7c40fd Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 18 Oct 2019 10:17:31 -0600 Subject: [PATCH 0394/1778] Rename function --- src/main/java/io/github/classgraph/ClassGraph.java | 2 +- src/main/java/io/github/classgraph/ScanResult.java | 2 +- 2 files 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 ec70f5963..c1647df3d 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -81,7 +81,7 @@ public class ClassGraph { // Run shutdown hook init code static { - ScanResult.init(); + ScanResult.initShutdownHook(); } // ------------------------------------------------------------------------------------------------------------- diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index e8a06fade..81e859f74 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -195,7 +195,7 @@ public SerializationFormat(final String serializationFormatStr, final ScanSpec s // ------------------------------------------------------------------------------------------------------------- // Shutdown hook init code - static void init() { + static void initShutdownHook() { final Thread hookThread = new Thread() { @Override public void run() { From d38ff9f28bb43747c3689ed843f6b975bb74379e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 18 Oct 2019 12:43:29 -0600 Subject: [PATCH 0395/1778] Ensure that context classloader ref is not held by shutdown hook (#376). --- .../java/io/github/classgraph/ClassGraph.java | 2 +- .../java/io/github/classgraph/ScanResult.java | 81 ++++++++++--------- .../AutoCloseableExecutorService.java | 13 +++ .../concurrency/SimpleThreadFactory.java | 24 ++++++ 4 files changed, 79 insertions(+), 41 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index c1647df3d..01132570c 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -83,7 +83,7 @@ public class ClassGraph { static { ScanResult.initShutdownHook(); } - + // ------------------------------------------------------------------------------------------------------------- /** Construct a ClassGraph instance. */ diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index 81e859f74..ccbe7e643 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -196,27 +196,9 @@ public SerializationFormat(final String serializationFormatStr, final ScanSpec s // Shutdown hook init code static void initShutdownHook() { - final Thread hookThread = new Thread() { - @Override - public void run() { - // Close all ScanResult instances that have not yet been closed - if (nonClosedWeakReferences != null) { - for (final WeakReference nonClosedWeakReference : new ArrayList<>( - nonClosedWeakReferences)) { - final ScanResult scanResult = nonClosedWeakReference.get(); - if (scanResult != null) { - scanResult.close(); - } - nonClosedWeakReferences.remove(nonClosedWeakReference); - } - } - } - }; - - // Set thread classloader to system classloader, so that a reference is not held to the caller's + // For shutdown hook, use system classloader, so that a reference is not held to the caller's // classloader, e.g. servlet container classloader (#376) final ClassLoader systemClassLoader = MappedByteBuffer.class.getClassLoader(); - hookThread.setContextClassLoader(systemClassLoader); // 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. Otherwise, the classloader may be @@ -224,28 +206,27 @@ public void run() { // fail, which will throw an exception and leave resources open (#331). // 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. - final Semaphore wait = new Semaphore(0); - final Thread preloadClassesThread = new Thread() { - @Override - public void run() { - // Create the new ConcurrentHashMap from the system classloader - nonClosedWeakReferences = Collections - .newSetFromMap(new ConcurrentHashMap, Boolean>()); - try { - // Warm up the classloader, caching the classes necessary to close direct byte buffers - FileUtils.closeDirectByteBuffer(ByteBuffer.allocateDirect(32), /* log = */ null); - } catch (final Exception e) { - // Should not happen - throw new RuntimeException(e); + // Run this thread with the same context classloader as the shutdown hook thread (the system classloader). + try (AutoCloseableExecutorService executorService = new AutoCloseableExecutorService(1, + systemClassLoader)) { + final Semaphore wait = new Semaphore(0); + executorService.execute(new Runnable() { + @Override + public void run() { + // Create the new ConcurrentHashMap from the system classloader + nonClosedWeakReferences = Collections + .newSetFromMap(new ConcurrentHashMap, Boolean>()); + try { + // Warm up the classloader, caching the classes necessary to close direct byte buffers + FileUtils.closeDirectByteBuffer(ByteBuffer.allocateDirect(32), /* log = */ null); + } catch (final Exception e) { + // Should not happen + throw new RuntimeException(e); + } + // Allow code below to continue + wait.release(); } - // Allow code below to continue - wait.release(); - } - }; - // Run this thread with the same context classloader as the shutdown hook thread (the system classloader) - preloadClassesThread.setContextClassLoader(systemClassLoader); - try (AutoCloseableExecutorService executorService = new AutoCloseableExecutorService(1)) { - executorService.execute(preloadClassesThread); + }); try { // Wait for preloadClassesThread to finish running wait.acquire(); @@ -255,6 +236,26 @@ public void run() { } } + // Create shutdown hook thread to close all open/mapped DirectByteBuffers on shutdown + final Thread hookThread = new Thread() { + @Override + public void run() { + // Close all ScanResult instances that have not yet been closed + if (nonClosedWeakReferences != null) { + for (final WeakReference nonClosedWeakReference : new ArrayList<>( + nonClosedWeakReferences)) { + final ScanResult scanResult = nonClosedWeakReference.get(); + if (scanResult != null) { + scanResult.close(); + } + nonClosedWeakReferences.remove(nonClosedWeakReference); + } + } + } + }; + // Set shutdown hook thread classloader to system classloader, dropping ref to context classloader + hookThread.setContextClassLoader(systemClassLoader); + // Add runtime shutdown hook to remove temporary files on Ctrl-C or System.exit(). Runtime.getRuntime().addShutdownHook(hookThread); } diff --git a/src/main/java/nonapi/io/github/classgraph/concurrency/AutoCloseableExecutorService.java b/src/main/java/nonapi/io/github/classgraph/concurrency/AutoCloseableExecutorService.java index d04119f94..d881ad413 100644 --- a/src/main/java/nonapi/io/github/classgraph/concurrency/AutoCloseableExecutorService.java +++ b/src/main/java/nonapi/io/github/classgraph/concurrency/AutoCloseableExecutorService.java @@ -53,6 +53,19 @@ public AutoCloseableExecutorService(final int numThreads) { new SimpleThreadFactory("ClassGraph-worker-", true)); } + /** + * A ThreadPoolExecutor that can be used in a try-with-resources block. + * + * @param numThreads + * The number of threads to allocate. + * @param threadContextClassLoader + * The context classloader to use for new threads, or null to use the caller's classloader + */ + public AutoCloseableExecutorService(final int numThreads, final ClassLoader threadContextClassLoader) { + super(numThreads, numThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(), + new SimpleThreadFactory("ClassGraph-worker-", true, threadContextClassLoader)); + } + /** * Catch exceptions from both submit() and execute(), and call {@link InterruptionChecker#interrupt()} to * interrupt all threads. 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 2ffd67466..5c6366c49 100644 --- a/src/main/java/nonapi/io/github/classgraph/concurrency/SimpleThreadFactory.java +++ b/src/main/java/nonapi/io/github/classgraph/concurrency/SimpleThreadFactory.java @@ -46,6 +46,9 @@ class SimpleThreadFactory implements java.util.concurrent.ThreadFactory { /** Whether to set daemon mode. */ private final boolean daemon; + /** The context classloader to use for new threads, or null to use the caller's classloader. */ + private final ClassLoader threadContextClassLoader; + /** * Constructor. * @@ -57,6 +60,24 @@ class SimpleThreadFactory implements java.util.concurrent.ThreadFactory { SimpleThreadFactory(final String threadNamePrefix, final boolean daemon) { this.threadNamePrefix = threadNamePrefix; this.daemon = daemon; + this.threadContextClassLoader = null; + } + + /** + * Constructor. + * + * @param threadNamePrefix + * prefix for created threads. + * @param daemon + * create daemon threads? + * @param threadContextClassLoader + * The context classloader to use for new threads, or null to use the caller's classloader + */ + SimpleThreadFactory(final String threadNamePrefix, final boolean daemon, + final ClassLoader threadContextClassLoader) { + this.threadNamePrefix = threadNamePrefix; + this.daemon = daemon; + this.threadContextClassLoader = threadContextClassLoader; } /* (non-Javadoc) @@ -65,6 +86,9 @@ class SimpleThreadFactory implements java.util.concurrent.ThreadFactory { @Override public Thread newThread(final Runnable r) { final Thread t = new Thread(r, threadNamePrefix + threadIdx.getAndIncrement()); + if (threadContextClassLoader != null) { + t.setContextClassLoader(threadContextClassLoader); + } t.setDaemon(daemon); return t; } From 03b3c4629f1a90b8a88fe982348c170a50e9c1e2 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 18 Oct 2019 12:47:19 -0600 Subject: [PATCH 0396/1778] Update comments --- src/main/java/io/github/classgraph/ScanResult.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index ccbe7e643..f4707004d 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -208,6 +208,7 @@ static void initShutdownHook() { // the PriviledgedAction anonymous inner classes used by FileUtils::closeDirectByteBuffer. // Run this thread with the same context classloader as the shutdown hook thread (the system classloader). try (AutoCloseableExecutorService executorService = new AutoCloseableExecutorService(1, + // Create new thread with system classloader systemClassLoader)) { final Semaphore wait = new Semaphore(0); executorService.execute(new Runnable() { @@ -228,7 +229,7 @@ public void run() { } }); try { - // Wait for preloadClassesThread to finish running + // Wait for the above thread to finish running wait.acquire(); } catch (final InterruptedException e) { // Should not happen From 2934320f3f6ed5322dea768a48b95694576ac244 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 18 Oct 2019 19:38:41 -0600 Subject: [PATCH 0397/1778] Enable JDT nullability analysis for fields --- pom.xml | 17 +++++++++++++++-- .../io/github/classgraph/MappableInfoList.java | 1 + .../classgraph/concurrency/SingletonMap.java | 2 ++ .../classgraph/concurrency/WorkQueue.java | 1 + .../classgraph/json/ReferenceEqualityKey.java | 6 ++++-- .../issues/ResolveTypeVariableTest.java | 1 + .../classgraph/json/JSONSerializationTest.java | 3 +++ 7 files changed, 27 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 53700d22d..2cd592383 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,7 @@ - + 4.0.0 io.github.classgraph @@ -120,6 +123,15 @@ 1.0.2.Final test + + + org.eclipse.jdt + org.eclipse.jdt.annotation + 2.2.300 + provided + @@ -231,7 +243,8 @@ - + org.codehaus.mojo.signature diff --git a/src/main/java/io/github/classgraph/MappableInfoList.java b/src/main/java/io/github/classgraph/MappableInfoList.java index e72ec4cf7..206fb6b37 100644 --- a/src/main/java/io/github/classgraph/MappableInfoList.java +++ b/src/main/java/io/github/classgraph/MappableInfoList.java @@ -108,6 +108,7 @@ public boolean containsName(final String name) { * The name to search for. * @return The list item with the given name, or null if not found. */ + @SuppressWarnings("null") public T get(final String name) { for (final T i : this) { if (i != null) { 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 85869d2c6..ebab598a5 100644 --- a/src/main/java/nonapi/io/github/classgraph/concurrency/SingletonMap.java +++ b/src/main/java/nonapi/io/github/classgraph/concurrency/SingletonMap.java @@ -86,6 +86,7 @@ public NullSingletonException(final K key) { */ private static class SingletonHolder { /** The singleton. */ + @SuppressWarnings("null") private volatile V singleton; /** Whether or not the singleton has been initialized (the count will have reached 0 if so). */ @@ -167,6 +168,7 @@ V get() throws InterruptedException { */ public V get(final K key, final LogNode log) throws E, InterruptedException, NullSingletonException { final SingletonHolder singletonHolder = map.get(key); + @SuppressWarnings("null") V instance = null; if (singletonHolder != null) { // There is already a SingletonHolder in the map for this key -- get the value 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 1e52acb65..0a2fbd90b 100644 --- a/src/main/java/nonapi/io/github/classgraph/concurrency/WorkQueue.java +++ b/src/main/java/nonapi/io/github/classgraph/concurrency/WorkQueue.java @@ -212,6 +212,7 @@ public Void call() throws Exception { /** * Send poison pills to workers. */ + @SuppressWarnings("null") private void sendPoisonPills() { for (int i = 0; i < numWorkers; i++) { workUnits.add(new WorkUnitWrapper(null)); diff --git a/src/main/java/nonapi/io/github/classgraph/json/ReferenceEqualityKey.java b/src/main/java/nonapi/io/github/classgraph/json/ReferenceEqualityKey.java index 146b68108..b0901154f 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/ReferenceEqualityKey.java +++ b/src/main/java/nonapi/io/github/classgraph/json/ReferenceEqualityKey.java @@ -55,7 +55,8 @@ public ReferenceEqualityKey(final K wrappedKey) { */ @Override public int hashCode() { - return wrappedKey == null ? 0 : wrappedKey.hashCode(); + final K key = wrappedKey; + return key == null ? 0 : key.hashCode(); } /* (non-Javadoc) @@ -76,6 +77,7 @@ public boolean equals(final Object obj) { */ @Override public String toString() { - return wrappedKey == null ? "null" : wrappedKey.toString(); + final K key = wrappedKey; + return key == null ? "null" : key.toString(); } } \ No newline at end of file diff --git a/src/test/java/io/github/classgraph/issues/ResolveTypeVariableTest.java b/src/test/java/io/github/classgraph/issues/ResolveTypeVariableTest.java index 8fff08a88..c1a5d9850 100644 --- a/src/test/java/io/github/classgraph/issues/ResolveTypeVariableTest.java +++ b/src/test/java/io/github/classgraph/issues/ResolveTypeVariableTest.java @@ -19,6 +19,7 @@ */ public class ResolveTypeVariableTest> { /** The list. */ + @SuppressWarnings("null") T list; /** diff --git a/src/test/java/io/github/classgraph/json/JSONSerializationTest.java b/src/test/java/io/github/classgraph/json/JSONSerializationTest.java index d6ccd0de6..2ff6d8729 100644 --- a/src/test/java/io/github/classgraph/json/JSONSerializationTest.java +++ b/src/test/java/io/github/classgraph/json/JSONSerializationTest.java @@ -38,6 +38,7 @@ private static class A { /** * Constructor. */ + @SuppressWarnings("null") public A() { } @@ -69,6 +70,7 @@ private static class B { /** * Constructor. */ + @SuppressWarnings("null") public B() { } @@ -147,6 +149,7 @@ private static class D { /** * Constructor. */ + @SuppressWarnings("null") public D() { } From fceb181d2248a7c8d936261c08b487865ec0f717 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 19 Oct 2019 09:27:53 -0600 Subject: [PATCH 0398/1778] If context classloader overridden, put threads in new threadgroup (#376) --- .../classgraph/concurrency/SimpleThreadFactory.java | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) 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 5c6366c49..e75ae9f86 100644 --- a/src/main/java/nonapi/io/github/classgraph/concurrency/SimpleThreadFactory.java +++ b/src/main/java/nonapi/io/github/classgraph/concurrency/SimpleThreadFactory.java @@ -84,8 +84,17 @@ class SimpleThreadFactory implements java.util.concurrent.ThreadFactory { * @see java.util.concurrent.ThreadFactory#newThread(java.lang.Runnable) */ @Override - public Thread newThread(final Runnable r) { - final Thread t = new Thread(r, threadNamePrefix + threadIdx.getAndIncrement()); + public Thread newThread(final Runnable runnable) { + ThreadGroup parentThreadGroup = Thread.currentThread().getThreadGroup(); + if (threadContextClassLoader != null) { + for (; parentThreadGroup.getParent() != null; parentThreadGroup = parentThreadGroup.getParent()) { + // If context classloader is overridden, find system (toplevel) thread group, and use this + // as the parent thread group, in order to drop any references to the container's context + // classloader, so that it may be garbage collected if the application is unloaded (#376). + } + parentThreadGroup = new ThreadGroup(parentThreadGroup, threadNamePrefix + "threadgroup"); + } + final Thread t = new Thread(parentThreadGroup, runnable, threadNamePrefix + threadIdx.getAndIncrement()); if (threadContextClassLoader != null) { t.setContextClassLoader(threadContextClassLoader); } From e22b3756671ee04526c192a43c3177cd56b4a1f4 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 19 Oct 2019 09:40:38 -0600 Subject: [PATCH 0399/1778] Put other shutdown thread in own thread group (#376) --- src/main/java/io/github/classgraph/ScanResult.java | 12 ++++++++++-- .../classgraph/concurrency/SimpleThreadFactory.java | 11 ++++++----- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index f4707004d..b2e88d5e9 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -237,8 +237,16 @@ public void run() { } } + ThreadGroup parentThreadGroup = Thread.currentThread().getThreadGroup(); + for (; parentThreadGroup.getParent() != null; parentThreadGroup = parentThreadGroup.getParent()) { + // Find system (toplevel) thread group, and use this as the parent thread group, in order to + // drop any references to the container's context classloader, so that it may be garbage collected + // if the application is unloaded (#376). + } + parentThreadGroup = new ThreadGroup(parentThreadGroup, "ClassGraph-shutdown-thread-group"); + // Create shutdown hook thread to close all open/mapped DirectByteBuffers on shutdown - final Thread hookThread = new Thread() { + final Thread hookThread = new Thread(parentThreadGroup, new Runnable() { @Override public void run() { // Close all ScanResult instances that have not yet been closed @@ -253,7 +261,7 @@ public void run() { } } } - }; + }, "ClassGraph-shutdown-thread"); // Set shutdown hook thread classloader to system classloader, dropping ref to context classloader hookThread.setContextClassLoader(systemClassLoader); 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 e75ae9f86..6b6484824 100644 --- a/src/main/java/nonapi/io/github/classgraph/concurrency/SimpleThreadFactory.java +++ b/src/main/java/nonapi/io/github/classgraph/concurrency/SimpleThreadFactory.java @@ -92,13 +92,14 @@ public Thread newThread(final Runnable runnable) { // as the parent thread group, in order to drop any references to the container's context // classloader, so that it may be garbage collected if the application is unloaded (#376). } - parentThreadGroup = new ThreadGroup(parentThreadGroup, threadNamePrefix + "threadgroup"); + parentThreadGroup = new ThreadGroup(parentThreadGroup, threadNamePrefix + "thread-group"); } - final Thread t = new Thread(parentThreadGroup, runnable, threadNamePrefix + threadIdx.getAndIncrement()); + final Thread thread = new Thread(parentThreadGroup, runnable, + threadNamePrefix + threadIdx.getAndIncrement()); if (threadContextClassLoader != null) { - t.setContextClassLoader(threadContextClassLoader); + thread.setContextClassLoader(threadContextClassLoader); } - t.setDaemon(daemon); - return t; + thread.setDaemon(daemon); + return thread; } } From d8d77cfa2984f3b35d128f09c0b04c2b94453594 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 19 Oct 2019 10:51:07 -0600 Subject: [PATCH 0400/1778] Add ClassGraph#disableShutdownHook() and ScanResult#closeAll() (#376) --- .../java/io/github/classgraph/ClassGraph.java | 45 ++++++++- .../java/io/github/classgraph/ScanResult.java | 99 ++++++++----------- .../AutoCloseableExecutorService.java | 34 +++++++ .../concurrency/SimpleThreadFactory.java | 40 +++++--- 4 files changed, 139 insertions(+), 79 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index 01132570c..a79777628 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -31,6 +31,7 @@ import java.io.File; import java.net.URI; import java.net.URL; +import java.nio.MappedByteBuffer; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; @@ -39,6 +40,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Pattern; import nonapi.io.github.classgraph.classpath.SystemJarFinder; @@ -79,16 +81,49 @@ public class ClassGraph { /** If non-null, log while scanning. */ private LogNode topLevelLog; - // Run shutdown hook init code - static { - ScanResult.initShutdownHook(); - } + /** If true, add a shutdown hook to close all files and direct byte buffers on shutdown. */ + private static final AtomicBoolean enableShutdownHook = new AtomicBoolean(true); + + /** If true, ScanResult#staticInit() has been run. */ + private static final AtomicBoolean scanResultStaticInitRun = new AtomicBoolean(false); // ------------------------------------------------------------------------------------------------------------- /** Construct a ClassGraph instance. */ public ClassGraph() { - // Blank + // Initialize ScanResult, if this is the first call to ClassGraph constructor + if (!scanResultStaticInitRun.getAndSet(true)) { + // A ref to the system ClassLoader. + final ClassLoader mappedByteBufferClassLoader = MappedByteBuffer.class.getClassLoader(); + + // Create nonClosedWeakReferences from the system classloader, so there's no chance that + // the shutdown hook will hold a ref to the context classloader (#376) + AutoCloseableExecutorService.runWithClassLoader(new Runnable() { + @Override + public void run() { + ScanResult.staticInit(); + } + }, mappedByteBufferClassLoader); + + // Set up shutdown hook, if enabled + if (enableShutdownHook.get()) { + ScanResult.initShutdownHook(mappedByteBufferClassLoader); + } + } + } + + /** + * Disable the adding of a shutdown hook, which closes all files and direct byte buffers on shutdown, enabling + * clean shutdown even if the user forgets to call {@link ScanResult#close()} on a {@link ScanResult}. This + * static method must be called before the first call to the {@link ClassGraph#ClassGraph()} constructor, since + * the first call to the constructor adds the shutdown hook if this has not been disabled. + * + *

+ * The shutdown hook should be disabled in environments where apps may be loaded and unloaded many times + * throughout the lifetime of the VM, such as web application servers. + */ + public static void disableShutdownHook() { + enableShutdownHook.set(false); } /** diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index b2e88d5e9..9e168d9a2 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -35,7 +35,6 @@ import java.net.URI; import java.net.URL; import java.nio.ByteBuffer; -import java.nio.MappedByteBuffer; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -46,12 +45,12 @@ import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Matcher; import java.util.regex.Pattern; import nonapi.io.github.classgraph.concurrency.AutoCloseableExecutorService; +import nonapi.io.github.classgraph.concurrency.SimpleThreadFactory; import nonapi.io.github.classgraph.fastzipfilereader.NestedJarHandler; import nonapi.io.github.classgraph.json.JSONDeserializer; import nonapi.io.github.classgraph.json.JSONSerializer; @@ -195,73 +194,39 @@ public SerializationFormat(final String serializationFormatStr, final ScanSpec s // ------------------------------------------------------------------------------------------------------------- // Shutdown hook init code - static void initShutdownHook() { - // For shutdown hook, use system classloader, so that a reference is not held to the caller's - // classloader, e.g. servlet container classloader (#376) - final ClassLoader systemClassLoader = MappedByteBuffer.class.getClassLoader(); - + /** + * Static initialization, called when the ClassGraph class is initialized. Call from + * {@link AutoCloseableExecutorService#runInSystemClassLoader(Runnable)}, so there's no chance that the shutdown + * hook will hold a ref to the context classloader. + */ + static void staticInit() { + nonClosedWeakReferences = Collections + .newSetFromMap(new ConcurrentHashMap, Boolean>()); // 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. Otherwise, the classloader may be // closed by its own shutdown hook before ClassGraph's shutdown hook can run, and classloading can // fail, which will throw an exception and leave resources open (#331). // 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. - // Run this thread with the same context classloader as the shutdown hook thread (the system classloader). - try (AutoCloseableExecutorService executorService = new AutoCloseableExecutorService(1, - // Create new thread with system classloader - systemClassLoader)) { - final Semaphore wait = new Semaphore(0); - executorService.execute(new Runnable() { - @Override - public void run() { - // Create the new ConcurrentHashMap from the system classloader - nonClosedWeakReferences = Collections - .newSetFromMap(new ConcurrentHashMap, Boolean>()); - try { - // Warm up the classloader, caching the classes necessary to close direct byte buffers - FileUtils.closeDirectByteBuffer(ByteBuffer.allocateDirect(32), /* log = */ null); - } catch (final Exception e) { - // Should not happen - throw new RuntimeException(e); - } - // Allow code below to continue - wait.release(); - } - }); - try { - // Wait for the above thread to finish running - wait.acquire(); - } catch (final InterruptedException e) { - // Should not happen - throw new RuntimeException(e); - } - } - - ThreadGroup parentThreadGroup = Thread.currentThread().getThreadGroup(); - for (; parentThreadGroup.getParent() != null; parentThreadGroup = parentThreadGroup.getParent()) { - // Find system (toplevel) thread group, and use this as the parent thread group, in order to - // drop any references to the container's context classloader, so that it may be garbage collected - // if the application is unloaded (#376). - } - parentThreadGroup = new ThreadGroup(parentThreadGroup, "ClassGraph-shutdown-thread-group"); + FileUtils.closeDirectByteBuffer(ByteBuffer.allocateDirect(32), /* log = */ null); + } + /** + * Initialize the shutdown hook. + * + * @param systemClassLoader + * the system classloader + */ + static void initShutdownHook(final ClassLoader systemClassLoader) { // Create shutdown hook thread to close all open/mapped DirectByteBuffers on shutdown - final Thread hookThread = new Thread(parentThreadGroup, new Runnable() { - @Override - public void run() { - // Close all ScanResult instances that have not yet been closed - if (nonClosedWeakReferences != null) { - for (final WeakReference nonClosedWeakReference : new ArrayList<>( - nonClosedWeakReferences)) { - final ScanResult scanResult = nonClosedWeakReference.get(); - if (scanResult != null) { - scanResult.close(); - } - nonClosedWeakReferences.remove(nonClosedWeakReference); + final Thread hookThread = new Thread( + SimpleThreadFactory.newThreadGroupWithRootAsParent("ClassGraph-shutdown-thread-group"), + new Runnable() { + @Override + public void run() { + ScanResult.closeAll(); } - } - } - }, "ClassGraph-shutdown-thread"); + }, "ClassGraph-shutdown-thread"); // Set shutdown hook thread classloader to system classloader, dropping ref to context classloader hookThread.setContextClassLoader(systemClassLoader); @@ -269,6 +234,20 @@ public void run() { Runtime.getRuntime().addShutdownHook(hookThread); } + /** Close all {@link ScanResult} instances that have not yet been closed. */ + public static void closeAll() { + if (nonClosedWeakReferences != null) { + for (final WeakReference nonClosedWeakReference : new ArrayList<>( + nonClosedWeakReferences)) { + final ScanResult scanResult = nonClosedWeakReference.get(); + if (scanResult != null) { + scanResult.close(); + } + nonClosedWeakReferences.remove(nonClosedWeakReference); + } + } + } + // ------------------------------------------------------------------------------------------------------------- // Constructor diff --git a/src/main/java/nonapi/io/github/classgraph/concurrency/AutoCloseableExecutorService.java b/src/main/java/nonapi/io/github/classgraph/concurrency/AutoCloseableExecutorService.java index d881ad413..dc3214e68 100644 --- a/src/main/java/nonapi/io/github/classgraph/concurrency/AutoCloseableExecutorService.java +++ b/src/main/java/nonapi/io/github/classgraph/concurrency/AutoCloseableExecutorService.java @@ -32,6 +32,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.Semaphore; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; @@ -100,6 +101,39 @@ public void afterExecute(final Runnable runnable, final Throwable throwable) { } } + /** + * Run a {@link Runnable} with a specific {@link ClassLoader}. + * + * @param runnable + * the {@link Runnable}. + * @param classLoader + * the {@link ClassLoader}. + */ + public static void runWithClassLoader(final Runnable runnable, final ClassLoader classLoader) { + // Run the Runnable in the system classloader + try (AutoCloseableExecutorService executorService = new AutoCloseableExecutorService(1, + // Create new thread with specific classloader + classLoader)) { + final Semaphore wait = new Semaphore(0); + executorService.execute(new Runnable() { + @Override + public void run() { + // Run the Runnable + runnable.run(); + // Allow code below to continue + wait.release(); + } + }); + try { + // Wait for the above thread to finish running + wait.acquire(); + } catch (final InterruptedException e) { + // Should not happen + throw new RuntimeException(e); + } + } + } + /** Shut down thread pool on close(). */ @Override public void close() { 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 6b6484824..18a0fc78d 100644 --- a/src/main/java/nonapi/io/github/classgraph/concurrency/SimpleThreadFactory.java +++ b/src/main/java/nonapi/io/github/classgraph/concurrency/SimpleThreadFactory.java @@ -35,7 +35,7 @@ * * @author Johno Crawford (johno@sulake.com) */ -class SimpleThreadFactory implements java.util.concurrent.ThreadFactory { +public class SimpleThreadFactory implements java.util.concurrent.ThreadFactory { /** The thread name prefix. */ private final String threadNamePrefix; @@ -80,22 +80,34 @@ class SimpleThreadFactory implements java.util.concurrent.ThreadFactory { this.threadContextClassLoader = threadContextClassLoader; } - /* (non-Javadoc) - * @see java.util.concurrent.ThreadFactory#newThread(java.lang.Runnable) + /** + * Create a new {@link ThreadGroup} with the root {@link ThreadGroup} as its parent. + * + * @param threadGroupName + * the thread group name + * @return the {@link ThreadGroup}. */ - @Override - public Thread newThread(final Runnable runnable) { + public static ThreadGroup newThreadGroupWithRootAsParent(final String threadGroupName) { ThreadGroup parentThreadGroup = Thread.currentThread().getThreadGroup(); - if (threadContextClassLoader != null) { - for (; parentThreadGroup.getParent() != null; parentThreadGroup = parentThreadGroup.getParent()) { - // If context classloader is overridden, find system (toplevel) thread group, and use this - // as the parent thread group, in order to drop any references to the container's context - // classloader, so that it may be garbage collected if the application is unloaded (#376). - } - parentThreadGroup = new ThreadGroup(parentThreadGroup, threadNamePrefix + "thread-group"); + for (; parentThreadGroup.getParent() != null; parentThreadGroup = parentThreadGroup.getParent()) { + // If context classloader is overridden, find system (toplevel) thread group, and use this + // as the parent thread group, in order to drop any references to the container's context + // classloader, so that it may be garbage collected if the application is unloaded (#376). } - final Thread thread = new Thread(parentThreadGroup, runnable, - threadNamePrefix + threadIdx.getAndIncrement()); + return new ThreadGroup(parentThreadGroup, threadGroupName); + } + + /** + * New thread. + * + * @param runnable + * the runnable + * @return the thread + */ + @Override + public Thread newThread(final Runnable runnable) { + final Thread thread = new Thread(newThreadGroupWithRootAsParent(threadNamePrefix + "thread-group"), + runnable, threadNamePrefix + threadIdx.getAndIncrement()); if (threadContextClassLoader != null) { thread.setContextClassLoader(threadContextClassLoader); } From ea7f503d75fd296e3bf52eac9fc381ebd5542bc5 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 19 Oct 2019 11:38:12 -0600 Subject: [PATCH 0401/1778] Use bootstrap classloader for shutdown hook (#376) --- .../java/io/github/classgraph/ClassGraph.java | 15 +++++++++------ .../concurrency/AutoCloseableExecutorService.java | 8 ++++---- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index a79777628..6e9dd7c72 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -31,7 +31,6 @@ import java.io.File; import java.net.URI; import java.net.URL; -import java.nio.MappedByteBuffer; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; @@ -93,21 +92,25 @@ public class ClassGraph { public ClassGraph() { // Initialize ScanResult, if this is the first call to ClassGraph constructor if (!scanResultStaticInitRun.getAndSet(true)) { - // A ref to the system ClassLoader. - final ClassLoader mappedByteBufferClassLoader = MappedByteBuffer.class.getClassLoader(); + // Get a ref to the bootstrap ClassLoader. + ClassLoader bootstrapClassLoader = ClassGraph.class.getClassLoader(); + for (; bootstrapClassLoader.getParent() != null; bootstrapClassLoader = bootstrapClassLoader + .getParent()) { + // Find root classloader + } // Create nonClosedWeakReferences from the system classloader, so there's no chance that // the shutdown hook will hold a ref to the context classloader (#376) - AutoCloseableExecutorService.runWithClassLoader(new Runnable() { + AutoCloseableExecutorService.runWithClassLoader(bootstrapClassLoader, new Runnable() { @Override public void run() { ScanResult.staticInit(); } - }, mappedByteBufferClassLoader); + }); // Set up shutdown hook, if enabled if (enableShutdownHook.get()) { - ScanResult.initShutdownHook(mappedByteBufferClassLoader); + ScanResult.initShutdownHook(bootstrapClassLoader); } } } diff --git a/src/main/java/nonapi/io/github/classgraph/concurrency/AutoCloseableExecutorService.java b/src/main/java/nonapi/io/github/classgraph/concurrency/AutoCloseableExecutorService.java index dc3214e68..80e27b67e 100644 --- a/src/main/java/nonapi/io/github/classgraph/concurrency/AutoCloseableExecutorService.java +++ b/src/main/java/nonapi/io/github/classgraph/concurrency/AutoCloseableExecutorService.java @@ -103,13 +103,13 @@ public void afterExecute(final Runnable runnable, final Throwable throwable) { /** * Run a {@link Runnable} with a specific {@link ClassLoader}. - * - * @param runnable - * the {@link Runnable}. + * * @param classLoader * the {@link ClassLoader}. + * @param runnable + * the {@link Runnable}. */ - public static void runWithClassLoader(final Runnable runnable, final ClassLoader classLoader) { + public static void runWithClassLoader(final ClassLoader classLoader, final Runnable runnable) { // Run the Runnable in the system classloader try (AutoCloseableExecutorService executorService = new AutoCloseableExecutorService(1, // Create new thread with specific classloader From afd905a18955e743c970c8f938ab1dcba101f1e0 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 19 Oct 2019 11:39:58 -0600 Subject: [PATCH 0402/1778] Use bootstrap classloader for shutdown hook (#376) --- .../java/io/github/classgraph/ClassGraph.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index 6e9dd7c72..5d82b0948 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -93,25 +93,25 @@ public ClassGraph() { // Initialize ScanResult, if this is the first call to ClassGraph constructor if (!scanResultStaticInitRun.getAndSet(true)) { // Get a ref to the bootstrap ClassLoader. - ClassLoader bootstrapClassLoader = ClassGraph.class.getClassLoader(); - for (; bootstrapClassLoader.getParent() != null; bootstrapClassLoader = bootstrapClassLoader - .getParent()) { + ClassLoader rootClassLoader = ClassGraph.class.getClassLoader(); + for (; rootClassLoader.getParent() != null; rootClassLoader = rootClassLoader.getParent()) { // Find root classloader } // Create nonClosedWeakReferences from the system classloader, so there's no chance that // the shutdown hook will hold a ref to the context classloader (#376) + final ClassLoader bootstrapClassLoader = rootClassLoader; AutoCloseableExecutorService.runWithClassLoader(bootstrapClassLoader, new Runnable() { @Override public void run() { ScanResult.staticInit(); + + // Set up shutdown hook, if enabled + if (enableShutdownHook.get()) { + ScanResult.initShutdownHook(bootstrapClassLoader); + } } }); - - // Set up shutdown hook, if enabled - if (enableShutdownHook.get()) { - ScanResult.initShutdownHook(bootstrapClassLoader); - } } } From 2e0074644ab18114a6c41ca8d85d1939d6d73917 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 19 Oct 2019 12:06:38 -0600 Subject: [PATCH 0403/1778] Remove attempt to set context classloader in shutdown hook (#376) --- .../java/io/github/classgraph/ClassGraph.java | 32 +-------- .../java/io/github/classgraph/ScanResult.java | 67 +++++++++---------- .../java/io/github/classgraph/Scanner.java | 3 + .../AutoCloseableExecutorService.java | 47 ------------- .../concurrency/SimpleThreadFactory.java | 45 +------------ 5 files changed, 39 insertions(+), 155 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index 5d82b0948..f3290da54 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -39,7 +39,6 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Pattern; import nonapi.io.github.classgraph.classpath.SystemJarFinder; @@ -80,39 +79,12 @@ public class ClassGraph { /** If non-null, log while scanning. */ private LogNode topLevelLog; - /** If true, add a shutdown hook to close all files and direct byte buffers on shutdown. */ - private static final AtomicBoolean enableShutdownHook = new AtomicBoolean(true); - - /** If true, ScanResult#staticInit() has been run. */ - private static final AtomicBoolean scanResultStaticInitRun = new AtomicBoolean(false); - // ------------------------------------------------------------------------------------------------------------- /** Construct a ClassGraph instance. */ public ClassGraph() { // Initialize ScanResult, if this is the first call to ClassGraph constructor - if (!scanResultStaticInitRun.getAndSet(true)) { - // Get a ref to the bootstrap ClassLoader. - ClassLoader rootClassLoader = ClassGraph.class.getClassLoader(); - for (; rootClassLoader.getParent() != null; rootClassLoader = rootClassLoader.getParent()) { - // Find root classloader - } - - // Create nonClosedWeakReferences from the system classloader, so there's no chance that - // the shutdown hook will hold a ref to the context classloader (#376) - final ClassLoader bootstrapClassLoader = rootClassLoader; - AutoCloseableExecutorService.runWithClassLoader(bootstrapClassLoader, new Runnable() { - @Override - public void run() { - ScanResult.staticInit(); - - // Set up shutdown hook, if enabled - if (enableShutdownHook.get()) { - ScanResult.initShutdownHook(bootstrapClassLoader); - } - } - }); - } + ScanResult.init(); } /** @@ -126,7 +98,7 @@ public void run() { * throughout the lifetime of the VM, such as web application servers. */ public static void disableShutdownHook() { - enableShutdownHook.set(false); + ScanResult.enableShutdownHook.set(false); } /** diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index 9e168d9a2..b8413fa85 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -49,8 +49,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import nonapi.io.github.classgraph.concurrency.AutoCloseableExecutorService; -import nonapi.io.github.classgraph.concurrency.SimpleThreadFactory; import nonapi.io.github.classgraph.fastzipfilereader.NestedJarHandler; import nonapi.io.github.classgraph.json.JSONDeserializer; import nonapi.io.github.classgraph.json.JSONSerializer; @@ -128,7 +126,14 @@ public final class ScanResult implements Closeable, AutoCloseable { * The set of WeakReferences to non-closed ScanResult objects. Uses WeakReferences so that garbage collection is * not blocked. (Bug #233) */ - private static Set> nonClosedWeakReferences; + private static Set> nonClosedWeakReferences = Collections + .newSetFromMap(new ConcurrentHashMap, Boolean>()); + + /** If true, ScanResult#staticInit() has been run. */ + private static final AtomicBoolean initialized = new AtomicBoolean(false); + + /** If true, add a shutdown hook to close all files and direct byte buffers on shutdown. */ + static final AtomicBoolean enableShutdownHook = new AtomicBoolean(true); // ------------------------------------------------------------------------------------------------------------- @@ -195,43 +200,35 @@ public SerializationFormat(final String serializationFormatStr, final ScanSpec s // Shutdown hook init code /** - * Static initialization, called when the ClassGraph class is initialized. Call from - * {@link AutoCloseableExecutorService#runInSystemClassLoader(Runnable)}, so there's no chance that the shutdown - * hook will hold a ref to the context classloader. + * Static initialization (warm up classloading), called when the ClassGraph class is initialized. */ - static void staticInit() { - nonClosedWeakReferences = Collections - .newSetFromMap(new ConcurrentHashMap, Boolean>()); - // 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. Otherwise, the classloader may be - // closed by its own shutdown hook before ClassGraph's shutdown hook can run, and classloading can - // fail, which will throw an exception and leave resources open (#331). - // 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); + static void init() { + 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. Otherwise, the classloader may be + // closed by its own shutdown hook before ClassGraph's shutdown hook can run, and classloading can + // fail, which will throw an exception and leave resources open (#331). + // 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); + } } /** - * Initialize the shutdown hook. - * - * @param systemClassLoader - * the system classloader + * Add a shutdown hook, if this is the first time ClassGraph has been run, and if the shutdown hook is enabled + * for the current context classloader. */ - static void initShutdownHook(final ClassLoader systemClassLoader) { - // Create shutdown hook thread to close all open/mapped DirectByteBuffers on shutdown - final Thread hookThread = new Thread( - SimpleThreadFactory.newThreadGroupWithRootAsParent("ClassGraph-shutdown-thread-group"), - new Runnable() { - @Override - public void run() { - ScanResult.closeAll(); - } - }, "ClassGraph-shutdown-thread"); - // Set shutdown hook thread classloader to system classloader, dropping ref to context classloader - hookThread.setContextClassLoader(systemClassLoader); - - // Add runtime shutdown hook to remove temporary files on Ctrl-C or System.exit(). - Runtime.getRuntime().addShutdownHook(hookThread); + static void addShutdownHook() { + if (enableShutdownHook.get()) { + // Add runtime shutdown hook to remove temporary files on Ctrl-C or System.exit(). + Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { + @Override + public void run() { + // Close all open/mapped DirectByteBuffers on shutdown + ScanResult.closeAll(); + } + }, "ClassGraph-shutdown-hook")); + } } /** Close all {@link ScanResult} instances that have not yet been closed. */ diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index efd246f60..3396db9c9 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -213,6 +213,9 @@ class Scanner implements Callable { } } } + + // Add a shutdown hook, if necessary + ScanResult.addShutdownHook(); } // ------------------------------------------------------------------------------------------------------------- diff --git a/src/main/java/nonapi/io/github/classgraph/concurrency/AutoCloseableExecutorService.java b/src/main/java/nonapi/io/github/classgraph/concurrency/AutoCloseableExecutorService.java index 80e27b67e..d04119f94 100644 --- a/src/main/java/nonapi/io/github/classgraph/concurrency/AutoCloseableExecutorService.java +++ b/src/main/java/nonapi/io/github/classgraph/concurrency/AutoCloseableExecutorService.java @@ -32,7 +32,6 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.Semaphore; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; @@ -54,19 +53,6 @@ public AutoCloseableExecutorService(final int numThreads) { new SimpleThreadFactory("ClassGraph-worker-", true)); } - /** - * A ThreadPoolExecutor that can be used in a try-with-resources block. - * - * @param numThreads - * The number of threads to allocate. - * @param threadContextClassLoader - * The context classloader to use for new threads, or null to use the caller's classloader - */ - public AutoCloseableExecutorService(final int numThreads, final ClassLoader threadContextClassLoader) { - super(numThreads, numThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(), - new SimpleThreadFactory("ClassGraph-worker-", true, threadContextClassLoader)); - } - /** * Catch exceptions from both submit() and execute(), and call {@link InterruptionChecker#interrupt()} to * interrupt all threads. @@ -101,39 +87,6 @@ public void afterExecute(final Runnable runnable, final Throwable throwable) { } } - /** - * Run a {@link Runnable} with a specific {@link ClassLoader}. - * - * @param classLoader - * the {@link ClassLoader}. - * @param runnable - * the {@link Runnable}. - */ - public static void runWithClassLoader(final ClassLoader classLoader, final Runnable runnable) { - // Run the Runnable in the system classloader - try (AutoCloseableExecutorService executorService = new AutoCloseableExecutorService(1, - // Create new thread with specific classloader - classLoader)) { - final Semaphore wait = new Semaphore(0); - executorService.execute(new Runnable() { - @Override - public void run() { - // Run the Runnable - runnable.run(); - // Allow code below to continue - wait.release(); - } - }); - try { - // Wait for the above thread to finish running - wait.acquire(); - } catch (final InterruptedException e) { - // Should not happen - throw new RuntimeException(e); - } - } - } - /** Shut down thread pool on close(). */ @Override public void close() { 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 18a0fc78d..567bd918e 100644 --- a/src/main/java/nonapi/io/github/classgraph/concurrency/SimpleThreadFactory.java +++ b/src/main/java/nonapi/io/github/classgraph/concurrency/SimpleThreadFactory.java @@ -46,9 +46,6 @@ public class SimpleThreadFactory implements java.util.concurrent.ThreadFactory { /** Whether to set daemon mode. */ private final boolean daemon; - /** The context classloader to use for new threads, or null to use the caller's classloader. */ - private final ClassLoader threadContextClassLoader; - /** * Constructor. * @@ -60,41 +57,6 @@ public class SimpleThreadFactory implements java.util.concurrent.ThreadFactory { SimpleThreadFactory(final String threadNamePrefix, final boolean daemon) { this.threadNamePrefix = threadNamePrefix; this.daemon = daemon; - this.threadContextClassLoader = null; - } - - /** - * Constructor. - * - * @param threadNamePrefix - * prefix for created threads. - * @param daemon - * create daemon threads? - * @param threadContextClassLoader - * The context classloader to use for new threads, or null to use the caller's classloader - */ - SimpleThreadFactory(final String threadNamePrefix, final boolean daemon, - final ClassLoader threadContextClassLoader) { - this.threadNamePrefix = threadNamePrefix; - this.daemon = daemon; - this.threadContextClassLoader = threadContextClassLoader; - } - - /** - * Create a new {@link ThreadGroup} with the root {@link ThreadGroup} as its parent. - * - * @param threadGroupName - * the thread group name - * @return the {@link ThreadGroup}. - */ - public static ThreadGroup newThreadGroupWithRootAsParent(final String threadGroupName) { - ThreadGroup parentThreadGroup = Thread.currentThread().getThreadGroup(); - for (; parentThreadGroup.getParent() != null; parentThreadGroup = parentThreadGroup.getParent()) { - // If context classloader is overridden, find system (toplevel) thread group, and use this - // as the parent thread group, in order to drop any references to the container's context - // classloader, so that it may be garbage collected if the application is unloaded (#376). - } - return new ThreadGroup(parentThreadGroup, threadGroupName); } /** @@ -106,11 +68,8 @@ public static ThreadGroup newThreadGroupWithRootAsParent(final String threadGrou */ @Override public Thread newThread(final Runnable runnable) { - final Thread thread = new Thread(newThreadGroupWithRootAsParent(threadNamePrefix + "thread-group"), - runnable, threadNamePrefix + threadIdx.getAndIncrement()); - if (threadContextClassLoader != null) { - thread.setContextClassLoader(threadContextClassLoader); - } + final Thread thread = new Thread(new ThreadGroup("ClassGraph-thread-group"), runnable, + threadNamePrefix + threadIdx.getAndIncrement()); thread.setDaemon(daemon); return thread; } From 4a154f576421e3076f9a3e47e242026b480f7c6d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 19 Oct 2019 12:31:48 -0600 Subject: [PATCH 0404/1778] Disable shutdown hooks for tomcat (#376) --- .../java/io/github/classgraph/Scanner.java | 4 +++- .../ClassLoaderHandlerRegistry.java | 9 ++++++++ .../TomcatWebappClassLoaderBaseHandler.java | 9 ++++++++ .../classpath/ClassLoaderOrder.java | 23 +++++++++++++++++++ .../classgraph/classpath/ClasspathFinder.java | 17 ++++++++++++++ 5 files changed, 61 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 3396db9c9..93de3d038 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -215,7 +215,9 @@ class Scanner implements Callable { } // Add a shutdown hook, if necessary - ScanResult.addShutdownHook(); + if (!this.classpathFinder.manualLifecycle()) { + ScanResult.addShutdownHook(); + } } // ------------------------------------------------------------------------------------------------------------- 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 87a839636..c7fa0a186 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java @@ -132,6 +132,9 @@ public static class ClassLoaderHandlerRegistryEntry { /** The ClassLoaderHandler class. */ public final Class classLoaderHandlerClass; + /** If true, don't add shutdown hooks, the {@link ClassLoaderHandler} will manage lifecycle itself. */ + public boolean manualLifecycle; + /** * Constructor. * @@ -160,6 +163,12 @@ private ClassLoaderHandlerRegistryEntry(final Class classLoaderClass) { return "org.apache.catalina.loader.WebappClassLoaderBase".equals(classLoaderClass.getName()); } + /** + * Shutdown hooks should not be used in Tomcat, because this leads to ClassLoader reference leaks (#376). + * + * @return true. + */ + public static boolean manualLifecycle() { + return true; + } + /** * Find the {@link ClassLoader} delegation order for a {@link ClassLoader}. * 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 b80263cce..810c08337 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderOrder.java @@ -65,10 +65,15 @@ public class ClassLoaderOrder { */ private final Set allParentClassLoaders = new HashSet<>(); + /** If true, don't add a shutdown hook -- a {@link ClassLoaderHandler} will manage the lifecycle instead. */ + private boolean manualLifecycle; + /** A map from {@link ClassLoader} to {@link ClassLoaderHandlerRegistryEntry}. */ private final Map classLoaderToClassLoaderHandlerRegistryEntry = // new HashMap<>(); + // ------------------------------------------------------------------------------------------------------------- + /** * Get the {@link ClassLoader} order. * @@ -88,6 +93,16 @@ public Set getAllParentClassLoaders() { return allParentClassLoaders; } + /** + * Checks if the lifecycle is manually managed by a {@link ClassLoaderHandler} (preventing the addition of a + * shutdown hook). + * + * @return true, if lifecycle is manual. + */ + public boolean manualLifecycle() { + return manualLifecycle; + } + /** * Find the {@link ClassLoaderHandler} that can handle a given {@link ClassLoader} instance. * @@ -138,6 +153,10 @@ public void add(final ClassLoader classLoader) { final ClassLoaderHandlerRegistryEntry entry = getRegistryEntry(classLoader); if (entry != null) { classLoaderOrder.add(new SimpleEntry<>(classLoader, entry)); + // Check if the ClassLoaderHandler wants to manually handle lifecycle + if (entry.manualLifecycle) { + manualLifecycle = true; + } } } } @@ -163,6 +182,10 @@ public void delegateTo(final ClassLoader classLoader, final boolean isParent) { if (delegatedTo.add(classLoader)) { // Find ClassLoaderHandlerRegistryEntry for this classloader final ClassLoaderHandlerRegistryEntry entry = getRegistryEntry(classLoader); + // Check if the ClassLoaderHandler wants to manually handle lifecycle + if (entry.manualLifecycle) { + manualLifecycle = true; + } // Delegate to this classloader, by recursing to that classloader to get its classloader order entry.findClassLoaderOrder(classLoader, this); } 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 65ef8060b..cff9efb21 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java @@ -33,6 +33,7 @@ import java.util.Map.Entry; import java.util.Set; +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.scanspec.ScanSpec; @@ -55,6 +56,9 @@ public class ClasspathFinder { */ private ClassLoader[] classLoaderOrderRespectingParentDelegation; + /** If true, don't add a shutdown hook -- a {@link ClassLoaderHandler} will manage the lifecycle instead. */ + private boolean manualLifecycle; + // ------------------------------------------------------------------------------------------------------------- /** @@ -84,6 +88,16 @@ public ClassLoader[] getClassLoaderOrderRespectingParentDelegation() { return classLoaderOrderRespectingParentDelegation; } + /** + * Checks if the lifecycle is manually managed by a {@link ClassLoaderHandler} (preventing the addition of a + * shutdown hook). + * + * @return true, if lifecycle is manual. + */ + public boolean manualLifecycle() { + return manualLifecycle; + } + // ------------------------------------------------------------------------------------------------------------- /** @@ -211,6 +225,9 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { // order, since this is not the default (issue #267). classLoaderOrderRespectingParentDelegation = finalClassLoaderOrder.toArray(new ClassLoader[0]); + // Check whether to add a shutdown hook + manualLifecycle = classLoaderOrder.manualLifecycle(); + // Get classpath elements from java.class.path, but don't add them if the element is in an ignored // parent classloader and not in a child classloader (and don't use java.class.path at all if // overrideClassLoaders is true or overrideClasspath is set) From 5dcfb88d793e04fe83d9d040f6d66d8598e4808b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 19 Oct 2019 12:49:03 -0600 Subject: [PATCH 0405/1778] Pass log through to classpath delegation order methods --- .../AntClassLoaderHandler.java | 14 +++++---- .../ClassLoaderHandlerRegistry.java | 9 ++++-- .../EquinoxClassLoaderHandler.java | 14 +++++---- ...quinoxContextFinderClassLoaderHandler.java | 16 ++++++---- .../FallbackClassLoaderHandler.java | 14 +++++---- .../FelixClassLoaderHandler.java | 14 +++++---- .../JBossClassLoaderHandler.java | 14 +++++---- .../JPMSClassLoaderHandler.java | 14 +++++---- .../OSGiDefaultClassLoaderHandler.java | 14 +++++---- ...DelegationOrderTestClassLoaderHandler.java | 14 +++++---- ...assWorldsClassRealmClassLoaderHandler.java | 19 +++++++----- .../SpringBootRestartClassLoaderHandler.java | 18 ++++++----- .../TomcatWebappClassLoaderBaseHandler.java | 30 +++++++++++++++---- .../URLClassLoaderHandler.java | 14 +++++---- .../WeblogicClassLoaderHandler.java | 14 +++++---- .../WebsphereLibertyClassLoaderHandler.java | 14 +++++---- ...ebsphereTraditionalClassLoaderHandler.java | 14 +++++---- .../classpath/ClassLoaderOrder.java | 19 ++++++++---- .../classgraph/classpath/ClasspathFinder.java | 20 +++++++------ 19 files changed, 195 insertions(+), 104 deletions(-) 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 33b80e0b6..7a3ec71da 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/AntClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/AntClassLoaderHandler.java @@ -45,9 +45,11 @@ private AntClassLoaderHandler() { * * @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) { + public static boolean canHandle(final Class classLoaderClass, final LogNode log) { return "org.apache.tools.ant.AntClassLoader".equals(classLoaderClass.getName()); } @@ -58,11 +60,13 @@ public static boolean canHandle(final Class classLoaderClass) { * 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) { - classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true); - classLoaderOrder.add(classLoader); + public static void findClassLoaderOrder(final ClassLoader classLoader, final ClassLoaderOrder classLoaderOrder, + final LogNode log) { + classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true, log); + classLoaderOrder.add(classLoader, log); } /** 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 c7fa0a186..7f83dfde9 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java @@ -144,7 +144,8 @@ public static class ClassLoaderHandlerRegistryEntry { private ClassLoaderHandlerRegistryEntry(final Class classLoaderHandlerClass) { this.classLoaderHandlerClass = classLoaderHandlerClass; try { - canHandleMethod = classLoaderHandlerClass.getDeclaredMethod("canHandle", Class.class); + canHandleMethod = classLoaderHandlerClass.getDeclaredMethod("canHandle", Class.class, + LogNode.class); } catch (final Exception e) { throw new RuntimeException( "Could not find canHandle method for " + classLoaderHandlerClass.getName(), e); @@ -176,11 +177,13 @@ private ClassLoaderHandlerRegistryEntry(final Class classLoader) { + public boolean canHandle(final Class classLoader, final LogNode log) { try { - return (boolean) canHandleMethod.invoke(null, classLoader); + return (boolean) canHandleMethod.invoke(null, classLoader, log); } catch (final Throwable e) { throw new RuntimeException( "Exception while calling canHandle for " + classLoaderHandlerClass.getName(), e); 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 79bbb18dc..5517fd6b4 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxClassLoaderHandler.java @@ -60,9 +60,11 @@ private EquinoxClassLoaderHandler() { * * @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) { + public static boolean canHandle(final Class classLoaderClass, final LogNode log) { return "org.eclipse.osgi.internal.loader.EquinoxClassLoader".equals(classLoaderClass.getName()); } @@ -73,11 +75,13 @@ public static boolean canHandle(final Class classLoaderClass) { * 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) { - classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true); - classLoaderOrder.add(classLoader); + public static void findClassLoaderOrder(final ClassLoader classLoader, final ClassLoaderOrder classLoaderOrder, + final LogNode log) { + classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true, log); + classLoaderOrder.add(classLoader, log); } /** 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 8d204f3ad..50bf9d8e0 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxContextFinderClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/EquinoxContextFinderClassLoaderHandler.java @@ -45,9 +45,11 @@ private EquinoxContextFinderClassLoaderHandler() { * * @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) { + public static boolean canHandle(final Class classLoaderClass, final LogNode log) { return "org.eclipse.osgi.internal.framework.ContextFinder".equals(classLoaderClass.getName()); } @@ -58,14 +60,16 @@ public static boolean canHandle(final Class classLoaderClass) { * 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) { + public static void findClassLoaderOrder(final ClassLoader classLoader, final ClassLoaderOrder classLoaderOrder, + final LogNode log) { classLoaderOrder.delegateTo( (ClassLoader) ReflectionUtils.getFieldVal(classLoader, "parentContextClassLoader", false), - /* isParent = */ true); - classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true); - classLoaderOrder.add(classLoader); + /* 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 38155d80f..1b222487c 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FallbackClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FallbackClassLoaderHandler.java @@ -47,9 +47,11 @@ private FallbackClassLoaderHandler() { * * @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) { + public static boolean canHandle(final Class classLoaderClass, final LogNode log) { // This is the fallback handler, it handles anything return true; } @@ -61,11 +63,13 @@ public static boolean canHandle(final Class classLoaderClass) { * 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) { - classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true); - classLoaderOrder.add(classLoader); + public static void findClassLoaderOrder(final ClassLoader classLoader, final ClassLoaderOrder classLoaderOrder, + final LogNode log) { + classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true, log); + classLoaderOrder.add(classLoader, log); } /** 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 24b40c31f..88e325fe7 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FelixClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FelixClassLoaderHandler.java @@ -57,9 +57,11 @@ private FelixClassLoaderHandler() { * * @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) { + 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" @@ -73,11 +75,13 @@ public static boolean canHandle(final Class classLoaderClass) { * 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) { - classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true); - classLoaderOrder.add(classLoader); + public static void findClassLoaderOrder(final ClassLoader classLoader, final ClassLoaderOrder classLoaderOrder, + final LogNode log) { + classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true, log); + classLoaderOrder.add(classLoader, 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 e186bbd5a..8971ea014 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JBossClassLoaderHandler.java @@ -60,9 +60,11 @@ private JBossClassLoaderHandler() { * * @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) { + public static boolean canHandle(final Class classLoaderClass, final LogNode log) { return "org.jboss.modules.ModuleClassLoader".equals(classLoaderClass.getName()); } @@ -73,11 +75,13 @@ public static boolean canHandle(final Class classLoaderClass) { * 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) { - classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true); - classLoaderOrder.add(classLoader); + public static void findClassLoaderOrder(final ClassLoader classLoader, final ClassLoaderOrder classLoaderOrder, + final LogNode log) { + classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true, log); + classLoaderOrder.add(classLoader, 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 f60e2c4f1..3c4802881 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JPMSClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/JPMSClassLoaderHandler.java @@ -47,9 +47,11 @@ private JPMSClassLoaderHandler() { * * @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) { + 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()); } @@ -61,14 +63,16 @@ public static boolean canHandle(final Class classLoaderClass) { * 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) { + 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); - classLoaderOrder.add(classLoader); + classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true, log); + classLoaderOrder.add(classLoader, 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 69df3b4a2..dc6a74f54 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/OSGiDefaultClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/OSGiDefaultClassLoaderHandler.java @@ -51,9 +51,11 @@ private OSGiDefaultClassLoaderHandler() { * * @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) { + public static boolean canHandle(final Class classLoaderClass, final LogNode log) { return "org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader".equals(classLoaderClass.getName()); } @@ -64,11 +66,13 @@ public static boolean canHandle(final Class classLoaderClass) { * 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) { - classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true); - classLoaderOrder.add(classLoader); + public static void findClassLoaderOrder(final ClassLoader classLoader, final ClassLoaderOrder classLoaderOrder, + final LogNode log) { + classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true, log); + classLoaderOrder.add(classLoader, 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 053bcf2b5..9d26a0fcc 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ParentLastDelegationOrderTestClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ParentLastDelegationOrderTestClassLoaderHandler.java @@ -45,9 +45,11 @@ private ParentLastDelegationOrderTestClassLoaderHandler() { * * @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) { + public static boolean canHandle(final Class classLoaderClass, final LogNode log) { return "io.github.classgraph.issues.issue267.FakeRestartClassLoader".equals(classLoaderClass.getName()); } @@ -58,12 +60,14 @@ public static boolean canHandle(final Class classLoaderClass) { * 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) { + public static void findClassLoaderOrder(final ClassLoader classLoader, final ClassLoaderOrder classLoaderOrder, + final LogNode log) { // Add self first, then delegate to parent - classLoaderOrder.add(classLoader); - classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true); + classLoaderOrder.add(classLoader, log); + classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true, 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 df42dd3b7..4714f16c2 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/PlexusClassWorldsClassRealmClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/PlexusClassWorldsClassRealmClassLoaderHandler.java @@ -51,9 +51,11 @@ private PlexusClassWorldsClassRealmClassLoaderHandler() { * * @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) { + public static boolean canHandle(final Class classLoaderClass, final LogNode log) { return "org.codehaus.plexus.classworlds.realm.ClassRealm".equals(classLoaderClass.getName()); } @@ -85,8 +87,11 @@ private static boolean isParentFirstStrategy(final ClassLoader classRealmInstanc * 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 classRealm, final ClassLoaderOrder classLoaderOrder) { + 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); if (foreignImports != null) { @@ -96,7 +101,7 @@ public static void findClassLoaderOrder(final ClassLoader classRealm, final Clas final ClassLoader foreignImportClassLoader = (ClassLoader) ReflectionUtils.invokeMethod(entry, "getClassLoader", false); // Treat foreign import classloader as if it is a parent classloader - classLoaderOrder.delegateTo(foreignImportClassLoader, /* isParent = */ true); + classLoaderOrder.delegateTo(foreignImportClassLoader, /* isParent = */ true, log); } } @@ -106,7 +111,7 @@ public static void findClassLoaderOrder(final ClassLoader classRealm, final Clas // From ClassRealm#loadClassFromSelf(String) -> findLoadedClass(String) for self-first strategy if (!isParentFirst) { // Add self before parent - classLoaderOrder.add(classRealm); + classLoaderOrder.add(classRealm, log); } // From ClassRealm#loadClassFromParent -- N.B. we are ignoring parentImports, which is used to filter @@ -114,13 +119,13 @@ public static void findClassLoaderOrder(final ClassLoader classRealm, final Clas // able to load classes by name that are not imported from the parent classloader). final ClassLoader parentClassLoader = (ClassLoader) ReflectionUtils.invokeMethod(classRealm, "getParentClassLoader", false); - classLoaderOrder.delegateTo(parentClassLoader, /* isParent = */ true); - classLoaderOrder.delegateTo(classRealm.getParent(), /* isParent = */ true); + classLoaderOrder.delegateTo(parentClassLoader, /* isParent = */ true, log); + classLoaderOrder.delegateTo(classRealm.getParent(), /* isParent = */ true, log); // From ClassRealm#loadClassFromSelf(String) -> findLoadedClass(String) for parent-first strategy if (isParentFirst) { // Add self after parent - classLoaderOrder.add(classRealm); + classLoaderOrder.add(classRealm, log); } } 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 be69d8ae2..0fabc1dc8 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/SpringBootRestartClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/SpringBootRestartClassLoaderHandler.java @@ -51,9 +51,11 @@ private SpringBootRestartClassLoaderHandler() { * * @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) { + public static boolean canHandle(final Class classLoaderClass, final LogNode log) { return "org.springframework.boot.devtools.restart.classloader.RestartClassLoader" .equals(classLoaderClass.getName()); } @@ -65,18 +67,20 @@ public static boolean canHandle(final Class classLoaderClass) { * 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) { + public static void findClassLoaderOrder(final ClassLoader classLoader, final ClassLoaderOrder classLoaderOrder, + final LogNode log) { // The Restart classloader is a parent-last classloader, so need to delegate to the context // classloader(s) before the parent. - classLoaderOrder.delegateTo(Thread.currentThread().getContextClassLoader(), /* isParent = */ false); - classLoaderOrder.delegateTo(ClassGraph.class.getClassLoader(), /* isParent = */ false); + classLoaderOrder.delegateTo(Thread.currentThread().getContextClassLoader(), /* isParent = */ false, log); + classLoaderOrder.delegateTo(ClassGraph.class.getClassLoader(), /* isParent = */ false, log); // Also delegate to system classloader, in case the above two are actually the same as `classLoader` - classLoaderOrder.delegateTo(ClassLoader.getSystemClassLoader(), /* isParent = */ true); + classLoaderOrder.delegateTo(ClassLoader.getSystemClassLoader(), /* isParent = */ true, log); // Finally delegate to the parent of the RestartClassLoader, in case that is different from // SystemClassLoader - classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true); + classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true, 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 77af01b6b..d14f43e41 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java @@ -48,10 +48,26 @@ private TomcatWebappClassLoaderBaseHandler() { * * @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) { - return "org.apache.catalina.loader.WebappClassLoaderBase".equals(classLoaderClass.getName()); + public static boolean canHandle(final Class classLoaderClass, final LogNode log) { + final boolean canHandle = "org.apache.catalina.loader.WebappClassLoaderBase" + .equals(classLoaderClass.getName()); + if (canHandle) { + // manualLifecycle() returns true below, which prevents the addition of a shutdown hook, in order + // to prevent the shutdown hook from holding a reference to the context classloader (#376). + // Instead, use ServletContextListener to close any open ScanResult instances on servlet shutdown. + try { + final Class servletContextListenerClass = Class.forName("javax.servlet.ServletContextListener"); + } catch (final ClassNotFoundException e) { + if (log != null) { + log.log("Cannot find ServletContextListener class, so cannot cleanly shut down ClassGraph"); + } + } + } + return canHandle; } /** @@ -70,11 +86,13 @@ public static boolean manualLifecycle() { * 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) { - classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true); - classLoaderOrder.add(classLoader); + public static void findClassLoaderOrder(final ClassLoader classLoader, final ClassLoaderOrder classLoaderOrder, + final LogNode log) { + classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true, log); + classLoaderOrder.add(classLoader, log); } /** 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 0eccd9ef8..cb3a62795 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/URLClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/URLClassLoaderHandler.java @@ -47,9 +47,11 @@ private URLClassLoaderHandler() { * * @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) { + public static boolean canHandle(final Class classLoaderClass, final LogNode log) { return "java.net.URLClassLoader".equals(classLoaderClass.getName()); } @@ -60,11 +62,13 @@ public static boolean canHandle(final Class classLoaderClass) { * 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) { - classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true); - classLoaderOrder.add(classLoader); + public static void findClassLoaderOrder(final ClassLoader classLoader, final ClassLoaderOrder classLoaderOrder, + final LogNode log) { + classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true, log); + classLoaderOrder.add(classLoader, log); } /** 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 b98e61c55..9e975d8c2 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WeblogicClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WeblogicClassLoaderHandler.java @@ -45,9 +45,11 @@ private WeblogicClassLoaderHandler() { * * @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) { + 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()) @@ -64,11 +66,13 @@ public static boolean canHandle(final Class classLoaderClass) { * 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) { - classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true); - classLoaderOrder.add(classLoader); + public static void findClassLoaderOrder(final ClassLoader classLoader, final ClassLoaderOrder classLoaderOrder, + final LogNode log) { + classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true, log); + classLoaderOrder.add(classLoader, 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 2a0bd02ce..e651c1988 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java @@ -64,9 +64,11 @@ private WebsphereLibertyClassLoaderHandler() { * * @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) { + 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()); } @@ -78,11 +80,13 @@ public static boolean canHandle(final Class classLoaderClass) { * 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) { - classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true); - classLoaderOrder.add(classLoader); + public static void findClassLoaderOrder(final ClassLoader classLoader, final ClassLoaderOrder classLoaderOrder, + final LogNode log) { + classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true, log); + classLoaderOrder.add(classLoader, 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 386e6f7cb..b352ee115 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereTraditionalClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereTraditionalClassLoaderHandler.java @@ -49,9 +49,11 @@ private WebsphereTraditionalClassLoaderHandler() { * * @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) { + 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()); @@ -64,11 +66,13 @@ public static boolean canHandle(final Class classLoaderClass) { * 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) { - classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true); - classLoaderOrder.add(classLoader); + public static void findClassLoaderOrder(final ClassLoader classLoader, final ClassLoaderOrder classLoaderOrder, + final LogNode log) { + classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true, log); + classLoaderOrder.add(classLoader, log); } /** 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 810c08337..680c09321 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.utils.LogNode; /** A class to find all unique classloaders. */ public class ClassLoaderOrder { @@ -108,9 +109,11 @@ public boolean manualLifecycle() { * * @param classLoader * the {@link ClassLoader}. + * @param log + * the log * @return the {@link ClassLoaderHandlerRegistryEntry} for the {@link ClassLoader}. */ - private ClassLoaderHandlerRegistryEntry getRegistryEntry(final ClassLoader 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 @@ -119,7 +122,7 @@ private ClassLoaderHandlerRegistryEntry getRegistryEntry(final ClassLoader class currClassLoaderClass = currClassLoaderClass.getSuperclass()) { // Find a ClassLoaderHandler that can handle the ClassLoader for (final ClassLoaderHandlerRegistryEntry ent : ClassLoaderHandlerRegistry.CLASS_LOADER_HANDLERS) { - if (ent.canHandle(currClassLoaderClass)) { + if (ent.canHandle(currClassLoaderClass, log)) { // This ClassLoaderHandler can handle the ClassLoader class, or one of its superclasses entry = ent; break; @@ -144,13 +147,15 @@ private ClassLoaderHandlerRegistryEntry getRegistryEntry(final ClassLoader class * * @param classLoader * the class loader + * @param log + * the log */ - public void add(final ClassLoader classLoader) { + public void add(final ClassLoader classLoader, final LogNode log) { if (classLoader == null) { return; } if (added.add(classLoader)) { - final ClassLoaderHandlerRegistryEntry entry = getRegistryEntry(classLoader); + final ClassLoaderHandlerRegistryEntry entry = getRegistryEntry(classLoader, log); if (entry != null) { classLoaderOrder.add(new SimpleEntry<>(classLoader, entry)); // Check if the ClassLoaderHandler wants to manually handle lifecycle @@ -168,8 +173,10 @@ public void add(final ClassLoader classLoader) { * the class loader * @param isParent * true if this is a parent of another classloader + * @param log + * the log */ - public void delegateTo(final ClassLoader classLoader, final boolean isParent) { + public void delegateTo(final ClassLoader classLoader, final boolean isParent, final LogNode log) { if (classLoader == null) { return; } @@ -181,7 +188,7 @@ public void delegateTo(final ClassLoader classLoader, final boolean isParent) { } if (delegatedTo.add(classLoader)) { // Find ClassLoaderHandlerRegistryEntry for this classloader - final ClassLoaderHandlerRegistryEntry entry = getRegistryEntry(classLoader); + final ClassLoaderHandlerRegistryEntry entry = getRegistryEntry(classLoader, log); // Check if the ClassLoaderHandler wants to manually handle lifecycle if (entry.manualLifecycle) { manualLifecycle = true; 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 cff9efb21..c88117dd5 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java @@ -178,13 +178,15 @@ 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 ClassLoader[] origClassLoaderOrder = scanSpec.overrideClassLoaders != null ? scanSpec.overrideClassLoaders.toArray(new ClassLoader[0]) : contextClassLoaders; if (origClassLoaderOrder != null) { for (final ClassLoader classLoader : origClassLoaderOrder) { - classLoaderOrder.delegateTo(classLoader, /* isParent = */ false); + classLoaderOrder.delegateTo(classLoader, /* isParent = */ false, classloaderOrderLog); } } @@ -192,8 +194,8 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { final Set allParentClassLoaders = classLoaderOrder.getAllParentClassLoaders(); // Get the classpath URLs from each ClassLoader - final LogNode classloaderOrderLog = classpathFinderLog == null ? null - : classpathFinderLog.log("Obtaining URLs from classloaders in delegation order:"); + 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 .getClassLoaderOrder()) { @@ -203,20 +205,20 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { if (scanSpec.ignoreParentClassLoaders && allParentClassLoaders.contains(classLoader)) { // If this is a parent and parent classloaders are being ignored, add classpath entries // to ignoredClasspathOrder - final LogNode classloaderURLLog = classloaderOrderLog == null ? null - : classloaderOrderLog + final LogNode classloaderHandlerLog = classloaderURLLog == null ? null + : classloaderURLLog .log("Ignoring parent classloader " + classLoader + ", normally handled by " + classLoaderHandlerRegistryEntry.classLoaderHandlerClass.getName()); classLoaderHandlerRegistryEntry.findClasspathOrder(classLoader, ignoredClasspathOrder, scanSpec, - classloaderURLLog); + classloaderHandlerLog); } else { // Otherwise add classpath entries to classpathOrder, and add the classloader to the // final classloader ordering - final LogNode classloaderURLLog = classloaderOrderLog == null ? null - : classloaderOrderLog.log("Classloader " + classLoader + " is handled by " + final LogNode classloaderHandlerLog = classloaderURLLog == null ? null + : classloaderURLLog.log("Classloader " + classLoader + " is handled by " + classLoaderHandlerRegistryEntry.classLoaderHandlerClass.getName()); classLoaderHandlerRegistryEntry.findClasspathOrder(classLoader, classpathOrder, scanSpec, - classloaderURLLog); + classloaderHandlerLog); finalClassLoaderOrder.add(classLoader); } } From cd4c7c875bd2d56f4510d234def47610e82d3fcb Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 19 Oct 2019 13:10:06 -0600 Subject: [PATCH 0406/1778] Register Tomcat ServletContextListener for clean shutdown (#376) --- pom.xml | 11 +++++ .../TomcatWebappClassLoaderBaseHandler.java | 49 +++++++++++++------ 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/pom.xml b/pom.xml index 2cd592383..be9d3757d 100644 --- a/pom.xml +++ b/pom.xml @@ -123,6 +123,9 @@ 1.0.2.Final test + + + @@ -132,6 +135,14 @@ 2.2.300 provided + + + + org.apache.tomcat + tomcat-catalina + 9.0.27 + provided + 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 d14f43e41..d4b6e596f 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,11 @@ import java.io.File; import java.util.List; +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; +import javax.servlet.annotation.WebListener; + +import io.github.classgraph.ScanResult; import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; import nonapi.io.github.classgraph.scanspec.ScanSpec; @@ -53,25 +58,12 @@ private TomcatWebappClassLoaderBaseHandler() { * @return true if this {@link ClassLoaderHandler} can handle the {@link ClassLoader}. */ public static boolean canHandle(final Class classLoaderClass, final LogNode log) { - final boolean canHandle = "org.apache.catalina.loader.WebappClassLoaderBase" - .equals(classLoaderClass.getName()); - if (canHandle) { - // manualLifecycle() returns true below, which prevents the addition of a shutdown hook, in order - // to prevent the shutdown hook from holding a reference to the context classloader (#376). - // Instead, use ServletContextListener to close any open ScanResult instances on servlet shutdown. - try { - final Class servletContextListenerClass = Class.forName("javax.servlet.ServletContextListener"); - } catch (final ClassNotFoundException e) { - if (log != null) { - log.log("Cannot find ServletContextListener class, so cannot cleanly shut down ClassGraph"); - } - } - } - return canHandle; + return "org.apache.catalina.loader.WebappClassLoaderBase".equals(classLoaderClass.getName()); } /** * Shutdown hooks should not be used in Tomcat, because this leads to ClassLoader reference leaks (#376). + * Instead, {@link TomcatLifeCycleListener} is registered below. * * @return true. */ @@ -79,6 +71,33 @@ public static boolean manualLifecycle() { return true; } + /** + * Register a {@link WebListener} to respond to Tomcat servlet context shutdown. + */ + @WebListener + public static class TomcatLifeCycleListener implements ServletContextListener { + /** + * Context initialized. + * + * @param event + * the event + */ + public void contextInitialized(ServletContextEvent event) { + // Do nothing + } + + /** + * Context destroyed. + * + * @param event + * the event + */ + public void contextDestroyed(ServletContextEvent event) { + // Cleanly close down any open {@link ScanResult} instances. + ScanResult.closeAll(); + } + } + /** * Find the {@link ClassLoader} delegation order for a {@link ClassLoader}. * From 246593f3d377eb844d3e41e8644b1ff959652e17 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 19 Oct 2019 13:16:41 -0600 Subject: [PATCH 0407/1778] Fix regression from a few commits ago --- .../classloaderhandler/ClassLoaderHandlerRegistry.java | 9 ++++++--- .../io/github/classgraph/classpath/ClassLoaderOrder.java | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) 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 7f83dfde9..5ad2221d2 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java @@ -152,7 +152,7 @@ private ClassLoaderHandlerRegistryEntry(final Class classLoader, final LogNode log) { * the {@link ClassLoader}. * @param classLoaderOrder * a {@link ClassLoaderOrder} object. + * @param log + * the log */ - public void findClassLoaderOrder(final ClassLoader classLoader, final ClassLoaderOrder classLoaderOrder) { + public void findClassLoaderOrder(final ClassLoader classLoader, final ClassLoaderOrder classLoaderOrder, + final LogNode log) { try { - findClassLoaderOrderMethod.invoke(null, classLoader, classLoaderOrder); + findClassLoaderOrderMethod.invoke(null, classLoader, classLoaderOrder, log); } catch (final Throwable e) { throw new RuntimeException( "Exception while calling findClassLoaderOrder for " + classLoaderHandlerClass.getName(), e); 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 680c09321..1529068bd 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderOrder.java @@ -194,7 +194,7 @@ public void delegateTo(final ClassLoader classLoader, final boolean isParent, fi manualLifecycle = true; } // Delegate to this classloader, by recursing to that classloader to get its classloader order - entry.findClassLoaderOrder(classLoader, this); + entry.findClassLoaderOrder(classLoader, this, log); } } } From f1c9704c8713b7b9b98735aacacf2eb6418035fa Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 19 Oct 2019 13:25:21 -0600 Subject: [PATCH 0408/1778] Update Javadoc (#376) --- .../TomcatWebappClassLoaderBaseHandler.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) 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 d4b6e596f..297b9c1a3 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java @@ -72,7 +72,13 @@ public static boolean manualLifecycle() { } /** - * Register a {@link WebListener} to respond to Tomcat servlet context shutdown. + * Register a {@link WebListener} to respond to Tomcat servlet context shutdown (#376). This creates classfile + * references to the classes {@link WebListener}, {@link ServletContextListener} and + * {@link ServletContextEvent}, however this class {@link TomcatLifeCycleListener} is never actually referenced + * by any other class in ClassGraph (it is only included in the classpath so that Tomcat can locate it using the + * {@link WebListener} annotation). Therefore ClassGraph has only a compile-time ("provides"-scoped) dependency + * on Tomcat to enable Tomcat to find this class, but a ClassNotFound exception should not be thrown by anything + * else that uses ClassGraph. */ @WebListener public static class TomcatLifeCycleListener implements ServletContextListener { From b43e3879cca3a6a24e94776b625cf1517db0b1ee Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 19 Oct 2019 13:32:26 -0600 Subject: [PATCH 0409/1778] Move Tomcat lifecycle code to new package --- .../TomcatWebappClassLoaderBaseHandler.java | 41 +----------- .../lifecycle/TomcatLifeCycleListener.java | 67 +++++++++++++++++++ 2 files changed, 69 insertions(+), 39 deletions(-) create mode 100644 src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/TomcatLifeCycleListener.java 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 297b9c1a3..7344e6579 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java @@ -31,11 +31,7 @@ import java.io.File; import java.util.List; -import javax.servlet.ServletContextEvent; -import javax.servlet.ServletContextListener; -import javax.servlet.annotation.WebListener; - -import io.github.classgraph.ScanResult; +import nonapi.io.github.classgraph.classloaderhandler.lifecycle.TomcatLifeCycleListener; import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; import nonapi.io.github.classgraph.scanspec.ScanSpec; @@ -63,7 +59,7 @@ public static boolean canHandle(final Class classLoaderClass, final LogNode l /** * Shutdown hooks should not be used in Tomcat, because this leads to ClassLoader reference leaks (#376). - * Instead, {@link TomcatLifeCycleListener} is registered below. + * Instead, {@link TomcatLifeCycleListener} listens for servlet disposal. * * @return true. */ @@ -71,39 +67,6 @@ public static boolean manualLifecycle() { return true; } - /** - * Register a {@link WebListener} to respond to Tomcat servlet context shutdown (#376). This creates classfile - * references to the classes {@link WebListener}, {@link ServletContextListener} and - * {@link ServletContextEvent}, however this class {@link TomcatLifeCycleListener} is never actually referenced - * by any other class in ClassGraph (it is only included in the classpath so that Tomcat can locate it using the - * {@link WebListener} annotation). Therefore ClassGraph has only a compile-time ("provides"-scoped) dependency - * on Tomcat to enable Tomcat to find this class, but a ClassNotFound exception should not be thrown by anything - * else that uses ClassGraph. - */ - @WebListener - public static class TomcatLifeCycleListener implements ServletContextListener { - /** - * Context initialized. - * - * @param event - * the event - */ - public void contextInitialized(ServletContextEvent event) { - // Do nothing - } - - /** - * Context destroyed. - * - * @param event - * the event - */ - public void contextDestroyed(ServletContextEvent event) { - // Cleanly close down any open {@link ScanResult} instances. - ScanResult.closeAll(); - } - } - /** * Find the {@link ClassLoader} delegation order for a {@link ClassLoader}. * diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/TomcatLifeCycleListener.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/TomcatLifeCycleListener.java new file mode 100644 index 000000000..f82199c08 --- /dev/null +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/TomcatLifeCycleListener.java @@ -0,0 +1,67 @@ +/* + * 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.classloaderhandler.lifecycle; + +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; +import javax.servlet.annotation.WebListener; + +import io.github.classgraph.ScanResult; + +/** + * Register a {@link WebListener} to respond to Tomcat servlet context shutdown (#376). This creates classfile + * references to the classes {@link WebListener}, {@link ServletContextListener} and {@link ServletContextEvent}, + * however this class {@link TomcatLifeCycleListener} is never actually referenced by any other class in ClassGraph + * (it is only included in the classpath so that Tomcat can locate it using the {@link WebListener} annotation). + * Therefore ClassGraph has only a compile-time ("provides"-scoped) dependency on Tomcat to enable Tomcat to find + * this class, but a ClassNotFound exception should not be thrown by anything else that uses ClassGraph. + */ +@WebListener +public class TomcatLifeCycleListener implements ServletContextListener { + /** + * Context initialized. + * + * @param event + * the event + */ + public void contextInitialized(ServletContextEvent event) { + // Do nothing + } + + /** + * Context destroyed. + * + * @param event + * the event + */ + public void contextDestroyed(ServletContextEvent event) { + // Cleanly close down any open {@link ScanResult} instances. + ScanResult.closeAll(); + } +} \ No newline at end of file From a87bb72d2a427381d3f78cac7c4fae50afcff2f9 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 19 Oct 2019 13:48:52 -0600 Subject: [PATCH 0410/1778] Also add lifecycle management for Spring (#376) --- pom.xml | 26 +++++- .../SpringBootRestartClassLoaderHandler.java | 11 +++ .../lifecycle/SpringLifeCycleListener.java | 79 +++++++++++++++++++ .../lifecycle/TomcatLifeCycleListener.java | 8 +- 4 files changed, 117 insertions(+), 7 deletions(-) create mode 100644 src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/SpringLifeCycleListener.java diff --git a/pom.xml b/pom.xml index be9d3757d..1363db580 100644 --- a/pom.xml +++ b/pom.xml @@ -123,9 +123,9 @@ 1.0.2.Final test - + - + @@ -135,14 +135,32 @@ 2.2.300 provided - - + + org.apache.tomcat tomcat-catalina 9.0.27 provided + + org.springframework + spring-context + 3.0.2.RELEASE + provided + + + org.springframework + spring-core + 5.2.0.RELEASE + provided + + + org.springframework.boot + spring-boot + 1.2.3.RELEASE + provided + 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 0fabc1dc8..747be7e9d 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/SpringBootRestartClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/SpringBootRestartClassLoaderHandler.java @@ -29,6 +29,7 @@ package nonapi.io.github.classgraph.classloaderhandler; import io.github.classgraph.ClassGraph; +import nonapi.io.github.classgraph.classloaderhandler.lifecycle.SpringLifeCycleListener; import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; import nonapi.io.github.classgraph.scanspec.ScanSpec; @@ -60,6 +61,16 @@ public static boolean canHandle(final Class classLoaderClass, final LogNode l .equals(classLoaderClass.getName()); } + /** + * Shutdown hooks should not be used in Spring, because this leads to ClassLoader reference leaks (#376). + * Instead, {@link SpringLifeCycleListener} listens for bean container disposal. + * + * @return true. + */ + public static boolean manualLifecycle() { + return true; + } + /** * Find the {@link ClassLoader} delegation order for a {@link ClassLoader}. * diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/SpringLifeCycleListener.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/SpringLifeCycleListener.java new file mode 100644 index 000000000..957b4e301 --- /dev/null +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/SpringLifeCycleListener.java @@ -0,0 +1,79 @@ +/* + * 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.classloaderhandler.lifecycle; + +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; + +import org.springframework.boot.context.embedded.ServletListenerRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import io.github.classgraph.ScanResult; + +/** + * Register a {@link ServletContextListener} to respond to Spring context shutdown (#376). This creates classfile + * references to Spring, Spring-Boot, and servlet classes, however this class {@link SpringLifeCycleListener} is + * never actually referenced by any other class in ClassGraph (it is only included in the classpath so that Spring + * can locate it using the {@link Configuration} annotation). Therefore ClassGraph has only a compile-time + * ("provides"-scoped) dependency on Spring to enable Spring to find this class, but a ClassNotFound exception + * should not be thrown by anything else that uses ClassGraph. + */ +@Configuration +public class SpringLifeCycleListener { + @Bean + ServletListenerRegistrationBean servletListener() { + final ServletListenerRegistrationBean srb = new ServletListenerRegistrationBean<>(); + srb.setListener(new ServletContextListener() { + /** + * Context initialized. + * + * @param event + * the event + */ + @Override + public void contextInitialized(final ServletContextEvent event) { + // Do nothing + } + + /** + * Context destroyed. + * + * @param event + * the event + */ + @Override + public void contextDestroyed(final ServletContextEvent event) { + // Cleanly close down any open {@link ScanResult} instances. + ScanResult.closeAll(); + } + }); + return srb; + } +} diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/TomcatLifeCycleListener.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/TomcatLifeCycleListener.java index f82199c08..8b9f57148 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/TomcatLifeCycleListener.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/TomcatLifeCycleListener.java @@ -50,8 +50,9 @@ public class TomcatLifeCycleListener implements ServletContextListener { * @param event * the event */ - public void contextInitialized(ServletContextEvent event) { - // Do nothing + @Override + public void contextInitialized(final ServletContextEvent event) { + // } /** @@ -60,7 +61,8 @@ public void contextInitialized(ServletContextEvent event) { * @param event * the event */ - public void contextDestroyed(ServletContextEvent event) { + @Override + public void contextDestroyed(final ServletContextEvent event) { // Cleanly close down any open {@link ScanResult} instances. ScanResult.closeAll(); } From 0ee797aafc51dd9bbb6fdd6e1f9848f788df753f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 19 Oct 2019 13:52:21 -0600 Subject: [PATCH 0411/1778] Clean up code for disabling shutdown hook (#376) --- .../java/io/github/classgraph/Scanner.java | 6 ++---- .../ClassLoaderHandlerRegistry.java | 9 -------- .../SpringBootRestartClassLoaderHandler.java | 11 ---------- .../TomcatWebappClassLoaderBaseHandler.java | 11 ---------- .../lifecycle/SpringLifeCycleListener.java | 3 ++- .../lifecycle/TomcatLifeCycleListener.java | 3 ++- .../classpath/ClassLoaderOrder.java | 21 ------------------- .../classgraph/classpath/ClasspathFinder.java | 17 --------------- 8 files changed, 6 insertions(+), 75 deletions(-) diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 93de3d038..1eca3ec88 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -214,10 +214,8 @@ class Scanner implements Callable { } } - // Add a shutdown hook, if necessary - if (!this.classpathFinder.manualLifecycle()) { - ScanResult.addShutdownHook(); - } + // Add a shutdown hook if necessary + ScanResult.addShutdownHook(); } // ------------------------------------------------------------------------------------------------------------- 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 5ad2221d2..9cd21d0b5 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java @@ -132,9 +132,6 @@ public static class ClassLoaderHandlerRegistryEntry { /** The ClassLoaderHandler class. */ public final Class classLoaderHandlerClass; - /** If true, don't add shutdown hooks, the {@link ClassLoaderHandler} will manage lifecycle itself. */ - public boolean manualLifecycle; - /** * Constructor. * @@ -164,12 +161,6 @@ private ClassLoaderHandlerRegistryEntry(final Class classLoaderClass, final LogNode l .equals(classLoaderClass.getName()); } - /** - * Shutdown hooks should not be used in Spring, because this leads to ClassLoader reference leaks (#376). - * Instead, {@link SpringLifeCycleListener} listens for bean container disposal. - * - * @return true. - */ - public static boolean manualLifecycle() { - return true; - } - /** * Find the {@link ClassLoader} delegation order for a {@link ClassLoader}. * 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 7344e6579..9151ae684 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java @@ -31,7 +31,6 @@ import java.io.File; import java.util.List; -import nonapi.io.github.classgraph.classloaderhandler.lifecycle.TomcatLifeCycleListener; import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; import nonapi.io.github.classgraph.scanspec.ScanSpec; @@ -57,16 +56,6 @@ public static boolean canHandle(final Class classLoaderClass, final LogNode l return "org.apache.catalina.loader.WebappClassLoaderBase".equals(classLoaderClass.getName()); } - /** - * Shutdown hooks should not be used in Tomcat, because this leads to ClassLoader reference leaks (#376). - * Instead, {@link TomcatLifeCycleListener} listens for servlet disposal. - * - * @return true. - */ - public static boolean manualLifecycle() { - return true; - } - /** * Find the {@link ClassLoader} delegation order for a {@link ClassLoader}. * diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/SpringLifeCycleListener.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/SpringLifeCycleListener.java index 957b4e301..961fc05e7 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/SpringLifeCycleListener.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/SpringLifeCycleListener.java @@ -35,6 +35,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; /** @@ -59,7 +60,7 @@ ServletListenerRegistrationBean servletListener() { */ @Override public void contextInitialized(final ServletContextEvent event) { - // Do nothing + ClassGraph.disableShutdownHook(); } /** diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/TomcatLifeCycleListener.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/TomcatLifeCycleListener.java index 8b9f57148..0f1c2bc53 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/TomcatLifeCycleListener.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/TomcatLifeCycleListener.java @@ -32,6 +32,7 @@ import javax.servlet.ServletContextListener; import javax.servlet.annotation.WebListener; +import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; /** @@ -52,7 +53,7 @@ public class TomcatLifeCycleListener implements ServletContextListener { */ @Override public void contextInitialized(final ServletContextEvent event) { - // + ClassGraph.disableShutdownHook(); } /** 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 1529068bd..a06e9dfa2 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderOrder.java @@ -66,9 +66,6 @@ public class ClassLoaderOrder { */ private final Set allParentClassLoaders = new HashSet<>(); - /** If true, don't add a shutdown hook -- a {@link ClassLoaderHandler} will manage the lifecycle instead. */ - private boolean manualLifecycle; - /** A map from {@link ClassLoader} to {@link ClassLoaderHandlerRegistryEntry}. */ private final Map classLoaderToClassLoaderHandlerRegistryEntry = // new HashMap<>(); @@ -94,16 +91,6 @@ public Set getAllParentClassLoaders() { return allParentClassLoaders; } - /** - * Checks if the lifecycle is manually managed by a {@link ClassLoaderHandler} (preventing the addition of a - * shutdown hook). - * - * @return true, if lifecycle is manual. - */ - public boolean manualLifecycle() { - return manualLifecycle; - } - /** * Find the {@link ClassLoaderHandler} that can handle a given {@link ClassLoader} instance. * @@ -158,10 +145,6 @@ public void add(final ClassLoader classLoader, final LogNode log) { final ClassLoaderHandlerRegistryEntry entry = getRegistryEntry(classLoader, log); if (entry != null) { classLoaderOrder.add(new SimpleEntry<>(classLoader, entry)); - // Check if the ClassLoaderHandler wants to manually handle lifecycle - if (entry.manualLifecycle) { - manualLifecycle = true; - } } } } @@ -189,10 +172,6 @@ public void delegateTo(final ClassLoader classLoader, final boolean isParent, fi if (delegatedTo.add(classLoader)) { // Find ClassLoaderHandlerRegistryEntry for this classloader final ClassLoaderHandlerRegistryEntry entry = getRegistryEntry(classLoader, log); - // Check if the ClassLoaderHandler wants to manually handle lifecycle - if (entry.manualLifecycle) { - manualLifecycle = true; - } // Delegate to this classloader, by recursing to that classloader to get its classloader order 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 c88117dd5..9b9af717c 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java @@ -33,7 +33,6 @@ import java.util.Map.Entry; import java.util.Set; -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.scanspec.ScanSpec; @@ -56,9 +55,6 @@ public class ClasspathFinder { */ private ClassLoader[] classLoaderOrderRespectingParentDelegation; - /** If true, don't add a shutdown hook -- a {@link ClassLoaderHandler} will manage the lifecycle instead. */ - private boolean manualLifecycle; - // ------------------------------------------------------------------------------------------------------------- /** @@ -88,16 +84,6 @@ public ClassLoader[] getClassLoaderOrderRespectingParentDelegation() { return classLoaderOrderRespectingParentDelegation; } - /** - * Checks if the lifecycle is manually managed by a {@link ClassLoaderHandler} (preventing the addition of a - * shutdown hook). - * - * @return true, if lifecycle is manual. - */ - public boolean manualLifecycle() { - return manualLifecycle; - } - // ------------------------------------------------------------------------------------------------------------- /** @@ -227,9 +213,6 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { // order, since this is not the default (issue #267). classLoaderOrderRespectingParentDelegation = finalClassLoaderOrder.toArray(new ClassLoader[0]); - // Check whether to add a shutdown hook - manualLifecycle = classLoaderOrder.manualLifecycle(); - // Get classpath elements from java.class.path, but don't add them if the element is in an ignored // parent classloader and not in a child classloader (and don't use java.class.path at all if // overrideClassLoaders is true or overrideClasspath is set) From 61674db21869b96d7eb03de2ffce7cbb602b2211 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 19 Oct 2019 14:15:58 -0600 Subject: [PATCH 0412/1778] Log lifecycle events in Tomcat and Spring (#376) --- .../lifecycle/SpringLifeCycleListener.java | 7 +++++++ .../lifecycle/TomcatLifeCycleListener.java | 7 +++++++ .../java/nonapi/io/github/classgraph/utils/LogNode.java | 3 +-- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/SpringLifeCycleListener.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/SpringLifeCycleListener.java index 961fc05e7..2501eca12 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/SpringLifeCycleListener.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/SpringLifeCycleListener.java @@ -28,6 +28,8 @@ */ package nonapi.io.github.classgraph.classloaderhandler.lifecycle; +import java.util.logging.Logger; + import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; @@ -52,6 +54,9 @@ public class SpringLifeCycleListener { ServletListenerRegistrationBean servletListener() { final ServletListenerRegistrationBean srb = new ServletListenerRegistrationBean<>(); srb.setListener(new ServletContextListener() { + /** The logger. */ + private final Logger log = Logger.getLogger(ClassGraph.class.getName()); + /** * Context initialized. * @@ -60,6 +65,7 @@ ServletListenerRegistrationBean servletListener() { */ @Override public void contextInitialized(final ServletContextEvent event) { + log.info("Spring container detected -- disabling ClassGraph shutdown hook"); ClassGraph.disableShutdownHook(); } @@ -72,6 +78,7 @@ public void contextInitialized(final ServletContextEvent event) { @Override public void contextDestroyed(final ServletContextEvent event) { // Cleanly close down any open {@link ScanResult} instances. + log.info("Closing any remaning open ClassGraph ScanResult instances"); ScanResult.closeAll(); } }); diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/TomcatLifeCycleListener.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/TomcatLifeCycleListener.java index 0f1c2bc53..77edeac26 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/TomcatLifeCycleListener.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/TomcatLifeCycleListener.java @@ -28,6 +28,8 @@ */ package nonapi.io.github.classgraph.classloaderhandler.lifecycle; +import java.util.logging.Logger; + import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.annotation.WebListener; @@ -45,6 +47,9 @@ */ @WebListener public class TomcatLifeCycleListener implements ServletContextListener { + /** The logger. */ + private final Logger log = Logger.getLogger(ClassGraph.class.getName()); + /** * Context initialized. * @@ -53,6 +58,7 @@ public class TomcatLifeCycleListener implements ServletContextListener { */ @Override public void contextInitialized(final ServletContextEvent event) { + log.info("Tomcat container detected -- disabling ClassGraph shutdown hook"); ClassGraph.disableShutdownHook(); } @@ -65,6 +71,7 @@ public void contextInitialized(final ServletContextEvent event) { @Override public void contextDestroyed(final ServletContextEvent event) { // Cleanly close down any open {@link ScanResult} instances. + log.info("Closing any remaning open ClassGraph ScanResult instances"); ScanResult.closeAll(); } } \ No newline at end of file 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 954f0a23e..361f67c57 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/LogNode.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/LogNode.java @@ -49,8 +49,7 @@ * retain a sane order. The order may also be made deterministic by specifying a sort key for log entries. */ public final class LogNode { - - /** The Constant log. */ + /** The logger. */ private static final Logger log = Logger.getLogger(ClassGraph.class.getName()); /** From ebe10501fd0f154871aa623612c8b0cb10fcd2d0 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 19 Oct 2019 14:30:56 -0600 Subject: [PATCH 0413/1778] Add "exports" to module descriptor for lifecycle mgmt classes (#376) --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index 1363db580..1d972273d 100644 --- a/pom.xml +++ b/pom.xml @@ -429,6 +429,7 @@ module io.github.classgraph { exports io.github.classgraph; + exports nonapi.io.github.classgraph.classloaderhandler.lifecycle; requires java.xml; requires jdk.unsupported; From 0f1d390b10578aef5b888acc03f0111b1cf82045 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 19 Oct 2019 14:33:27 -0600 Subject: [PATCH 0414/1778] Reformat --- pom.xml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/pom.xml b/pom.xml index 1d972273d..9143c27fa 100644 --- a/pom.xml +++ b/pom.xml @@ -426,16 +426,14 @@ /** ${project.name}: ${project.description} ( ${project.url} ) */ - module - io.github.classgraph { + module io.github.classgraph { exports io.github.classgraph; - exports nonapi.io.github.classgraph.classloaderhandler.lifecycle; - requires - java.xml; + exports + nonapi.io.github.classgraph.classloaderhandler.lifecycle; + requires java.xml; requires jdk.unsupported; requires java.management; - requires - java.logging; + requires java.logging; } From d0f4a3a0eec7cf8b9bd910e9b36ede16d30ec56d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 19 Oct 2019 15:03:24 -0600 Subject: [PATCH 0415/1778] TomcatLifeCycleListener -> ServletLifeCycleListener (#376) --- pom.xml | 21 +++-------- ...ner.java => ServletLifeCycleListener.java} | 8 ++--- .../lifecycle/SpringLifeCycleListener.java | 36 +------------------ 3 files changed, 10 insertions(+), 55 deletions(-) rename src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/{TomcatLifeCycleListener.java => ServletLifeCycleListener.java} (87%) diff --git a/pom.xml b/pom.xml index 9143c27fa..a7dd58c75 100644 --- a/pom.xml +++ b/pom.xml @@ -136,23 +136,12 @@ provided - + + - org.apache.tomcat - tomcat-catalina - 9.0.27 - provided - - - org.springframework - spring-context - 3.0.2.RELEASE - provided - - - org.springframework - spring-core - 5.2.0.RELEASE + javax.servlet + javax.servlet-api + 3.0.1 provided diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/TomcatLifeCycleListener.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/ServletLifeCycleListener.java similarity index 87% rename from src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/TomcatLifeCycleListener.java rename to src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/ServletLifeCycleListener.java index 77edeac26..a24764c9e 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/TomcatLifeCycleListener.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/ServletLifeCycleListener.java @@ -40,13 +40,13 @@ /** * Register a {@link WebListener} to respond to Tomcat servlet context shutdown (#376). This creates classfile * references to the classes {@link WebListener}, {@link ServletContextListener} and {@link ServletContextEvent}, - * however this class {@link TomcatLifeCycleListener} is never actually referenced by any other class in ClassGraph + * however this class {@link ServletLifeCycleListener} is never actually referenced by any other class in ClassGraph * (it is only included in the classpath so that Tomcat can locate it using the {@link WebListener} annotation). * Therefore ClassGraph has only a compile-time ("provides"-scoped) dependency on Tomcat to enable Tomcat to find * this class, but a ClassNotFound exception should not be thrown by anything else that uses ClassGraph. */ @WebListener -public class TomcatLifeCycleListener implements ServletContextListener { +public class ServletLifeCycleListener implements ServletContextListener { /** The logger. */ private final Logger log = Logger.getLogger(ClassGraph.class.getName()); @@ -58,7 +58,7 @@ public class TomcatLifeCycleListener implements ServletContextListener { */ @Override public void contextInitialized(final ServletContextEvent event) { - log.info("Tomcat container detected -- disabling ClassGraph shutdown hook"); + log.info("Servlet container initialized -- disabling ClassGraph shutdown hook"); ClassGraph.disableShutdownHook(); } @@ -71,7 +71,7 @@ public void contextInitialized(final ServletContextEvent event) { @Override public void contextDestroyed(final ServletContextEvent event) { // Cleanly close down any open {@link ScanResult} instances. - log.info("Closing any remaning open ClassGraph ScanResult instances"); + log.info("Servlet context destroyed -- closing any remaning open ClassGraph ScanResult instances"); ScanResult.closeAll(); } } \ No newline at end of file diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/SpringLifeCycleListener.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/SpringLifeCycleListener.java index 2501eca12..ea884c85e 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/SpringLifeCycleListener.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/SpringLifeCycleListener.java @@ -28,18 +28,12 @@ */ package nonapi.io.github.classgraph.classloaderhandler.lifecycle; -import java.util.logging.Logger; - -import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import org.springframework.boot.context.embedded.ServletListenerRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import io.github.classgraph.ClassGraph; -import io.github.classgraph.ScanResult; - /** * Register a {@link ServletContextListener} to respond to Spring context shutdown (#376). This creates classfile * references to Spring, Spring-Boot, and servlet classes, however this class {@link SpringLifeCycleListener} is @@ -53,35 +47,7 @@ public class SpringLifeCycleListener { @Bean ServletListenerRegistrationBean servletListener() { final ServletListenerRegistrationBean srb = new ServletListenerRegistrationBean<>(); - srb.setListener(new ServletContextListener() { - /** The logger. */ - private final Logger log = Logger.getLogger(ClassGraph.class.getName()); - - /** - * Context initialized. - * - * @param event - * the event - */ - @Override - public void contextInitialized(final ServletContextEvent event) { - log.info("Spring container detected -- disabling ClassGraph shutdown hook"); - ClassGraph.disableShutdownHook(); - } - - /** - * Context destroyed. - * - * @param event - * the event - */ - @Override - public void contextDestroyed(final ServletContextEvent event) { - // Cleanly close down any open {@link ScanResult} instances. - log.info("Closing any remaning open ClassGraph ScanResult instances"); - ScanResult.closeAll(); - } - }); + srb.setListener(new ServletLifeCycleListener()); return srb; } } From 637318846bcdd71362b69c10da6a9f436c1565ae Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 19 Oct 2019 15:06:26 -0600 Subject: [PATCH 0416/1778] Update Javadoc --- .../lifecycle/ServletLifeCycleListener.java | 12 ++++++------ .../lifecycle/SpringLifeCycleListener.java | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/ServletLifeCycleListener.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/ServletLifeCycleListener.java index a24764c9e..5b35d550a 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/ServletLifeCycleListener.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/ServletLifeCycleListener.java @@ -38,12 +38,12 @@ import io.github.classgraph.ScanResult; /** - * Register a {@link WebListener} to respond to Tomcat servlet context shutdown (#376). This creates classfile - * references to the classes {@link WebListener}, {@link ServletContextListener} and {@link ServletContextEvent}, - * however this class {@link ServletLifeCycleListener} is never actually referenced by any other class in ClassGraph - * (it is only included in the classpath so that Tomcat can locate it using the {@link WebListener} annotation). - * Therefore ClassGraph has only a compile-time ("provides"-scoped) dependency on Tomcat to enable Tomcat to find - * this class, but a ClassNotFound exception should not be thrown by anything else that uses ClassGraph. + * Register a {@link WebListener} to respond to servlet context shutdown (#376). This creates classfile references + * to javax.servlet classes, however this class is never actually referenced by any other class in ClassGraph (it is + * only included in the classpath so that the servlet container can locate it using the {@link WebListener} + * annotation). Therefore ClassGraph has only a compile-time ("provides"-scoped) dependency on the servlet container + * to enable the container to find this class, but a ClassNotFound exception should not be thrown by anything else + * that uses ClassGraph. */ @WebListener public class ServletLifeCycleListener implements ServletContextListener { diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/SpringLifeCycleListener.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/SpringLifeCycleListener.java index ea884c85e..f0ce2525f 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/SpringLifeCycleListener.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/SpringLifeCycleListener.java @@ -36,11 +36,11 @@ /** * Register a {@link ServletContextListener} to respond to Spring context shutdown (#376). This creates classfile - * references to Spring, Spring-Boot, and servlet classes, however this class {@link SpringLifeCycleListener} is - * never actually referenced by any other class in ClassGraph (it is only included in the classpath so that Spring - * can locate it using the {@link Configuration} annotation). Therefore ClassGraph has only a compile-time - * ("provides"-scoped) dependency on Spring to enable Spring to find this class, but a ClassNotFound exception - * should not be thrown by anything else that uses ClassGraph. + * references to Spring and servlet classes, however this class is never actually referenced by any other class in + * ClassGraph (it is only included in the classpath so that Spring can locate it using the {@link Configuration} + * annotation). Therefore ClassGraph has only a compile-time ("provides"-scoped) dependency on Spring to enable + * Spring to find this class, but a ClassNotFound exception should not be thrown by anything else that uses + * ClassGraph. */ @Configuration public class SpringLifeCycleListener { From c1ab20b6387ee354a85cbbdf75fcf482fada67be Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 19 Oct 2019 20:40:03 -0600 Subject: [PATCH 0417/1778] Add OSGi lifecycle listener (untested) (#376) --- pom.xml | 6 ++ .../lifecycle/OSGiLifeCycleListener.java | 67 +++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/OSGiLifeCycleListener.java diff --git a/pom.xml b/pom.xml index a7dd58c75..b25e563cf 100644 --- a/pom.xml +++ b/pom.xml @@ -150,6 +150,12 @@ 1.2.3.RELEASE provided + + org.osgi + org.osgi.service.component.annotations + 1.3.0 + provided + diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/OSGiLifeCycleListener.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/OSGiLifeCycleListener.java new file mode 100644 index 000000000..6e799f1d3 --- /dev/null +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/OSGiLifeCycleListener.java @@ -0,0 +1,67 @@ +/* + * 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.classloaderhandler.lifecycle; + +import java.util.logging.Logger; + +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ScanResult; + +/** + * Register an OSGi {@link Component} to respond to OSGi activation and deactivation (#376). This creates classfile + * references to OSGi service annotation classes, however this class is never actually referenced by any other class + * in ClassGraph (it is only included in the classpath so that the OSGi container can locate it using the + * {@link Component} annotation). Therefore ClassGraph has only a compile-time ("provides"-scoped) dependency on the + * OSGi annotations, to enable the container to find this class, but a ClassNotFound exception should not be thrown + * by anything else that uses ClassGraph. + */ +@Component +public class OSGiLifeCycleListener { + /** The logger. */ + private final Logger log = Logger.getLogger(ClassGraph.class.getName()); + + /** Service activated. */ + @Activate + public void activate() { + log.info("OSGi service activated -- disabling ClassGraph shutdown hook"); + ClassGraph.disableShutdownHook(); + } + + /** Service deactivated. */ + @Deactivate + public void deactivate() { + // Cleanly close down any open {@link ScanResult} instances. + log.info("OSGi service deactivated -- closing any remaning open ClassGraph ScanResult instances"); + ScanResult.closeAll(); + } +} \ No newline at end of file From cf550ac18187c415a2de79331bf3b1b32abaa405 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 19 Oct 2019 20:52:12 -0600 Subject: [PATCH 0418/1778] Remove OSGi lifecycle listener (annotations don't work at runtime) #376 --- pom.xml | 6 -- .../lifecycle/OSGiLifeCycleListener.java | 67 ------------------- 2 files changed, 73 deletions(-) delete mode 100644 src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/OSGiLifeCycleListener.java diff --git a/pom.xml b/pom.xml index b25e563cf..a7dd58c75 100644 --- a/pom.xml +++ b/pom.xml @@ -150,12 +150,6 @@ 1.2.3.RELEASE provided - - org.osgi - org.osgi.service.component.annotations - 1.3.0 - provided - diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/OSGiLifeCycleListener.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/OSGiLifeCycleListener.java deleted file mode 100644 index 6e799f1d3..000000000 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/OSGiLifeCycleListener.java +++ /dev/null @@ -1,67 +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.classloaderhandler.lifecycle; - -import java.util.logging.Logger; - -import org.osgi.service.component.annotations.Activate; -import org.osgi.service.component.annotations.Component; -import org.osgi.service.component.annotations.Deactivate; - -import io.github.classgraph.ClassGraph; -import io.github.classgraph.ScanResult; - -/** - * Register an OSGi {@link Component} to respond to OSGi activation and deactivation (#376). This creates classfile - * references to OSGi service annotation classes, however this class is never actually referenced by any other class - * in ClassGraph (it is only included in the classpath so that the OSGi container can locate it using the - * {@link Component} annotation). Therefore ClassGraph has only a compile-time ("provides"-scoped) dependency on the - * OSGi annotations, to enable the container to find this class, but a ClassNotFound exception should not be thrown - * by anything else that uses ClassGraph. - */ -@Component -public class OSGiLifeCycleListener { - /** The logger. */ - private final Logger log = Logger.getLogger(ClassGraph.class.getName()); - - /** Service activated. */ - @Activate - public void activate() { - log.info("OSGi service activated -- disabling ClassGraph shutdown hook"); - ClassGraph.disableShutdownHook(); - } - - /** Service deactivated. */ - @Deactivate - public void deactivate() { - // Cleanly close down any open {@link ScanResult} instances. - log.info("OSGi service deactivated -- closing any remaning open ClassGraph ScanResult instances"); - ScanResult.closeAll(); - } -} \ No newline at end of file From 2150257d1a0a0123d28285f54eb77014ce4d5a73 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 19 Oct 2019 21:00:15 -0600 Subject: [PATCH 0419/1778] [maven-release-plugin] prepare release classgraph-4.8.51 --- pom.xml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index a7dd58c75..8015d5933 100644 --- a/pom.xml +++ b/pom.xml @@ -1,12 +1,9 @@ - + 4.0.0 io.github.classgraph classgraph - 4.8.51-SNAPSHOT + 4.8.51 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 - HEAD + classgraph-4.8.51 @@ -261,8 +258,7 @@ - + org.codehaus.mojo.signature From ca5f156e0e8ba6504426d73b59c2e9b474b1990b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 19 Oct 2019 21:00:22 -0600 Subject: [PATCH 0420/1778] [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 8015d5933..6ba56253d 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.51 + 4.8.52-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.51 + HEAD From ace370750304f3adbde9f584921374a07a8863ba Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 19 Oct 2019 23:12:02 -0600 Subject: [PATCH 0421/1778] In OOM situation, call runFinalization() after gc() --- src/main/java/io/github/classgraph/ClasspathElementDir.java | 1 + .../io/github/classgraph/fastzipfilereader/PhysicalZipFile.java | 1 + 2 files changed, 2 insertions(+) diff --git a/src/main/java/io/github/classgraph/ClasspathElementDir.java b/src/main/java/io/github/classgraph/ClasspathElementDir.java index 64fedef38..d0882c64c 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementDir.java @@ -201,6 +201,7 @@ public synchronized ByteBuffer read() throws IOException { // (there is a limit to the number of mapped files -- 64k on Linux) // See: http://www.mapdb.org/blog/mmap_files_alloc_and_jvm_crash/ System.gc(); + System.runFinalization(); // Then try calling map again buffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size()); } 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 f4100ee18..49c4bc780 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/PhysicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/PhysicalZipFile.java @@ -138,6 +138,7 @@ public ByteBuffer newInstance(final Integer chunkIdxI, final LogNode log) throws // (there is a limit to the number of mapped files -- 64k on Linux) // See: http://www.mapdb.org/blog/mmap_files_alloc_and_jvm_crash/ System.gc(); + System.runFinalization(); // Then try calling map again buffer = fc.map(FileChannel.MapMode.READ_ONLY, pos, chunkSize); } From 05e33f477d6f78888846de19a18f191aeafea854 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 19 Oct 2019 23:24:13 -0600 Subject: [PATCH 0422/1778] Remove shutdown hook entirely (#376) --- .../java/io/github/classgraph/ClassGraph.java | 14 ---- .../java/io/github/classgraph/ScanResult.java | 71 ++++++------------- .../java/io/github/classgraph/Scanner.java | 3 - .../lifecycle/ServletLifeCycleListener.java | 14 ++-- .../lifecycle/SpringLifeCycleListener.java | 11 ++- 5 files changed, 34 insertions(+), 79 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index f3290da54..96e8a2d49 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -87,20 +87,6 @@ public ClassGraph() { ScanResult.init(); } - /** - * Disable the adding of a shutdown hook, which closes all files and direct byte buffers on shutdown, enabling - * clean shutdown even if the user forgets to call {@link ScanResult#close()} on a {@link ScanResult}. This - * static method must be called before the first call to the {@link ClassGraph#ClassGraph()} constructor, since - * the first call to the constructor adds the shutdown hook if this has not been disabled. - * - *

- * The shutdown hook should be disabled in environments where apps may be loaded and unloaded many times - * throughout the lifetime of the VM, such as web application servers. - */ - public static void disableShutdownHook() { - ScanResult.enableShutdownHook.set(false); - } - /** * Get the version number of ClassGraph. * diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index b8413fa85..8413dfff9 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -132,9 +132,6 @@ public final class ScanResult implements Closeable, AutoCloseable { /** If true, ScanResult#staticInit() has been run. */ private static final AtomicBoolean initialized = new AtomicBoolean(false); - /** If true, add a shutdown hook to close all files and direct byte buffers on shutdown. */ - static final AtomicBoolean enableShutdownHook = new AtomicBoolean(true); - // ------------------------------------------------------------------------------------------------------------- /** The current serialization format. */ @@ -205,46 +202,15 @@ public SerializationFormat(final String serializationFormatStr, final ScanSpec s static void init() { 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. Otherwise, the classloader may be - // closed by its own shutdown hook before ClassGraph's shutdown hook can run, and classloading can - // fail, which will throw an exception and leave resources open (#331). + // to be loaded to close resources are already loaded and cached. This was originally for use in + // a shutdown hook (#331), which has now been removed, but it is probably still a good idea to + // ensure that classes needed to unmap DirectByteBuffer instances are available at init. // We achieve this by mmap'ing a file and then closing it, since the only problematic classes are // the PriviledgedAction anonymous inner classes used by FileUtils::closeDirectByteBuffer. FileUtils.closeDirectByteBuffer(ByteBuffer.allocateDirect(32), /* log = */ null); } } - /** - * Add a shutdown hook, if this is the first time ClassGraph has been run, and if the shutdown hook is enabled - * for the current context classloader. - */ - static void addShutdownHook() { - if (enableShutdownHook.get()) { - // Add runtime shutdown hook to remove temporary files on Ctrl-C or System.exit(). - Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { - @Override - public void run() { - // Close all open/mapped DirectByteBuffers on shutdown - ScanResult.closeAll(); - } - }, "ClassGraph-shutdown-hook")); - } - } - - /** Close all {@link ScanResult} instances that have not yet been closed. */ - public static void closeAll() { - if (nonClosedWeakReferences != null) { - for (final WeakReference nonClosedWeakReference : new ArrayList<>( - nonClosedWeakReferences)) { - final ScanResult scanResult = nonClosedWeakReference.get(); - if (scanResult != null) { - scanResult.close(); - } - nonClosedWeakReferences.remove(nonClosedWeakReference); - } - } - } - // ------------------------------------------------------------------------------------------------------------- // Constructor @@ -327,13 +293,8 @@ public static void closeAll() { this.classGraphClassLoader = new ClassGraphClassLoader(this); // Provide the shutdown hook with a weak reference to this ScanResult - if (nonClosedWeakReferences != null) { - this.weakReference = new WeakReference<>(this); - nonClosedWeakReferences.add(this.weakReference); - } else { - // Should not happen - throw new RuntimeException("nonClosedWeakReferences should not be null"); - } + this.weakReference = new WeakReference<>(this); + nonClosedWeakReferences.add(this.weakReference); } /** Index {@link Resource} and {@link ClassInfo} objects. */ @@ -1390,6 +1351,7 @@ public boolean isObtainedFromDeserialization() { @Override public void close() { if (!closed.getAndSet(true)) { + nonClosedWeakReferences.remove(weakReference); if (classpathOrder != null) { classpathOrder.clear(); classpathOrder = null; @@ -1430,14 +1392,27 @@ public void close() { } classGraphClassLoader = null; classLoaderOrderRespectingParentDelegation = null; - // Remove WeakReference to this ScanResult, so shutdown hook does not try to close this - if (nonClosedWeakReferences != null) { - nonClosedWeakReferences.remove(weakReference); - } // Flush log on exit, in case additional log entries were generated after scan() completed if (topLevelLog != null) { topLevelLog.flush(); } } } + + /** + * 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() { + for (final WeakReference nonClosedWeakReference : new ArrayList<>(nonClosedWeakReferences)) { + final ScanResult scanResult = nonClosedWeakReference.get(); + if (scanResult != null) { + scanResult.close(); + } + } + } } diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 1eca3ec88..efd246f60 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -213,9 +213,6 @@ class Scanner implements Callable { } } } - - // Add a shutdown hook if necessary - ScanResult.addShutdownHook(); } // ------------------------------------------------------------------------------------------------------------- diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/ServletLifeCycleListener.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/ServletLifeCycleListener.java index 5b35d550a..c794eb764 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/ServletLifeCycleListener.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/ServletLifeCycleListener.java @@ -38,12 +38,11 @@ import io.github.classgraph.ScanResult; /** - * Register a {@link WebListener} to respond to servlet context shutdown (#376). This creates classfile references - * to javax.servlet classes, however this class is never actually referenced by any other class in ClassGraph (it is - * only included in the classpath so that the servlet container can locate it using the {@link WebListener} - * annotation). Therefore ClassGraph has only a compile-time ("provides"-scoped) dependency on the servlet container - * to enable the container to find this class, but a ClassNotFound exception should not be thrown by anything else - * that uses ClassGraph. + * Register a {@link WebListener} to respond to servlet context shutdown by closing any remaining open ScanResult + * instances (#376). This creates classfile references to javax.servlet classes, however this class is never + * referenced by any other class in ClassGraph (it is only included in the classpath so that the servlet container + * can locate it using the {@link WebListener} annotation). Therefore ClassGraph has only a compile-time + * ("provides"-scoped) dependency on the servlet container to enable the container to find this class. */ @WebListener public class ServletLifeCycleListener implements ServletContextListener { @@ -58,8 +57,7 @@ public class ServletLifeCycleListener implements ServletContextListener { */ @Override public void contextInitialized(final ServletContextEvent event) { - log.info("Servlet container initialized -- disabling ClassGraph shutdown hook"); - ClassGraph.disableShutdownHook(); + log.info("Servlet context initialized"); } /** diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/SpringLifeCycleListener.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/SpringLifeCycleListener.java index f0ce2525f..52811a50b 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/SpringLifeCycleListener.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/SpringLifeCycleListener.java @@ -35,12 +35,11 @@ import org.springframework.context.annotation.Configuration; /** - * Register a {@link ServletContextListener} to respond to Spring context shutdown (#376). This creates classfile - * references to Spring and servlet classes, however this class is never actually referenced by any other class in - * ClassGraph (it is only included in the classpath so that Spring can locate it using the {@link Configuration} - * annotation). Therefore ClassGraph has only a compile-time ("provides"-scoped) dependency on Spring to enable - * Spring to find this class, but a ClassNotFound exception should not be thrown by anything else that uses - * ClassGraph. + * Register a {@link ServletContextListener} to respond to Spring context shutdown by closing any remaining open + * ScanResult instances (#376). This creates classfile references to Spring and servlet classes, however this class + * is never actually referenced by any other class in ClassGraph (it is only included in the classpath so that + * Spring can locate it using the {@link Configuration} annotation). Therefore ClassGraph has only a compile-time + * ("provides"-scoped) dependency on Spring to enable Spring to find this class. */ @Configuration public class SpringLifeCycleListener { From 6b9c9e0df6bad303029c6a2f5c144029ab9e1ba0 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 19 Oct 2019 23:32:30 -0600 Subject: [PATCH 0423/1778] [maven-release-plugin] prepare release classgraph-4.8.52 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 6ba56253d..c0892f609 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.52-SNAPSHOT + 4.8.52 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.52 From 7f84794963e1ff0fad2187d93af9c948fe36b620 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 19 Oct 2019 23:32:37 -0600 Subject: [PATCH 0424/1778] [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 c0892f609..21deff2e6 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.52 + 4.8.53-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.52 + HEAD From 9af9de983c2e18398b8222cfa14c9ee2b798dbd5 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 21 Oct 2019 14:16:10 -0600 Subject: [PATCH 0425/1778] Add GitHub Sponsors sponsorship links --- FUNDING.yml | 2 ++ README.md | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 FUNDING.yml diff --git a/FUNDING.yml b/FUNDING.yml new file mode 100644 index 000000000..a29449a98 --- /dev/null +++ b/FUNDING.yml @@ -0,0 +1,2 @@ +github: lukehutch + diff --git a/README.md b/README.md index d1e6577e5..3f6050ca2 100644 --- a/README.md +++ b/README.md @@ -131,10 +131,12 @@ This will allow you to build a local SNAPSHOT jar in `target/`. Alternatively, u * Feel free to subscribe to the [ClassGraph-Users](https://groups.google.com/d/forum/classgraph-users) email list for updates, or to ask questions. * There is also a [Gitter room](https://gitter.im/classgraph/Lobby) for discussion of ClassGraph. -## Author +## Sponsorship ClassGraph was written by Luke Hutchison ([@LH](http://twitter.com/LH) on Twitter). +If ClassGraph is critical to your work, you can help fund further development through the [GitHub Sponsors Program](https://github.com/sponsors/lukehutch). + ### Acknowledgments ClassGraph would not be possible without contributions from numerous users, including in the form of bug reports, feature requests, code contributions, and assistance with testing. From dcc87b0ad9df1dd4839b76f7a5fbe8ff086c1598 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 21 Oct 2019 14:27:33 -0600 Subject: [PATCH 0426/1778] Move to .github --- FUNDING.yml => .github/FUNDING.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename FUNDING.yml => .github/FUNDING.yml (100%) diff --git a/FUNDING.yml b/.github/FUNDING.yml similarity index 100% rename from FUNDING.yml rename to .github/FUNDING.yml From 0863cc57ea69c52ea4b699c0dd03e1175fe29365 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 21 Oct 2019 14:29:25 -0600 Subject: [PATCH 0427/1778] Update FUNDING.yml --- .github/FUNDING.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index a29449a98..4391ce004 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,2 +1 @@ github: lukehutch - From e693cb29c5c9e4e08549651bf1d3524a7918ca9f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 21 Oct 2019 14:45:44 -0600 Subject: [PATCH 0428/1778] Add GitHub Sponsors logo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3f6050ca2..bb42832d6 100644 --- a/README.md +++ b/README.md @@ -133,7 +133,7 @@ This will allow you to build a local SNAPSHOT jar in `target/`. Alternatively, u ## Sponsorship -ClassGraph was written by Luke Hutchison ([@LH](http://twitter.com/LH) on Twitter). + ClassGraph was written by Luke Hutchison ([@LH](http://twitter.com/LH) on Twitter). If ClassGraph is critical to your work, you can help fund further development through the [GitHub Sponsors Program](https://github.com/sponsors/lukehutch). From 1db4db5b6083567ff41a8f61acb563cbd45f68c9 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 21 Oct 2019 14:46:33 -0600 Subject: [PATCH 0429/1778] Resize logo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bb42832d6..48ed9c24d 100644 --- a/README.md +++ b/README.md @@ -133,7 +133,7 @@ This will allow you to build a local SNAPSHOT jar in `target/`. Alternatively, u ## Sponsorship - ClassGraph was written by Luke Hutchison ([@LH](http://twitter.com/LH) on Twitter). + ClassGraph was written by Luke Hutchison ([@LH](http://twitter.com/LH) on Twitter). If ClassGraph is critical to your work, you can help fund further development through the [GitHub Sponsors Program](https://github.com/sponsors/lukehutch). From bc2cccebd1b8e4e565ea91759c8d0f1c46274781 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 21 Oct 2019 14:47:28 -0600 Subject: [PATCH 0430/1778] Update docs --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 48ed9c24d..817da5329 100644 --- a/README.md +++ b/README.md @@ -137,11 +137,11 @@ This will allow you to build a local SNAPSHOT jar in `target/`. Alternatively, u If ClassGraph is critical to your work, you can help fund further development through the [GitHub Sponsors Program](https://github.com/sponsors/lukehutch). -### Acknowledgments +## Acknowledgments ClassGraph would not be possible without contributions from numerous users, including in the form of bug reports, feature requests, code contributions, and assistance with testing. -### Alternatives +## Alternatives Some other classpath scanning mechanisms include: From e2912d7d44956665de763067273809a9225a4588 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 21 Oct 2019 14:49:08 -0600 Subject: [PATCH 0431/1778] Try using div for vert spacing --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 817da5329..904b30d1c 100644 --- a/README.md +++ b/README.md @@ -133,10 +133,14 @@ This will allow you to build a local SNAPSHOT jar in `target/`. Alternatively, u ## Sponsorship +

+ ClassGraph was written by Luke Hutchison ([@LH](http://twitter.com/LH) on Twitter). If ClassGraph is critical to your work, you can help fund further development through the [GitHub Sponsors Program](https://github.com/sponsors/lukehutch). +
+ ## Acknowledgments ClassGraph would not be possible without contributions from numerous users, including in the form of bug reports, feature requests, code contributions, and assistance with testing. From 67420a85f21eced9daf6a8f61809cd14f2adda1e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 21 Oct 2019 14:51:44 -0600 Subject: [PATCH 0432/1778] Update vert spacing --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 904b30d1c..9a63e89d0 100644 --- a/README.md +++ b/README.md @@ -133,13 +133,14 @@ This will allow you to build a local SNAPSHOT jar in `target/`. Alternatively, u ## Sponsorship -
- - ClassGraph was written by Luke Hutchison ([@LH](http://twitter.com/LH) on Twitter). + ClassGraph was written by Luke Hutchison ([@LH](http://twitter.com/LH) on Twitter). If ClassGraph is critical to your work, you can help fund further development through the [GitHub Sponsors Program](https://github.com/sponsors/lukehutch). -
+
+
+
+
## Acknowledgments From 656a326f3121483ea12998b834c4abb95d9040c4 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 21 Oct 2019 14:52:45 -0600 Subject: [PATCH 0433/1778] More spacing changes --- README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 9a63e89d0..b2e4d4fca 100644 --- a/README.md +++ b/README.md @@ -133,12 +133,10 @@ This will allow you to build a local SNAPSHOT jar in `target/`. Alternatively, u ## Sponsorship - ClassGraph was written by Luke Hutchison ([@LH](http://twitter.com/LH) on Twitter). +ClassGraph was written by Luke Hutchison ([@LH](http://twitter.com/LH) on Twitter). -If ClassGraph is critical to your work, you can help fund further development through the [GitHub Sponsors Program](https://github.com/sponsors/lukehutch). + If ClassGraph is critical to your work, you can help fund further development through the [GitHub Sponsors Program](https://github.com/sponsors/lukehutch). -
-


From 6302a83e01802d81f80b0df0e9537193b90dbf4c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 21 Oct 2019 14:53:39 -0600 Subject: [PATCH 0434/1778] Update docs --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b2e4d4fca..efb58a9b4 100644 --- a/README.md +++ b/README.md @@ -135,9 +135,10 @@ This will allow you to build a local SNAPSHOT jar in `target/`. Alternatively, u ClassGraph was written by Luke Hutchison ([@LH](http://twitter.com/LH) on Twitter). - If ClassGraph is critical to your work, you can help fund further development through the [GitHub Sponsors Program](https://github.com/sponsors/lukehutch). +If ClassGraph is critical to your work, you can help fund further development through the [GitHub Sponsors Program](https://github.com/sponsors/lukehutch). + + -

## Acknowledgments From c516e5a806a8f6ee83497cb0714798c673d68833 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 21 Oct 2019 14:55:12 -0600 Subject: [PATCH 0435/1778] Update docs --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index efb58a9b4..43b8473aa 100644 --- a/README.md +++ b/README.md @@ -137,9 +137,7 @@ ClassGraph was written by Luke Hutchison ([@LH](http://twitter.com/LH) on Twitte If ClassGraph is critical to your work, you can help fund further development through the [GitHub Sponsors Program](https://github.com/sponsors/lukehutch). - - -
+ ## Acknowledgments From 30901bcc830b78e7c0ba8ff1b5ee4e243d5c9e8b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 21 Oct 2019 14:56:02 -0600 Subject: [PATCH 0436/1778] Update docs --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 43b8473aa..cf1595314 100644 --- a/README.md +++ b/README.md @@ -137,7 +137,7 @@ ClassGraph was written by Luke Hutchison ([@LH](http://twitter.com/LH) on Twitte If ClassGraph is critical to your work, you can help fund further development through the [GitHub Sponsors Program](https://github.com/sponsors/lukehutch). - +
## Acknowledgments From ea0d166fee0d5a7c6b4262a181c3bfdb95806c70 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 28 Oct 2019 01:49:10 -0600 Subject: [PATCH 0437/1778] Run in SecurityManager ThreadGroup if there's a SecurityManager --- .../io/github/classgraph/concurrency/SimpleThreadFactory.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 567bd918e..808f0619c 100644 --- a/src/main/java/nonapi/io/github/classgraph/concurrency/SimpleThreadFactory.java +++ b/src/main/java/nonapi/io/github/classgraph/concurrency/SimpleThreadFactory.java @@ -68,7 +68,9 @@ public class SimpleThreadFactory implements java.util.concurrent.ThreadFactory { */ @Override public Thread newThread(final Runnable runnable) { - final Thread thread = new Thread(new ThreadGroup("ClassGraph-thread-group"), runnable, + final SecurityManager s = System.getSecurityManager(); + final Thread thread = new Thread( + s != null ? s.getThreadGroup() : new ThreadGroup("ClassGraph-thread-group"), runnable, threadNamePrefix + threadIdx.getAndIncrement()); thread.setDaemon(daemon); return thread; From 355b1633cbe13a07e7814ca60ad330771a740eed Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 3 Nov 2019 18:09:34 -0700 Subject: [PATCH 0438/1778] Source > Format --- src/main/java/io/github/classgraph/ArrayClassInfo.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/io/github/classgraph/ArrayClassInfo.java b/src/main/java/io/github/classgraph/ArrayClassInfo.java index ce70ca712..60f13b40d 100644 --- a/src/main/java/io/github/classgraph/ArrayClassInfo.java +++ b/src/main/java/io/github/classgraph/ArrayClassInfo.java @@ -82,6 +82,7 @@ void setScanResult(final ScanResult scanResult) { * * @return The raw type signature string of the array class. */ + @Override public String getTypeSignatureStr() { return arrayTypeSignature.getTypeSignatureStr(); } From 0538be3f3885d1d471b1dd596f292fa2fc5d96cd Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 3 Nov 2019 18:11:09 -0700 Subject: [PATCH 0439/1778] Expose getTypeDescriptorStr() and getTypeSignatureStr() (#380) --- .../java/io/github/classgraph/ClassInfo.java | 15 +++++- .../java/io/github/classgraph/FieldInfo.java | 48 +++++++++++++++++-- .../java/io/github/classgraph/MethodInfo.java | 46 ++++++++++++++++-- 3 files changed, 99 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index 6f565b426..1faaedaa0 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -2455,9 +2455,10 @@ ClassInfoList getClassesWithFieldAnnotationDirectOnly() { // ------------------------------------------------------------------------------------------------------------- /** - * Get the type signature of the class. + * Get the parsed type signature for the class. * - * @return The class type signature, if available, otherwise returns null. + * @return The parsed type signature for the class, including any generic type parameters, or null if not + * available (probably indicating the class is not generic). */ public ClassTypeSignature getTypeSignature() { if (typeSignatureStr == null) { @@ -2474,6 +2475,16 @@ public ClassTypeSignature getTypeSignature() { return typeSignature; } + /** + * Get the type signature string for the class. + * + * @return The type signature string for the class, including any generic type parameters, or null if not + * available (probably indicating the class is not generic). + */ + public String getTypeSignatureStr() { + return typeSignatureStr; + } + // ------------------------------------------------------------------------------------------------------------- /** diff --git a/src/main/java/io/github/classgraph/FieldInfo.java b/src/main/java/io/github/classgraph/FieldInfo.java index 4a50d5d80..a893cd510 100644 --- a/src/main/java/io/github/classgraph/FieldInfo.java +++ b/src/main/java/io/github/classgraph/FieldInfo.java @@ -197,9 +197,10 @@ public int getModifiers() { } /** - * Returns the parsed type descriptor for the field, if available. + * 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 for the field, if available, else returns null. + * @return The parsed type descriptor string for the field. */ public TypeSignature getTypeDescriptor() { if (typeDescriptorStr == null) { @@ -217,9 +218,21 @@ public TypeSignature getTypeDescriptor() { } /** - * Returns the parsed type signature for the field, if available. + * 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 parsed type signature for the field, if available, else returns null. + * @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()} + * instead. + * + * @return The parsed type signature for the field, or null if not available. */ public TypeSignature getTypeSignature() { if (typeSignatureStr == null) { @@ -236,6 +249,17 @@ 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 @@ -253,6 +277,22 @@ public TypeSignature getTypeSignatureOrTypeDescriptor() { } } + /** + * 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 constant final field. Requires * {@link ClassGraph#enableStaticFinalFieldConstantInitializerValues()} to have been called. diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index 6631f9ac8..5da1a659d 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -192,7 +192,7 @@ public ClassInfo getClassInfo() { /** * Returns the parsed type descriptor for the method, which will not include type parameters. If you need - * generic type parameters, call getTypeSignature() instead. + * generic type parameters, call {@link #getTypeSignature()} instead. * * @return The parsed type descriptor for the method. */ @@ -208,9 +208,20 @@ 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 getTypeDescriptor() instead. + * indicating that no type signature information is available for this method, call {@link #getTypeDescriptor()} + * instead. * * @return The parsed type signature for the method, or null if not available. */ @@ -227,8 +238,19 @@ public MethodTypeSignature getTypeSignature() { } /** - * Returns the parsed type signature for the method, possibly including type parameters. If the parsed type - * signature is null, indicating that no type signature information is available for this method, returns the + * 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 * parsed type descriptor instead. * * @return The parsed type signature for the method, or if not available, the parsed type descriptor for the @@ -243,6 +265,22 @@ public MethodTypeSignature getTypeSignatureOrTypeDescriptor() { } } + /** + * 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; + } + } + // ------------------------------------------------------------------------------------------------------------- /** From 35cf1dcfcef2585f1f08a8641ff479e34a85d88e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 3 Nov 2019 18:20:21 -0700 Subject: [PATCH 0440/1778] Remove `static final` check from field constant initializers (#379) --- .../java/io/github/classgraph/ClassGraph.java | 30 +++++++++++++++---- .../java/io/github/classgraph/Classfile.java | 5 ++-- .../java/io/github/classgraph/FieldInfo.java | 16 ++++++---- .../github/classgraph/scanspec/ScanSpec.java | 2 +- .../classgraph/test/ClassGraphTest.java | 4 +-- .../test/fieldinfo/FieldInfoTest.java | 5 ++-- 6 files changed, 42 insertions(+), 20 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index 96e8a2d49..32b4fddbf 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -133,7 +133,7 @@ public ClassGraph verbose(final boolean verbose) { * *

* Calls {@link #enableClassInfo()}, {@link #enableFieldInfo()}, {@link #enableMethodInfo()}, - * {@link #enableAnnotationInfo()}, {@link #enableStaticFinalFieldConstantInitializerValues()}, + * {@link #enableAnnotationInfo()}, {@link #enableFieldConstantInitializerValues()}, * {@link #ignoreClassVisibility()}, {@link #ignoreFieldVisibility()}, and {@link #ignoreMethodVisibility()}. * * @return this (for method chaining). @@ -143,7 +143,7 @@ public ClassGraph enableAllInfo() { enableFieldInfo(); enableMethodInfo(); enableAnnotationInfo(); - enableStaticFinalFieldConstantInitializerValues(); + enableFieldConstantInitializerValues(); ignoreClassVisibility(); ignoreFieldVisibility(); ignoreMethodVisibility(); @@ -227,15 +227,35 @@ public ClassGraph ignoreFieldVisibility() { } /** - * Enables the saving of static final field constant initializer values. By default, constant initializer values - * are not scanned. Automatically calls {@link #enableClassInfo()} and {@link #enableFieldInfo()}. + * Deprecated, because non-static / non-final fields can have initializer values too. Please use + * {@link #enableFieldConstantInitializerValues()} instead. * * @return this (for method chaining). */ + @Deprecated public ClassGraph enableStaticFinalFieldConstantInitializerValues() { + enableFieldConstantInitializerValues(); + return this; + } + + /** + * Enables the saving of field constant initializer values. By default, constant initializer values are not + * scanned. If this is enabled, you can obtain the constant field initializer values from + * {@link FieldInfo#getConstantInitializerValue()}. Note that constant initializer values are usually only of + * primitive type, or String constants. Also note that it is up to the compiler as to whether or not a + * constant-valued field is assigned as a constant in the field definition itself, or whether it is assigned + * manually in static or non-static class initializer blocks or the constructor -- so your mileage may vary in + * being able to extract constant initializer values. + * + *

+ * Automatically calls {@link #enableClassInfo()} and {@link #enableFieldInfo()}. + * + * @return this (for method chaining). + */ + public ClassGraph enableFieldConstantInitializerValues() { enableClassInfo(); enableFieldInfo(); - scanSpec.enableStaticFinalFieldConstantInitializerValues = true; + scanSpec.enableFieldConstantInitializerValues = true; return this; } diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index 63617fe9d..6d49a48a7 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -1246,10 +1246,9 @@ private void readFields() throws IOException, ClassfileFormatException { // Info on modifier flags: http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.5 final int fieldModifierFlags = inputStreamOrByteBuffer.readUnsignedShort(); final boolean isPublicField = ((fieldModifierFlags & 0x0001) == 0x0001); - final boolean isStaticFinalField = ((fieldModifierFlags & 0x0018) == 0x0018); final boolean fieldIsVisible = isPublicField || scanSpec.ignoreFieldVisibility; - final boolean getStaticFinalFieldConstValue = scanSpec.enableStaticFinalFieldConstantInitializerValues - && isStaticFinalField && fieldIsVisible; + final boolean getStaticFinalFieldConstValue = scanSpec.enableFieldConstantInitializerValues + && fieldIsVisible; if (!fieldIsVisible || (!scanSpec.enableFieldInfo && !getStaticFinalFieldConstValue)) { // Skip field inputStreamOrByteBuffer.readUnsignedShort(); // fieldNameCpIdx diff --git a/src/main/java/io/github/classgraph/FieldInfo.java b/src/main/java/io/github/classgraph/FieldInfo.java index a893cd510..1ffc0bb50 100644 --- a/src/main/java/io/github/classgraph/FieldInfo.java +++ b/src/main/java/io/github/classgraph/FieldInfo.java @@ -294,16 +294,20 @@ public String getTypeSignatureOrTypeDescriptorStr() { } /** - * Returns the constant initializer value of a constant final field. Requires - * {@link ClassGraph#enableStaticFinalFieldConstantInitializerValues()} to have been called. + * Returns the constant initializer value of a field. Requires + * {@link ClassGraph#enableFieldConstantInitializerValues()} to have been called. Will only return non-null for + * fields that have constant initializers, which is usually only fields of primitive type, or String constants. + * Also note that it is up to the compiler as to whether or not a constant-valued field is assigned as a + * constant in the field definition itself, or whether it is assigned manually in static or non-static class + * initializer blocks or the constructor -- so your mileage may vary in being able to extract constant + * initializer values. * - * @return The initializer value, if this is a static final field, and has a constant initializer value, or null - * if none. + * @return The initializer value, if this field has a constant initializer value, or null if none. */ public Object getConstantInitializerValue() { - if (!scanResult.scanSpec.enableStaticFinalFieldConstantInitializerValues) { + if (!scanResult.scanSpec.enableFieldConstantInitializerValues) { throw new IllegalArgumentException( - "Please call ClassGraph#enableStaticFinalFieldConstantInitializerValues() " + "before #scan()"); + "Please call ClassGraph#enableFieldConstantInitializerValues() " + "before #scan()"); } return constantInitializerValue == null ? null : constantInitializerValue.get(); } 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 28f5ff141..cf8c4d539 100644 --- a/src/main/java/nonapi/io/github/classgraph/scanspec/ScanSpec.java +++ b/src/main/java/nonapi/io/github/classgraph/scanspec/ScanSpec.java @@ -124,7 +124,7 @@ public class ScanSpec { public boolean enableAnnotationInfo; /** Enable the storing of constant initializer values for static final fields in ClassInfo objects. */ - public boolean enableStaticFinalFieldConstantInitializerValues; + public boolean enableFieldConstantInitializerValues; /** If true, enables the determination of inter-class dependencies. */ public boolean enableInterClassDependencies; diff --git a/src/test/java/io/github/classgraph/test/ClassGraphTest.java b/src/test/java/io/github/classgraph/test/ClassGraphTest.java index e74813dda..1923fa765 100644 --- a/src/test/java/io/github/classgraph/test/ClassGraphTest.java +++ b/src/test/java/io/github/classgraph/test/ClassGraphTest.java @@ -378,7 +378,7 @@ public void accept(final Resource res, final byte[] arr) { @Test public void scanStaticFinalFieldName() { try (ScanResult scanResult = new ClassGraph().whitelistPackages(WHITELIST_PACKAGE) - .enableStaticFinalFieldConstantInitializerValues().scan()) { + .enableFieldConstantInitializerValues().scan()) { int numInitializers = 0; for (final FieldInfo fieldInfo : scanResult.getClassInfo(StaticField.class.getName()).getFieldInfo()) { if (fieldInfo.getConstantInitializerValue() != null) { @@ -403,7 +403,7 @@ public void scanStaticFinalFieldNameIgnoreVisibility() throws Exception { fieldNames.add(StaticField.class.getName() + "." + fieldName); } try (ScanResult scanResult = new ClassGraph().whitelistPackages(WHITELIST_PACKAGE) - .enableStaticFinalFieldConstantInitializerValues().ignoreFieldVisibility().scan()) { + .enableFieldConstantInitializerValues().ignoreFieldVisibility().scan()) { int numInitializers = 0; for (final FieldInfo fieldInfo : scanResult.getClassInfo(StaticField.class.getName()).getFieldInfo()) { final Object constInitializerValue = fieldInfo.getConstantInitializerValue(); diff --git a/src/test/java/io/github/classgraph/test/fieldinfo/FieldInfoTest.java b/src/test/java/io/github/classgraph/test/fieldinfo/FieldInfoTest.java index da0628e09..a2a29197b 100644 --- a/src/test/java/io/github/classgraph/test/fieldinfo/FieldInfoTest.java +++ b/src/test/java/io/github/classgraph/test/fieldinfo/FieldInfoTest.java @@ -72,8 +72,7 @@ public void fieldInfoNotEnabled() { @Test public void testGetFieldInfo() { try (ScanResult scanResult = new ClassGraph().whitelistPackages(FieldInfoTest.class.getPackage().getName()) - .enableFieldInfo().enableStaticFinalFieldConstantInitializerValues().enableAnnotationInfo() - .scan()) { + .enableFieldInfo().enableFieldConstantInitializerValues().enableAnnotationInfo().scan()) { final List fieldInfoStrs = scanResult.getClassInfo(FieldInfoTest.class.getName()).getFieldInfo() .getAsStrings(); assertThat(fieldInfoStrs).containsOnly( @@ -89,7 +88,7 @@ public void testGetFieldInfo() { @Test public void testGetFieldInfoIgnoringVisibility() { try (ScanResult scanResult = new ClassGraph().whitelistPackages(FieldInfoTest.class.getPackage().getName()) - .enableFieldInfo().enableStaticFinalFieldConstantInitializerValues().enableAnnotationInfo() + .enableFieldInfo().enableFieldConstantInitializerValues().enableAnnotationInfo() .ignoreFieldVisibility().scan()) { final List fieldInfoStrs = scanResult.getClassInfo(FieldInfoTest.class.getName()).getFieldInfo() .getAsStrings(); From 979ba84e144bfbd2af78e6a3696988de7934bfc8 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 4 Nov 2019 01:05:45 -0700 Subject: [PATCH 0441/1778] Update unit test (#379) --- .../test/fieldinfo/FieldInfoTest.java | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/test/java/io/github/classgraph/test/fieldinfo/FieldInfoTest.java b/src/test/java/io/github/classgraph/test/fieldinfo/FieldInfoTest.java index a2a29197b..fa315642b 100644 --- a/src/test/java/io/github/classgraph/test/fieldinfo/FieldInfoTest.java +++ b/src/test/java/io/github/classgraph/test/fieldinfo/FieldInfoTest.java @@ -43,17 +43,24 @@ * FieldInfoTest. */ public class FieldInfoTest { - /** The Constant publicFieldWithAnnotation. */ + /** Constant publicFieldWithAnnotation. */ @ExternalAnnotation public static final int publicFieldWithAnnotation = 3; - /** The Constant privateFieldWithAnnotation. */ + /** Constant privateFieldWithAnnotation. */ @ExternalAnnotation private static final String privateFieldWithAnnotation = "test"; - /** The field without annotation. */ + /** Field without annotation. */ public int fieldWithoutAnnotation; + /** + * Field with initializer but without static or final modifier. In Java, the constant pool is not currently used + * by the compiler to assign initializer values for non-static, non-final fields, whereas it supposedly is in + * Kotlin (#380). + */ + public int nonStaticNonFinalFieldWithInitializer = 5; + /** * Field info not enabled. */ @@ -78,7 +85,7 @@ public void testGetFieldInfo() { assertThat(fieldInfoStrs).containsOnly( "@" + ExternalAnnotation.class.getName() + " public static final int publicFieldWithAnnotation = 3", - "public int fieldWithoutAnnotation"); + "public int fieldWithoutAnnotation", "public int nonStaticNonFinalFieldWithInitializer"); } } @@ -97,7 +104,7 @@ public void testGetFieldInfoIgnoringVisibility() { + " public static final int publicFieldWithAnnotation = 3", "@" + ExternalAnnotation.class.getName() + " private static final java.lang.String privateFieldWithAnnotation = \"test\"", - "public int fieldWithoutAnnotation"); + "public int fieldWithoutAnnotation", "public int nonStaticNonFinalFieldWithInitializer"); } } } From 53dbde0b3a043814866bc4492cb5c97acc38d130 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 4 Nov 2019 01:23:58 -0700 Subject: [PATCH 0442/1778] Add check for static non-final field --- .../classgraph/test/fieldinfo/FieldInfoTest.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/test/java/io/github/classgraph/test/fieldinfo/FieldInfoTest.java b/src/test/java/io/github/classgraph/test/fieldinfo/FieldInfoTest.java index fa315642b..7ddf042de 100644 --- a/src/test/java/io/github/classgraph/test/fieldinfo/FieldInfoTest.java +++ b/src/test/java/io/github/classgraph/test/fieldinfo/FieldInfoTest.java @@ -57,10 +57,15 @@ public class FieldInfoTest { /** * Field with initializer but without static or final modifier. In Java, the constant pool is not currently used * by the compiler to assign initializer values for non-static, non-final fields, whereas it supposedly is in - * Kotlin (#380). + * Kotlin (#379). */ public int nonStaticNonFinalFieldWithInitializer = 5; + /** + * Static non-final field with initializer (#379). + */ + public static int staticNonFinalFieldWithInitializer = 7; + /** * Field info not enabled. */ @@ -85,7 +90,8 @@ public void testGetFieldInfo() { assertThat(fieldInfoStrs).containsOnly( "@" + ExternalAnnotation.class.getName() + " public static final int publicFieldWithAnnotation = 3", - "public int fieldWithoutAnnotation", "public int nonStaticNonFinalFieldWithInitializer"); + "public int fieldWithoutAnnotation", "public int nonStaticNonFinalFieldWithInitializer", + "public static int staticNonFinalFieldWithInitializer"); } } @@ -104,7 +110,8 @@ public void testGetFieldInfoIgnoringVisibility() { + " public static final int publicFieldWithAnnotation = 3", "@" + ExternalAnnotation.class.getName() + " private static final java.lang.String privateFieldWithAnnotation = \"test\"", - "public int fieldWithoutAnnotation", "public int nonStaticNonFinalFieldWithInitializer"); + "public int fieldWithoutAnnotation", "public int nonStaticNonFinalFieldWithInitializer", + "public static int staticNonFinalFieldWithInitializer"); } } } From 1924d62cd4005acc03e8f1a50476c9c5451acb26 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 4 Nov 2019 13:00:38 -0700 Subject: [PATCH 0443/1778] Un-deprecate enableStaticFinalFieldConstantInitializerValues() (#379) --- .../java/io/github/classgraph/ClassGraph.java | 49 ++++++++++--------- .../java/io/github/classgraph/Classfile.java | 2 +- .../java/io/github/classgraph/FieldInfo.java | 16 +++--- .../github/classgraph/scanspec/ScanSpec.java | 2 +- .../classgraph/test/ClassGraphTest.java | 4 +- .../test/fieldinfo/FieldInfoTest.java | 5 +- 6 files changed, 41 insertions(+), 37 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index 32b4fddbf..a56f38f41 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -133,7 +133,7 @@ public ClassGraph verbose(final boolean verbose) { * *

* Calls {@link #enableClassInfo()}, {@link #enableFieldInfo()}, {@link #enableMethodInfo()}, - * {@link #enableAnnotationInfo()}, {@link #enableFieldConstantInitializerValues()}, + * {@link #enableAnnotationInfo()}, {@link #enableStaticFinalFieldConstantInitializerValues()}, * {@link #ignoreClassVisibility()}, {@link #ignoreFieldVisibility()}, and {@link #ignoreMethodVisibility()}. * * @return this (for method chaining). @@ -143,7 +143,7 @@ public ClassGraph enableAllInfo() { enableFieldInfo(); enableMethodInfo(); enableAnnotationInfo(); - enableFieldConstantInitializerValues(); + enableStaticFinalFieldConstantInitializerValues(); ignoreClassVisibility(); ignoreFieldVisibility(); ignoreMethodVisibility(); @@ -227,35 +227,38 @@ public ClassGraph ignoreFieldVisibility() { } /** - * Deprecated, because non-static / non-final fields can have initializer values too. Please use - * {@link #enableFieldConstantInitializerValues()} instead. - * - * @return this (for method chaining). - */ - @Deprecated - public ClassGraph enableStaticFinalFieldConstantInitializerValues() { - enableFieldConstantInitializerValues(); - return this; - } - - /** - * Enables the saving of field constant initializer values. By default, constant initializer values are not - * scanned. If this is enabled, you can obtain the constant field initializer values from - * {@link FieldInfo#getConstantInitializerValue()}. Note that constant initializer values are usually only of - * primitive type, or String constants. Also note that it is up to the compiler as to whether or not a - * constant-valued field is assigned as a constant in the field definition itself, or whether it is assigned - * manually in static or non-static class initializer blocks or the constructor -- so your mileage may vary in - * being able to extract constant initializer values. + * Enables the saving of static final field constant initializer values. By default, constant initializer values + * are not scanned. If this is enabled, you can obtain the constant field initializer values from + * {@link FieldInfo#getConstantInitializerValue()}. + * + *

+ * Note that constant initializer values are usually only of primitive type, or String constants (or values that + * can be computed and reduced to one of those types at compiletime). + * + *

+ * Also note that it is up to the compiler as to whether or not a constant-valued field is assigned as a + * constant in the field definition itself, or whether it is assigned manually in static class initializer + * blocks -- so your mileage may vary in being able to extract constant initializer values. + * + *

+ * In fact in Kotlin, even constant initializers for non-static / non-final fields are stored in a field + * attribute in the classfile (and so these values may be picked up by ClassGraph by calling this method), + * although any field initializers for non-static fields are supposed to be ignored by the JVM according to the + * classfile spec, so the Kotlin compiler may change in future to stop generating these values, and you probably + * shouldn't rely on being able to get the initializers for non-static fields in Kotlin. (As far as non-final + * fields, javac simply does not add constant initializer values to the field attributes list for non-final + * fields, even if they are static, but the spec doesn't say whether or not the JVM should ignore constant + * initializers for non-final fields.) * *

* Automatically calls {@link #enableClassInfo()} and {@link #enableFieldInfo()}. * * @return this (for method chaining). */ - public ClassGraph enableFieldConstantInitializerValues() { + public ClassGraph enableStaticFinalFieldConstantInitializerValues() { enableClassInfo(); enableFieldInfo(); - scanSpec.enableFieldConstantInitializerValues = true; + scanSpec.enableStaticFinalFieldConstantInitializerValues = true; return this; } diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index 6d49a48a7..7afaa4d75 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -1247,7 +1247,7 @@ private void readFields() throws IOException, ClassfileFormatException { final int fieldModifierFlags = inputStreamOrByteBuffer.readUnsignedShort(); final boolean isPublicField = ((fieldModifierFlags & 0x0001) == 0x0001); final boolean fieldIsVisible = isPublicField || scanSpec.ignoreFieldVisibility; - final boolean getStaticFinalFieldConstValue = scanSpec.enableFieldConstantInitializerValues + final boolean getStaticFinalFieldConstValue = scanSpec.enableStaticFinalFieldConstantInitializerValues && fieldIsVisible; if (!fieldIsVisible || (!scanSpec.enableFieldInfo && !getStaticFinalFieldConstValue)) { // Skip field diff --git a/src/main/java/io/github/classgraph/FieldInfo.java b/src/main/java/io/github/classgraph/FieldInfo.java index 1ffc0bb50..6d5a8408b 100644 --- a/src/main/java/io/github/classgraph/FieldInfo.java +++ b/src/main/java/io/github/classgraph/FieldInfo.java @@ -295,19 +295,19 @@ public String getTypeSignatureOrTypeDescriptorStr() { /** * Returns the constant initializer value of a field. Requires - * {@link ClassGraph#enableFieldConstantInitializerValues()} to have been called. Will only return non-null for - * fields that have constant initializers, which is usually only fields of primitive type, or String constants. - * Also note that it is up to the compiler as to whether or not a constant-valued field is assigned as a - * constant in the field definition itself, or whether it is assigned manually in static or non-static class - * initializer blocks or the constructor -- so your mileage may vary in being able to extract constant - * initializer values. + * {@link ClassGraph#enableStaticFinalFieldConstantInitializerValues()} to have been called. Will only return + * non-null for fields that have constant initializers, which is usually only fields of primitive type, or + * String constants. Also note that it is up to the compiler as to whether or not a constant-valued field is + * assigned as a constant in the field definition itself, or whether it is assigned manually in static or + * non-static class initializer blocks or the constructor -- so your mileage may vary in being able to extract + * constant initializer values. * * @return The initializer value, if this field has a constant initializer value, or null if none. */ public Object getConstantInitializerValue() { - if (!scanResult.scanSpec.enableFieldConstantInitializerValues) { + if (!scanResult.scanSpec.enableStaticFinalFieldConstantInitializerValues) { throw new IllegalArgumentException( - "Please call ClassGraph#enableFieldConstantInitializerValues() " + "before #scan()"); + "Please call ClassGraph#enableStaticFinalFieldConstantInitializerValues() " + "before #scan()"); } return constantInitializerValue == null ? null : constantInitializerValue.get(); } 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 cf8c4d539..28f5ff141 100644 --- a/src/main/java/nonapi/io/github/classgraph/scanspec/ScanSpec.java +++ b/src/main/java/nonapi/io/github/classgraph/scanspec/ScanSpec.java @@ -124,7 +124,7 @@ public class ScanSpec { public boolean enableAnnotationInfo; /** Enable the storing of constant initializer values for static final fields in ClassInfo objects. */ - public boolean enableFieldConstantInitializerValues; + public boolean enableStaticFinalFieldConstantInitializerValues; /** If true, enables the determination of inter-class dependencies. */ public boolean enableInterClassDependencies; diff --git a/src/test/java/io/github/classgraph/test/ClassGraphTest.java b/src/test/java/io/github/classgraph/test/ClassGraphTest.java index 1923fa765..e74813dda 100644 --- a/src/test/java/io/github/classgraph/test/ClassGraphTest.java +++ b/src/test/java/io/github/classgraph/test/ClassGraphTest.java @@ -378,7 +378,7 @@ public void accept(final Resource res, final byte[] arr) { @Test public void scanStaticFinalFieldName() { try (ScanResult scanResult = new ClassGraph().whitelistPackages(WHITELIST_PACKAGE) - .enableFieldConstantInitializerValues().scan()) { + .enableStaticFinalFieldConstantInitializerValues().scan()) { int numInitializers = 0; for (final FieldInfo fieldInfo : scanResult.getClassInfo(StaticField.class.getName()).getFieldInfo()) { if (fieldInfo.getConstantInitializerValue() != null) { @@ -403,7 +403,7 @@ public void scanStaticFinalFieldNameIgnoreVisibility() throws Exception { fieldNames.add(StaticField.class.getName() + "." + fieldName); } try (ScanResult scanResult = new ClassGraph().whitelistPackages(WHITELIST_PACKAGE) - .enableFieldConstantInitializerValues().ignoreFieldVisibility().scan()) { + .enableStaticFinalFieldConstantInitializerValues().ignoreFieldVisibility().scan()) { int numInitializers = 0; for (final FieldInfo fieldInfo : scanResult.getClassInfo(StaticField.class.getName()).getFieldInfo()) { final Object constInitializerValue = fieldInfo.getConstantInitializerValue(); diff --git a/src/test/java/io/github/classgraph/test/fieldinfo/FieldInfoTest.java b/src/test/java/io/github/classgraph/test/fieldinfo/FieldInfoTest.java index 7ddf042de..3910fcaeb 100644 --- a/src/test/java/io/github/classgraph/test/fieldinfo/FieldInfoTest.java +++ b/src/test/java/io/github/classgraph/test/fieldinfo/FieldInfoTest.java @@ -84,7 +84,8 @@ public void fieldInfoNotEnabled() { @Test public void testGetFieldInfo() { try (ScanResult scanResult = new ClassGraph().whitelistPackages(FieldInfoTest.class.getPackage().getName()) - .enableFieldInfo().enableFieldConstantInitializerValues().enableAnnotationInfo().scan()) { + .enableFieldInfo().enableStaticFinalFieldConstantInitializerValues().enableAnnotationInfo() + .scan()) { final List fieldInfoStrs = scanResult.getClassInfo(FieldInfoTest.class.getName()).getFieldInfo() .getAsStrings(); assertThat(fieldInfoStrs).containsOnly( @@ -101,7 +102,7 @@ public void testGetFieldInfo() { @Test public void testGetFieldInfoIgnoringVisibility() { try (ScanResult scanResult = new ClassGraph().whitelistPackages(FieldInfoTest.class.getPackage().getName()) - .enableFieldInfo().enableFieldConstantInitializerValues().enableAnnotationInfo() + .enableFieldInfo().enableStaticFinalFieldConstantInitializerValues().enableAnnotationInfo() .ignoreFieldVisibility().scan()) { final List fieldInfoStrs = scanResult.getClassInfo(FieldInfoTest.class.getName()).getFieldInfo() .getAsStrings(); From 4b733d9f494297237125859e2d2e8f1cc0cef620 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 4 Nov 2019 13:01:39 -0700 Subject: [PATCH 0444/1778] [maven-release-plugin] prepare release classgraph-4.8.53 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 21deff2e6..3ef02fc3f 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.53-SNAPSHOT + 4.8.53 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.53 From 44b6710ff99ec7f1f30f61fb92a282fa8249a221 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 4 Nov 2019 13:01:46 -0700 Subject: [PATCH 0445/1778] [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 3ef02fc3f..e873e611a 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.53 + 4.8.54-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.53 + HEAD From d91f4f6a393b371c14ba35f6c4e14e8c9fc53dd2 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 12 Nov 2019 13:07:50 -0700 Subject: [PATCH 0446/1778] Add tests to compare Java reflection API with ClassGraph for "declared" --- .../issues/DeclaredVsNonDeclaredTest.java | 220 ++++++++++++++++++ 1 file changed, 220 insertions(+) create mode 100644 src/test/java/io/github/classgraph/issues/DeclaredVsNonDeclaredTest.java diff --git a/src/test/java/io/github/classgraph/issues/DeclaredVsNonDeclaredTest.java b/src/test/java/io/github/classgraph/issues/DeclaredVsNonDeclaredTest.java new file mode 100644 index 000000000..b14c7ffc8 --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/DeclaredVsNonDeclaredTest.java @@ -0,0 +1,220 @@ +package io.github.classgraph.issues; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.List; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +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.MethodInfo; +import io.github.classgraph.ScanResult; + +/** + * Test. + */ +public class DeclaredVsNonDeclaredTest { + /** + * SuperClass. + */ + public static class SuperClass { + /** Public superclass field. */ + public int publicSuperClassField; + + /** Private superclass field. */ + @SuppressWarnings("unused") + private int privateSuperClassField; + + /** + * Public superclass method. + */ + public void publicSuperClassMethod() { + } + + /** + * Private superclass method. + */ + @SuppressWarnings("unused") + private void privateSuperClassMethod() { + } + } + + /** + * SubClass. + */ + public static class SubClass extends SuperClass { + /** Public subclass field. */ + public int publicSubClassField; + + /** Private subclass field. */ + @SuppressWarnings("unused") + private int privateSubClassField; + + /** + * Public subclass method. + */ + public void publicSubClassMethod() { + } + + /** + * Private subclass method. + */ + @SuppressWarnings("unused") + private void privateSubClassMethod() { + } + } + + /** + * Compare results. + * + * @param superClassInfo + * the superclass info + * @param subClassInfo + * the subclass info + * @param ignoreVisibility + * whether or not to ignore method and field visibility + */ + private void compareResults(final ClassInfo superClassInfo, final ClassInfo subClassInfo, + final boolean ignoreVisibility) { + final Predicate filterOutClassMethods = name -> !name.equals("wait") && !name.equals("equals") + && !name.equals("toString") && !name.equals("hashCode") && !name.equals("getClass") + && !name.equals("notify") && !name.equals("notifyAll"); + + // METHODS + + final Function> getClassGraphMethodNames = classInfo -> classInfo.getMethodInfo() + .stream().map(MethodInfo::getName).collect(Collectors.toList()); + + final Function> getClassGraphDeclaredMethodNames = classInfo -> classInfo + .getDeclaredMethodInfo().stream().map(MethodInfo::getName).collect(Collectors.toList()); + + final Function, String[]> getClassMethodNames = clazz -> Arrays.stream(clazz.getMethods()) + .map(Method::getName).filter(filterOutClassMethods).collect(Collectors.toList()) + .toArray(new String[0]); + + final Function, String[]> getClassDeclaredMethodNames = clazz -> Arrays + .stream(clazz.getDeclaredMethods()).map(Method::getName).filter(filterOutClassMethods) + .collect(Collectors.toList()).toArray(new String[0]); + + // Non-"declared" methods, superclass + + assertThat(getClassGraphMethodNames.apply(superClassInfo)).containsExactlyInAnyOrder( + ignoreVisibility ? new String[] { "publicSuperClassMethod", "privateSuperClassMethod" } + : new String[] { "publicSuperClassMethod" }); + assertThat(getClassMethodNames.apply(SuperClass.class)).containsExactlyInAnyOrder("publicSuperClassMethod"); + + // "Declared" methods, superclass + + assertThat(getClassGraphDeclaredMethodNames.apply(superClassInfo)).containsExactlyInAnyOrder( + ignoreVisibility ? new String[] { "publicSuperClassMethod", "privateSuperClassMethod" } + : new String[] { "publicSuperClassMethod" }); + assertThat(getClassDeclaredMethodNames.apply(SuperClass.class)) + .containsExactlyInAnyOrder("publicSuperClassMethod", "privateSuperClassMethod"); + + // Non-"declared" methods, subclass + + assertThat(getClassGraphMethodNames.apply(subClassInfo)).containsExactlyInAnyOrder(ignoreVisibility + ? new String[] { "publicSuperClassMethod", "privateSuperClassMethod", "publicSubClassMethod", + "privateSubClassMethod" } + : new String[] { "publicSuperClassMethod", "publicSubClassMethod" }); + assertThat(getClassMethodNames.apply(SubClass.class)).containsExactlyInAnyOrder("publicSuperClassMethod", + "publicSubClassMethod"); + + // "Declared" methods, subclass + + assertThat(getClassGraphDeclaredMethodNames.apply(subClassInfo)).containsExactlyInAnyOrder( + ignoreVisibility ? new String[] { "publicSubClassMethod", "privateSubClassMethod" } + : new String[] { "publicSubClassMethod" }); + assertThat(getClassDeclaredMethodNames.apply(SubClass.class)) + .containsExactlyInAnyOrder("publicSubClassMethod", "privateSubClassMethod"); + + // FIELDS + + final Function> getClassGraphFieldNames = classInfo -> classInfo.getFieldInfo() + .stream().map(FieldInfo::getName).collect(Collectors.toList()); + + final Function> getClassGraphDeclaredFieldNames = classInfo -> classInfo + .getDeclaredFieldInfo().stream().map(FieldInfo::getName).collect(Collectors.toList()); + + final Function, String[]> getClassFieldNames = clazz -> Arrays.stream(clazz.getFields()) + .map(Field::getName).filter(filterOutClassMethods).collect(Collectors.toList()) + .toArray(new String[0]); + + final Function, String[]> getClassDeclaredFieldNames = clazz -> Arrays + .stream(clazz.getDeclaredFields()).map(Field::getName).filter(filterOutClassMethods) + .collect(Collectors.toList()).toArray(new String[0]); + + // Non-"declared" fields, superclass + + assertThat(getClassGraphFieldNames.apply(superClassInfo)).containsExactlyInAnyOrder( + ignoreVisibility ? new String[] { "publicSuperClassField", "privateSuperClassField" } + : new String[] { "publicSuperClassField" }); + assertThat(getClassFieldNames.apply(SuperClass.class)).containsExactlyInAnyOrder("publicSuperClassField"); + + // "Declared" fields, superclass + + assertThat(getClassGraphDeclaredFieldNames.apply(superClassInfo)).containsExactlyInAnyOrder( + ignoreVisibility ? new String[] { "publicSuperClassField", "privateSuperClassField" } + : new String[] { "publicSuperClassField" }); + assertThat(getClassDeclaredFieldNames.apply(SuperClass.class)) + .containsExactlyInAnyOrder("publicSuperClassField", "privateSuperClassField"); + + // Non-"declared" fields, subclass + + assertThat(getClassGraphFieldNames.apply(subClassInfo)) + .containsExactlyInAnyOrder(ignoreVisibility + ? new String[] { "publicSuperClassField", "privateSuperClassField", "publicSubClassField", + "privateSubClassField" } + : new String[] { "publicSuperClassField", "publicSubClassField" }); + assertThat(getClassFieldNames.apply(SubClass.class)).containsExactlyInAnyOrder("publicSuperClassField", + "publicSubClassField"); + + // "Declared" fields, subclass + + assertThat(getClassGraphDeclaredFieldNames.apply(subClassInfo)).containsExactlyInAnyOrder( + ignoreVisibility ? new String[] { "publicSubClassField", "privateSubClassField" } + : new String[] { "publicSubClassField" }); + assertThat(getClassDeclaredFieldNames.apply(SubClass.class)) + .containsExactlyInAnyOrder("publicSubClassField", "privateSubClassField"); + } + + /** + * Test ClassGraph's "declared" vs. non-"declared" method/field retrieval against the Java reflection API, + * without calling {@link ClassGraph#ignoreMethodVisibility()} or {@link ClassGraph#ignoreFieldVisibility()}. + */ + @Test + public void publicDeclaredVsNonDeclared() { + try (ScanResult scanResult = new ClassGraph().enableClassInfo() // + .enableMethodInfo() // + .enableFieldInfo() // + .whitelistPackages(DeclaredVsNonDeclaredTest.class.getPackage().getName()).scan()) { + final ClassInfo superClassInfo = scanResult.getClassInfo(SuperClass.class.getName()); + final ClassInfo subClassInfo = scanResult.getClassInfo(SubClass.class.getName()); + compareResults(superClassInfo, subClassInfo, /* ignoreVisibility = */ false); + } + } + + /** + * Test ClassGraph's "declared" vs. non-"declared" method/field retrieval against the Java reflection API, + * without calling {@link ClassGraph#ignoreMethodVisibility()} or {@link ClassGraph#ignoreFieldVisibility()}. + */ + @Test + public void publicAndPrivateDeclaredVsNonDeclared() { + try (ScanResult scanResult = new ClassGraph().enableClassInfo() // + .enableMethodInfo().ignoreMethodVisibility() // + .enableFieldInfo().ignoreFieldVisibility() // + .whitelistPackages(DeclaredVsNonDeclaredTest.class.getPackage().getName()).scan()) { + final ClassInfo superClassInfo = scanResult.getClassInfo(SuperClass.class.getName()); + final ClassInfo subClassInfo = scanResult.getClassInfo(SubClass.class.getName()); + compareResults(superClassInfo, subClassInfo, /* ignoreVisibility = */ true); + } + } +} From d6d918673ed0b1a1be8e76b85d93913f92ce7389 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 12 Nov 2019 13:17:27 -0700 Subject: [PATCH 0447/1778] Small tweak --- .../classgraph/issues/DeclaredVsNonDeclaredTest.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/test/java/io/github/classgraph/issues/DeclaredVsNonDeclaredTest.java b/src/test/java/io/github/classgraph/issues/DeclaredVsNonDeclaredTest.java index b14c7ffc8..c72e00f89 100644 --- a/src/test/java/io/github/classgraph/issues/DeclaredVsNonDeclaredTest.java +++ b/src/test/java/io/github/classgraph/issues/DeclaredVsNonDeclaredTest.java @@ -144,13 +144,12 @@ private void compareResults(final ClassInfo superClassInfo, final ClassInfo subC final Function> getClassGraphDeclaredFieldNames = classInfo -> classInfo .getDeclaredFieldInfo().stream().map(FieldInfo::getName).collect(Collectors.toList()); - final Function, String[]> getClassFieldNames = clazz -> Arrays.stream(clazz.getFields()) - .map(Field::getName).filter(filterOutClassMethods).collect(Collectors.toList()) - .toArray(new String[0]); + final Function, List> getClassFieldNames = clazz -> Arrays.stream(clazz.getFields()) + .map(Field::getName).filter(filterOutClassMethods).collect(Collectors.toList()); - final Function, String[]> getClassDeclaredFieldNames = clazz -> Arrays + final Function, List> getClassDeclaredFieldNames = clazz -> Arrays .stream(clazz.getDeclaredFields()).map(Field::getName).filter(filterOutClassMethods) - .collect(Collectors.toList()).toArray(new String[0]); + .collect(Collectors.toList()); // Non-"declared" fields, superclass From 3ba99110e5172f411d0add2b8b02ca8e6d94b2bd Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 12 Nov 2019 14:32:56 -0700 Subject: [PATCH 0448/1778] Reorder code to mirror docs --- .../issues/DeclaredVsNonDeclaredTest.java | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/test/java/io/github/classgraph/issues/DeclaredVsNonDeclaredTest.java b/src/test/java/io/github/classgraph/issues/DeclaredVsNonDeclaredTest.java index c72e00f89..9fc80f74f 100644 --- a/src/test/java/io/github/classgraph/issues/DeclaredVsNonDeclaredTest.java +++ b/src/test/java/io/github/classgraph/issues/DeclaredVsNonDeclaredTest.java @@ -111,23 +111,23 @@ private void compareResults(final ClassInfo superClassInfo, final ClassInfo subC : new String[] { "publicSuperClassMethod" }); assertThat(getClassMethodNames.apply(SuperClass.class)).containsExactlyInAnyOrder("publicSuperClassMethod"); - // "Declared" methods, superclass - - assertThat(getClassGraphDeclaredMethodNames.apply(superClassInfo)).containsExactlyInAnyOrder( - ignoreVisibility ? new String[] { "publicSuperClassMethod", "privateSuperClassMethod" } - : new String[] { "publicSuperClassMethod" }); - assertThat(getClassDeclaredMethodNames.apply(SuperClass.class)) - .containsExactlyInAnyOrder("publicSuperClassMethod", "privateSuperClassMethod"); - // Non-"declared" methods, subclass assertThat(getClassGraphMethodNames.apply(subClassInfo)).containsExactlyInAnyOrder(ignoreVisibility - ? new String[] { "publicSuperClassMethod", "privateSuperClassMethod", "publicSubClassMethod", + ? new String[] { "publicSuperClassMethod", "publicSubClassMethod", "privateSuperClassMethod", "privateSubClassMethod" } : new String[] { "publicSuperClassMethod", "publicSubClassMethod" }); assertThat(getClassMethodNames.apply(SubClass.class)).containsExactlyInAnyOrder("publicSuperClassMethod", "publicSubClassMethod"); + // "Declared" methods, superclass + + assertThat(getClassGraphDeclaredMethodNames.apply(superClassInfo)).containsExactlyInAnyOrder( + ignoreVisibility ? new String[] { "publicSuperClassMethod", "privateSuperClassMethod" } + : new String[] { "publicSuperClassMethod" }); + assertThat(getClassDeclaredMethodNames.apply(SuperClass.class)) + .containsExactlyInAnyOrder("publicSuperClassMethod", "privateSuperClassMethod"); + // "Declared" methods, subclass assertThat(getClassGraphDeclaredMethodNames.apply(subClassInfo)).containsExactlyInAnyOrder( @@ -158,24 +158,24 @@ private void compareResults(final ClassInfo superClassInfo, final ClassInfo subC : new String[] { "publicSuperClassField" }); assertThat(getClassFieldNames.apply(SuperClass.class)).containsExactlyInAnyOrder("publicSuperClassField"); - // "Declared" fields, superclass - - assertThat(getClassGraphDeclaredFieldNames.apply(superClassInfo)).containsExactlyInAnyOrder( - ignoreVisibility ? new String[] { "publicSuperClassField", "privateSuperClassField" } - : new String[] { "publicSuperClassField" }); - assertThat(getClassDeclaredFieldNames.apply(SuperClass.class)) - .containsExactlyInAnyOrder("publicSuperClassField", "privateSuperClassField"); - // Non-"declared" fields, subclass assertThat(getClassGraphFieldNames.apply(subClassInfo)) .containsExactlyInAnyOrder(ignoreVisibility - ? new String[] { "publicSuperClassField", "privateSuperClassField", "publicSubClassField", + ? new String[] { "publicSuperClassField", "publicSubClassField", "privateSuperClassField", "privateSubClassField" } : new String[] { "publicSuperClassField", "publicSubClassField" }); assertThat(getClassFieldNames.apply(SubClass.class)).containsExactlyInAnyOrder("publicSuperClassField", "publicSubClassField"); + // "Declared" fields, superclass + + assertThat(getClassGraphDeclaredFieldNames.apply(superClassInfo)).containsExactlyInAnyOrder( + ignoreVisibility ? new String[] { "publicSuperClassField", "privateSuperClassField" } + : new String[] { "publicSuperClassField" }); + assertThat(getClassDeclaredFieldNames.apply(SuperClass.class)) + .containsExactlyInAnyOrder("publicSuperClassField", "privateSuperClassField"); + // "Declared" fields, subclass assertThat(getClassGraphDeclaredFieldNames.apply(subClassInfo)).containsExactlyInAnyOrder( From 82235d9fd120aae20f68d9cd236587ba86a30f44 Mon Sep 17 00:00:00 2001 From: Michael Simons Date: Wed, 13 Nov 2019 10:51:00 +0100 Subject: [PATCH 0449/1778] Use 'fields' instead of 'methods' in getXXXFieldInfo() JavaDoc. --- src/main/java/io/github/classgraph/ClassInfo.java | 6 +++--- 1 file 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 1faaedaa0..cef9d91af 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -2277,7 +2277,7 @@ ClassInfoList getClassesWithMethodParameterAnnotationDirectOnly() { * {@link IllegalArgumentException}. * *

- * By default only returns information for public methods, unless {@link ClassGraph#ignoreFieldVisibility()} was + * By default only returns information for public fields, unless {@link ClassGraph#ignoreFieldVisibility()} was * called before the scan. * * @return the list of FieldInfo objects for visible fields declared by this class, or the empty list if no @@ -2306,7 +2306,7 @@ public FieldInfoList getDeclaredFieldInfo() { * {@link IllegalArgumentException}. * *

- * By default only returns information for public methods, unless {@link ClassGraph#ignoreFieldVisibility()} was + * By default only returns information for public fields, unless {@link ClassGraph#ignoreFieldVisibility()} was * called before the scan. * * @return the list of FieldInfo objects for visible fields of this class or its superclases, or the empty list @@ -2386,7 +2386,7 @@ public FieldInfo getDeclaredFieldInfo(final String fieldName) { * {@link IllegalArgumentException}. * *

- * By default only returns information for public methods, unless {@link ClassGraph#ignoreFieldVisibility()} was + * By default only returns information for public fields, unless {@link ClassGraph#ignoreFieldVisibility()} was * called before the scan. * * @param fieldName From ca81f8d51306a6e6b9492d1b4e7804d2476c9add Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 18 Nov 2019 06:34:24 -0700 Subject: [PATCH 0450/1778] Enable module scanning for AppClassLoader and PlatformClassLoader (#382) --- .../java/io/github/classgraph/Scanner.java | 39 ++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index efd246f60..1f3c13c78 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -158,8 +158,45 @@ class Scanner implements Callable { final ModuleFinder moduleFinder = new ModuleFinder(classpathFinder.getCallStack(), scanSpec, classpathFinderLog); + // 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.overrideClasspath != null) { + // Don't scan modules if classpath is overridden + scanModules = false; + } else if (scanSpec.overrideClassLoaders != null) { + // If classloaders are overridden, only scan modules if an override classloader is a JPMS + // AppClassLoader or PlatformClassLoader + scanModules = 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; + } 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 " + + "jdk.internal.loader.ClassLoaders$PlatformClassLoader, which is a system " + + "classloader, so enableSystemJarsAndModules() was called automatically"); + } + scanSpec.enableSystemJarsAndModules = true; + } + } + } + } else { + // If classloaders are not overridden and classpath is not overridden, only scan modules + // if module scanning is enabled + scanModules = scanSpec.scanModules; + } + this.moduleOrder = new ArrayList<>(); - if (scanSpec.overrideClasspath == null && scanSpec.overrideClassLoaders == null && scanSpec.scanModules) { + if (scanModules) { // Add modules to start of classpath order, before traditional classpath final List systemModuleRefs = moduleFinder.getSystemModuleRefs(); final ClassLoader defaultClassLoader = classLoaderOrderRespectingParentDelegation != null From d5da564cd059b76e33d5fdacf60cafd926802894 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 18 Nov 2019 06:38:29 -0700 Subject: [PATCH 0451/1778] [maven-release-plugin] prepare release classgraph-4.8.54 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e873e611a..75cca5779 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.54-SNAPSHOT + 4.8.54 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.54 From 5384341e21acd69b3b587a67194d6d76ccd216df Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 18 Nov 2019 06:38:36 -0700 Subject: [PATCH 0452/1778] [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 75cca5779..87b97d701 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.54 + 4.8.55-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.54 + HEAD From bd4a7f4b59a85989feba6b9a7b3b9b98198d512e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 18 Nov 2019 06:45:06 -0700 Subject: [PATCH 0453/1778] Fix comment --- 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 1f3c13c78..df3da68c5 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -173,7 +173,7 @@ class Scanner implements Callable { 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 + // Thread.currentThread().getContextClassLoader() [.getParent()] or similar if (classLoaderClassName.equals("jdk.internal.loader.ClassLoaders$AppClassLoader")) { scanModules = true; } else if (classLoaderClassName.equals("jdk.internal.loader.ClassLoaders$PlatformClassLoader")) { From 4192576a74ffc449c2700be3d379a9712848f8b8 Mon Sep 17 00:00:00 2001 From: contextshuffling Date: Sat, 23 Nov 2019 17:31:19 -0600 Subject: [PATCH 0454/1778] sort fields for deterministic order --- .../io/github/classgraph/json/ClassFields.java | 18 ++++++++++++++++++ .../classgraph/json/JSONSerializationTest.java | 8 ++++---- 2 files changed, 22 insertions(+), 4 deletions(-) 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 7feef95aa..929b6aee5 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/ClassFields.java +++ b/src/main/java/nonapi/io/github/classgraph/json/ClassFields.java @@ -32,6 +32,8 @@ import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -96,6 +98,22 @@ public ClassFields(final Class cls, final boolean resolveTypes, final boolean throw new IllegalArgumentException("Illegal class type: " + currType); } final Field[] fields = currRawType.getDeclaredFields(); + List res = new ArrayList<>(); + Arrays.sort(fields, new Comparator() { + @Override + public int compare(Field a, Field b) { + if (a.getName().equals("format")){ + return -1; + } else if (b.getName().equals("format")) { + return 1; + } + return a.getName().compareTo(b.getName()); + } + }); + for(Field f : fields) { + res.add(f.getName()); + } + System.out.println(res); final List fieldOrderWithinClass = new ArrayList<>(); for (final Field field : fields) { // Mask superclass fields if subclass has a field of the same name diff --git a/src/test/java/io/github/classgraph/json/JSONSerializationTest.java b/src/test/java/io/github/classgraph/json/JSONSerializationTest.java index 2ff6d8729..a4d94adab 100644 --- a/src/test/java/io/github/classgraph/json/JSONSerializationTest.java +++ b/src/test/java/io/github/classgraph/json/JSONSerializationTest.java @@ -255,10 +255,10 @@ public void testJSON() { final String json0 = JSONSerializer.serializeFromField(h, "g", 0, false); final String expected = // - "{\"e\":{\"q\":{\"b\":3,\"a\":{\"x\":[3],\"y\":\"x\"},\"arr\":[3,3,3]},\"map\":{\"3\":3}," - + "\"list\":[3,3,3],\"c\":{\"b\":5,\"a\":{\"x\":[5],\"y\":\"x\"},\"arr\":[5,5,5]}," - + "\"z\":42},\"f\":{\"z\":1.5,\"q\":{\"b\":1.5,\"a\":{\"x\":[1.5],\"y\":\"x\"}," - + "\"arr\":[1.5,1.5,1.5]},\"map\":{\"1.5\":1.5},\"list\":[1.5,1.5,1.5],\"wxy\":123}}"; + "{\"e\":{\"list\":[3,3,3],\"map\":{\"3\":3},\"q\":{\"b\":3,\"a\":{\"x\":[3],\"y\":\"x\"}," + + "\"arr\":[3,3,3]}," + "\"c\":{\"b\":5,\"a\":{\"x\":[5],\"y\":\"x\"},\"arr\":[5,5,5]}," + + "\"z\":42},\"f\":{\"list\":[1.5,1.5,1.5],\"map\":{\"1.5\":1.5}," + "\"q\":{\"b\":1.5," + + "\"a\":{\"x\":[1.5],\"y\":\"x\"},\"arr\":[1.5,1.5,1.5]},\"z\":1.5,\"wxy\":123}}"; assertThat(json0).isEqualTo(expected); From 44434b92ed15dc2c2cc16e4efc951c8c843218b5 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 23 Nov 2019 18:03:38 -0700 Subject: [PATCH 0455/1778] Update patch in #383, conditioned on class name --- .../github/classgraph/json/ClassFields.java | 49 +++++++++++++------ 1 file changed, 33 insertions(+), 16 deletions(-) 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 929b6aee5..1457338f0 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/ClassFields.java +++ b/src/main/java/nonapi/io/github/classgraph/json/ClassFields.java @@ -40,6 +40,8 @@ import java.util.Map; import java.util.Set; +import io.github.classgraph.ScanResult; + /** * The list of fields that can be (de)serialized (non-final, non-transient, non-synthetic, accessible), and their * corresponding resolved (concrete) types. @@ -66,6 +68,28 @@ class ClassFields { // TODO: replace this with getter/setter MethodHandles for speed Field idField; + /** Used to sort fields into deterministic order. */ + private static final Comparator FIELD_NAME_ORDER_COMPARATOR = // + new Comparator() { + @Override + public int compare(Field a, Field b) { + return a.getName().compareTo(b.getName()); + } + }; + + /** + * Used to sort fields into deterministic order for SerializationFormat class (which needs to have "format" + * field in first position for ClassGraph's serialization format) (#383). + */ + private static final Comparator SERIALIZATION_FORMAT_FIELD_NAME_ORDER_COMPARATOR = // + new Comparator() { + @Override + public int compare(Field a, Field b) { + return a.getName().equals("format") ? -1 + : b.getName().equals("format") ? 1 : a.getName().compareTo(b.getName()); + } + }; + /** * Constructor. * @@ -97,23 +121,16 @@ public ClassFields(final Class cls, final boolean resolveTypes, final boolean // Class definitions should not be of type WildcardType or GenericArrayType throw new IllegalArgumentException("Illegal class type: " + currType); } + + // getDeclaredFields() does not guarantee any given order, so need to sort fields. (#383) final Field[] fields = currRawType.getDeclaredFields(); - List res = new ArrayList<>(); - Arrays.sort(fields, new Comparator() { - @Override - public int compare(Field a, Field b) { - if (a.getName().equals("format")){ - return -1; - } else if (b.getName().equals("format")) { - return 1; - } - return a.getName().compareTo(b.getName()); - } - }); - for(Field f : fields) { - res.add(f.getName()); - } - System.out.println(res); + Arrays.sort(fields, cls.getName().equals(ScanResult.class.getName() + "$SerializationFormat") + // Special sort order for SerializationFormat class: put "format" field first + ? SERIALIZATION_FORMAT_FIELD_NAME_ORDER_COMPARATOR + // Otherwise just sort by name so that order is deterministic + : FIELD_NAME_ORDER_COMPARATOR); + + // Find any @Id-annotated field, and get Field type info final List fieldOrderWithinClass = new ArrayList<>(); for (final Field field : fields) { // Mask superclass fields if subclass has a field of the same name From 2942811281f992fbe99252932416571e566a4b77 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 23 Nov 2019 18:04:22 -0700 Subject: [PATCH 0456/1778] Source > Cleanup --- .../java/nonapi/io/github/classgraph/json/ClassFields.java | 4 ++-- .../io/github/classgraph/json/JSONSerializationTest.java | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) 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 1457338f0..c1ebd47c8 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/ClassFields.java +++ b/src/main/java/nonapi/io/github/classgraph/json/ClassFields.java @@ -72,7 +72,7 @@ class ClassFields { private static final Comparator FIELD_NAME_ORDER_COMPARATOR = // new Comparator() { @Override - public int compare(Field a, Field b) { + public int compare(final Field a, final Field b) { return a.getName().compareTo(b.getName()); } }; @@ -84,7 +84,7 @@ public int compare(Field a, Field b) { private static final Comparator SERIALIZATION_FORMAT_FIELD_NAME_ORDER_COMPARATOR = // new Comparator() { @Override - public int compare(Field a, Field b) { + public int compare(final Field a, final Field b) { return a.getName().equals("format") ? -1 : b.getName().equals("format") ? 1 : a.getName().compareTo(b.getName()); } diff --git a/src/test/java/io/github/classgraph/json/JSONSerializationTest.java b/src/test/java/io/github/classgraph/json/JSONSerializationTest.java index a4d94adab..f92f27576 100644 --- a/src/test/java/io/github/classgraph/json/JSONSerializationTest.java +++ b/src/test/java/io/github/classgraph/json/JSONSerializationTest.java @@ -256,9 +256,9 @@ public void testJSON() { final String expected = // "{\"e\":{\"list\":[3,3,3],\"map\":{\"3\":3},\"q\":{\"b\":3,\"a\":{\"x\":[3],\"y\":\"x\"}," - + "\"arr\":[3,3,3]}," + "\"c\":{\"b\":5,\"a\":{\"x\":[5],\"y\":\"x\"},\"arr\":[5,5,5]}," - + "\"z\":42},\"f\":{\"list\":[1.5,1.5,1.5],\"map\":{\"1.5\":1.5}," + "\"q\":{\"b\":1.5," - + "\"a\":{\"x\":[1.5],\"y\":\"x\"},\"arr\":[1.5,1.5,1.5]},\"z\":1.5,\"wxy\":123}}"; + + "\"arr\":[3,3,3]}," + "\"c\":{\"b\":5,\"a\":{\"x\":[5],\"y\":\"x\"},\"arr\":[5,5,5]}," + + "\"z\":42},\"f\":{\"list\":[1.5,1.5,1.5],\"map\":{\"1.5\":1.5}," + "\"q\":{\"b\":1.5," + + "\"a\":{\"x\":[1.5],\"y\":\"x\"},\"arr\":[1.5,1.5,1.5]},\"z\":1.5,\"wxy\":123}}"; assertThat(json0).isEqualTo(expected); From 4791d0a93f1c1fbbc215e636d75076a8f60f558d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 24 Nov 2019 22:31:37 -0700 Subject: [PATCH 0457/1778] Small optimization --- .../java/nonapi/io/github/classgraph/json/ClassFields.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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 c1ebd47c8..56cd29abf 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/ClassFields.java +++ b/src/main/java/nonapi/io/github/classgraph/json/ClassFields.java @@ -90,6 +90,10 @@ public int compare(final Field a, final Field b) { } }; + /** The name of the SerializationFormat class (used by ClassGraph to serialize a ScanResult). */ + private static final String SERIALIZATION_FORMAT_CLASS_NAME = ScanResult.class.getName() + + "$SerializationFormat"; + /** * Constructor. * @@ -124,7 +128,7 @@ public ClassFields(final Class cls, final boolean resolveTypes, final boolean // getDeclaredFields() does not guarantee any given order, so need to sort fields. (#383) final Field[] fields = currRawType.getDeclaredFields(); - Arrays.sort(fields, cls.getName().equals(ScanResult.class.getName() + "$SerializationFormat") + Arrays.sort(fields, cls.getName().equals(SERIALIZATION_FORMAT_CLASS_NAME) // Special sort order for SerializationFormat class: put "format" field first ? SERIALIZATION_FORMAT_FIELD_NAME_ORDER_COMPARATOR // Otherwise just sort by name so that order is deterministic From 34179ea113cced7d132b8cc55157672072222845 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 25 Nov 2019 02:10:09 -0700 Subject: [PATCH 0458/1778] Speed up zip entry path processing --- .../io/github/classgraph/utils/FileUtils.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 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 946bd675d..5a62bf022 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -319,20 +319,23 @@ public static String sanitizeEntryPath(final String path, final boolean removeIn // Find all '/' and '!' character positions, which split a path into segments boolean foundSegmentToSanitize = false; + final int pathLen = path.length(); + final char[] pathChars = new char[pathLen]; + path.getChars(0, pathLen, pathChars, 0); { int lastSepIdx = -1; char prevC = '\0'; - for (int i = 0; i < path.length() + 1; i++) { - final char c = i == path.length() ? '\0' : path.charAt(i); + for (int i = 0, ii = pathLen + 1; i < ii; i++) { + final char c = i == pathLen ? '\0' : pathChars[i]; if (c == '/' || c == '!' || c == '\0') { final int segmentLength = i - (lastSepIdx + 1); if ( // Found empty segment "//" or "!!" (segmentLength == 0 && prevC == c) // Found segment "." - || (segmentLength == 1 && path.charAt(i - 1) == '.') + || (segmentLength == 1 && pathChars[i - 1] == '.') // Found segment ".." - || (segmentLength == 2 && path.charAt(i - 2) == '.' && path.charAt(i - 1) == '.')) { + || (segmentLength == 2 && pathChars[i - 2] == '.' && pathChars[i - 1] == '.')) { foundSegmentToSanitize = true; } lastSepIdx = i; @@ -349,8 +352,8 @@ public static String sanitizeEntryPath(final String path, final boolean removeIn List currSectionSegments = new ArrayList<>(); allSectionSegments.add(currSectionSegments); int lastSepIdx = -1; - for (int i = 0; i < path.length() + 1; i++) { - final char c = i == path.length() ? '\0' : path.charAt(i); + for (int i = 0; i < pathLen + 1; i++) { + final char c = i == pathLen ? '\0' : pathChars[i]; if (c == '/' || c == '!' || c == '\0') { final String segment = path.substring(lastSepIdx + 1, i); if (segment.equals(".") || segment.isEmpty()) { From d5a01b3e49b9ba515df4e0ed3da2b67fe81e70b1 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 25 Nov 2019 02:16:56 -0700 Subject: [PATCH 0459/1778] Speed up zip entry path processing --- .../io/github/classgraph/utils/FileUtils.java | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 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 5a62bf022..224d11d9b 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -345,7 +345,8 @@ public static String sanitizeEntryPath(final String path, final boolean removeIn } // Handle "..", "." and empty path segments, if any were found - String pathSanitized = path; + final boolean pathHasInitialSlash = !path.isEmpty() && path.charAt(0) == '/'; + final StringBuilder pathSanitized = new StringBuilder(); if (foundSegmentToSanitize) { // Sanitize between "!" section markers separately (".." should not apply past preceding "!") final List> allSectionSegments = new ArrayList<>(); @@ -376,34 +377,35 @@ public static String sanitizeEntryPath(final String path, final boolean removeIn } } // Turn sections and segments back into path string - final StringBuilder buf = new StringBuilder(); for (final List sectionSegments : allSectionSegments) { if (!sectionSegments.isEmpty()) { // Delineate segments with "!" - if (buf.length() > 0) { - buf.append('!'); + if (pathSanitized.length() > 0) { + pathSanitized.append('!'); } for (final String sectionSegment : sectionSegments) { - buf.append('/'); - buf.append(sectionSegment); + pathSanitized.append('/'); + pathSanitized.append(sectionSegment); } } } - pathSanitized = buf.toString(); - if (pathSanitized.isEmpty() && path.startsWith("/")) { - pathSanitized = "/"; + if (pathSanitized.length() == 0 && pathHasInitialSlash) { + pathSanitized.append('/'); } + } else { + pathSanitized.append(path); } - if (removeInitialSlash || !path.startsWith("/")) { + 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 // (the string-building code above prepends "/" to every segment). Note that "/" is always added // after "!", since "jar:" URLs expect this. - while (pathSanitized.startsWith("/")) { - pathSanitized = pathSanitized.substring(1); + while (startIdx < pathSanitized.length() && pathSanitized.charAt(startIdx) == '/') { + startIdx++; } } - return pathSanitized; + return pathSanitized.substring(startIdx); } // ------------------------------------------------------------------------------------------------------------- From ddd9291333958ba886a06b6b27b635e2ce39720c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 25 Nov 2019 02:24:53 -0700 Subject: [PATCH 0460/1778] Optimize zip entry path handling --- .../io/github/classgraph/utils/FileUtils.java | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 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 224d11d9b..1f1673e58 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -345,28 +345,30 @@ public static String sanitizeEntryPath(final String path, final boolean removeIn } // Handle "..", "." and empty path segments, if any were found - final boolean pathHasInitialSlash = !path.isEmpty() && path.charAt(0) == '/'; - final StringBuilder pathSanitized = new StringBuilder(); + final boolean pathHasInitialSlash = pathLen > 0 && pathChars[0] == '/'; + final StringBuilder pathSanitized = new StringBuilder(pathLen + 16); if (foundSegmentToSanitize) { // Sanitize between "!" section markers separately (".." should not apply past preceding "!") - final List> allSectionSegments = new ArrayList<>(); - List currSectionSegments = new ArrayList<>(); + final List> allSectionSegments = new ArrayList<>(); + List currSectionSegments = new ArrayList<>(); allSectionSegments.add(currSectionSegments); int lastSepIdx = -1; for (int i = 0; i < pathLen + 1; i++) { final char c = i == pathLen ? '\0' : pathChars[i]; if (c == '/' || c == '!' || c == '\0') { - final String segment = path.substring(lastSepIdx + 1, i); - if (segment.equals(".") || segment.isEmpty()) { - // Ignore "/./" or empty segment "//" - } else if (segment.equals("..")) { + final int segmentStartIdx = lastSepIdx + 1; + final int segmentLen = i - segmentStartIdx; + if (segmentLen == 0 || (segmentLen == 1 && pathChars[segmentStartIdx] == '.')) { + // Ignore empty segment "//" or idempotent segment "/./" + } else if (segmentLen == 2 && pathChars[segmentStartIdx] == '.' + && pathChars[segmentStartIdx + 1] == '.') { // Remove one segment if ".." encountered, but do not allow ".." above top of hierarchy if (!currSectionSegments.isEmpty()) { currSectionSegments.remove(currSectionSegments.size() - 1); } } else { // Encountered normal path segment - currSectionSegments.add(segment); + currSectionSegments.add(path.subSequence(segmentStartIdx, segmentStartIdx + segmentLen)); } if (c == '!' && !currSectionSegments.isEmpty()) { // Begin new section @@ -377,13 +379,13 @@ public static String sanitizeEntryPath(final String path, final boolean removeIn } } // Turn sections and segments back into path string - for (final List sectionSegments : allSectionSegments) { + for (final List sectionSegments : allSectionSegments) { if (!sectionSegments.isEmpty()) { // Delineate segments with "!" if (pathSanitized.length() > 0) { pathSanitized.append('!'); } - for (final String sectionSegment : sectionSegments) { + for (final CharSequence sectionSegment : sectionSegments) { pathSanitized.append('/'); pathSanitized.append(sectionSegment); } From 672f93d64c539b50fc81338739c699595ebe95f1 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 25 Nov 2019 02:39:33 -0700 Subject: [PATCH 0461/1778] Speed up zip entry processing by making date and time parsing lazy --- .../classgraph/ClasspathElementZip.java | 3 +- .../fastzipfilereader/FastZipEntry.java | 55 +++++++++++++++++-- .../fastzipfilereader/LogicalZipFile.java | 26 ++------- 3 files changed, 58 insertions(+), 26 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index 227eeb1fb..196da0389 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -283,7 +283,8 @@ void open(final WorkQueue workQueue, final int classpath * @return the resource */ private Resource newResource(final FastZipEntry zipEntry, final String pathRelativeToPackageRoot) { - return new Resource(this, zipEntry.uncompressedSize, zipEntry.lastModified, zipEntry.posixFilePermissions) { + return new Resource(this, zipEntry.uncompressedSize, zipEntry.getLastModifiedTimeMillis(), + zipEntry.posixFilePermissions) { /** * Path with package root prefix and/or any Spring Boot prefix ("BOOT-INF/classes/" or * "WEB-INF/classes/") removed. 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 c47cd1581..83dcc3412 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/FastZipEntry.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/FastZipEntry.java @@ -35,7 +35,9 @@ import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.nio.file.attribute.PosixFilePermission; +import java.util.Calendar; import java.util.Set; +import java.util.TimeZone; import java.util.concurrent.atomic.AtomicBoolean; import java.util.zip.DataFormatException; import java.util.zip.Inflater; @@ -69,7 +71,13 @@ public class FastZipEntry implements Comparable { public final long uncompressedSize; /** The last modified millis since the epoch, or 0L if it is unknown */ - public final long lastModified; + private long lastModifiedTimeMillis; + + /** The last modified time in MSDOS format, if {@link FastZipEntry#lastModifiedTimeMillis} is 0L. */ + private final int lastModifiedTimeMSDOS; + + /** The last modified date in MSDOS format, if {@link FastZipEntry#lastModifiedTimeMillis} is 0L. */ + private final int lastModifiedDateMSDOS; /** The file permissions for this resource, or null if unknown */ public final Set posixFilePermissions; @@ -109,14 +117,20 @@ public class FastZipEntry implements Comparable { * The uncompressed size of the entry. * @param nestedJarHandler * The {@link NestedJarHandler}. - * @param lastModified - * The last modified date/time in millis since the epoch + * @param lastModifiedTimeMillis + * The last modified date/time in millis since the epoch, or 0L if unknown (in which case, the MSDOS + * time and date fields will be provided). + * @param lastModifiedTimeMSDOS + * The last modified date, in MSDOS format, if lastModifiedMillis is 0L. + * @param lastModifiedDateMSDOS + * The last modified date, in MSDOS format, if lastModifiedMillis is 0L. * @param posixFilePermissions * The POSIX file permissions */ FastZipEntry(final LogicalZipFile parentLogicalZipFile, final long locHeaderPos, final String entryName, final boolean isDeflated, final long compressedSize, final long uncompressedSize, - final NestedJarHandler nestedJarHandler, final long lastModified, + final NestedJarHandler nestedJarHandler, final long lastModifiedTimeMillis, + final int lastModifiedTimeMSDOS, final int lastModifiedDateMSDOS, final Set posixFilePermissions) { this.parentLogicalZipFile = parentLogicalZipFile; this.locHeaderPos = locHeaderPos; @@ -125,7 +139,9 @@ public class FastZipEntry implements Comparable { this.compressedSize = compressedSize; this.uncompressedSize = !isDeflated && uncompressedSize < 0 ? compressedSize : uncompressedSize; this.nestedJarHandler = nestedJarHandler; - this.lastModified = lastModified; + this.lastModifiedTimeMillis = lastModifiedTimeMillis; + this.lastModifiedTimeMSDOS = lastModifiedTimeMSDOS; + this.lastModifiedDateMSDOS = lastModifiedDateMSDOS; this.posixFilePermissions = posixFilePermissions; // Get multi-release jar version number, and strip any version prefix @@ -613,6 +629,35 @@ public String getPath() { return parentLogicalZipFile.getPath() + "!/" + entryName; } + /** + * Get the last modified time in Epoch millis, or 0L if unknown. + * + * @return the last modified time in Epoch millis. + */ + public long getLastModifiedTimeMillis() { + // If lastModifiedTimeMillis is zero, but there is an MSDOS date and time available + if (lastModifiedTimeMillis == 0L && (lastModifiedDateMSDOS != 0 || lastModifiedTimeMSDOS != 0)) { + // Convert from MS-DOS Date & Time Format to Epoch millis + final int lastModifiedSecond = (lastModifiedTimeMSDOS & 0b11111) * 2; + final int lastModifiedMinute = lastModifiedTimeMSDOS >> 5 & 0b111111; + final int lastModifiedHour = lastModifiedTimeMSDOS >> 11; + final int lastModifiedDay = lastModifiedDateMSDOS & 0b11111; + final int lastModifiedMonth = (lastModifiedDateMSDOS >> 5 & 0b111) - 1; + final int lastModifiedYear = (lastModifiedDateMSDOS >> 9) + 1980; + + final Calendar lastModifiedCalendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + lastModifiedCalendar.set(lastModifiedYear, lastModifiedMonth, lastModifiedDay, lastModifiedHour, + lastModifiedMinute, lastModifiedSecond); + lastModifiedCalendar.set(Calendar.MILLISECOND, 0); + + // Cache converted time by overwriting the zero lastModifiedTimeMillis field + lastModifiedTimeMillis = lastModifiedCalendar.getTimeInMillis(); + } + + // Return the last modified time, or 0L if it is totally unknown. + return lastModifiedTimeMillis; + } + /* (non-Javadoc) * @see java.lang.Object#toString() */ 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 35c2f8a5f..eba0bdddf 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java @@ -40,7 +40,6 @@ import java.nio.file.attribute.PosixFilePermission; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; -import java.util.Calendar; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -48,7 +47,6 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import java.util.TimeZone; import java.util.concurrent.ConcurrentHashMap; import io.github.classgraph.ClassGraphException; @@ -770,29 +768,17 @@ private void readCentralDirectory(final ZipFileSliceReader zipFileSliceReader, f } } + int lastModifiedTimeMSDOS = 0; + int lastModifiedDateMSDOS = 0; if (lastModifiedMillis == 0L) { - // If Unix timestamp was not provided, convertr zip entry timestamp from MS-DOS format - final int lastModifiedTime = entryBytes != null + // If Unix timestamp was not provided, convert zip entry timestamp from MS-DOS format + lastModifiedTimeMSDOS = entryBytes != null ? ZipFileSliceReader.getShort(entryBytes, entOff + 12) : zipFileSliceReader.getShort(cenPos + entOff + 12); - final int lastModifiedDate = entryBytes != null + lastModifiedDateMSDOS = entryBytes != null ? ZipFileSliceReader.getShort(entryBytes, entOff + 14) : zipFileSliceReader.getShort(cenPos + entOff + 14); - - // MS-DOS Date & Time Format - final int lastModifiedSecond = (lastModifiedTime & 0b11111) * 2; - final int lastModifiedMinute = lastModifiedTime >> 5 & 0b111111; - final int lastModifiedHour = lastModifiedTime >> 11; - final int lastModifiedDay = lastModifiedDate & 0b11111; - final int lastModifiedMonth = (lastModifiedDate >> 5 & 0b111) - 1; - final int lastModifiedYear = (lastModifiedDate >> 9) + 1980; - - final Calendar lastModifiedCalendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - lastModifiedCalendar.set(lastModifiedYear, lastModifiedMonth, lastModifiedDay, lastModifiedHour, - lastModifiedMinute, lastModifiedSecond); - lastModifiedCalendar.set(Calendar.MILLISECOND, 0); - lastModifiedMillis = lastModifiedCalendar.getTimeInMillis(); } if (compressedSize < 0 || pos < 0) { @@ -816,7 +802,7 @@ private void readCentralDirectory(final ZipFileSliceReader zipFileSliceReader, f // Add zip entry final FastZipEntry entry = new FastZipEntry(this, locHeaderPos, entryNameSanitized, isDeflated, compressedSize, uncompressedSize, physicalZipFile.nestedJarHandler, lastModifiedMillis, - perms); + lastModifiedTimeMSDOS, lastModifiedDateMSDOS, perms); entries.add(entry); // Record manifest entry From cd776eaef7bd4ea405f997883006e7147c948d7d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 25 Nov 2019 02:47:27 -0700 Subject: [PATCH 0462/1778] Update Javadoc --- src/main/java/io/github/classgraph/MethodInfoList.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/classgraph/MethodInfoList.java b/src/main/java/io/github/classgraph/MethodInfoList.java index 50bd018fd..880743556 100644 --- a/src/main/java/io/github/classgraph/MethodInfoList.java +++ b/src/main/java/io/github/classgraph/MethodInfoList.java @@ -144,8 +144,8 @@ public boolean containsName(final String methodName) { * @param methodName * The name of a method. * @return A list of {@link MethodInfo} objects in the list with the given name (there may be more than one - * method with a given name, due to overloading). Returns the empty list if no method had a matching - * name. + * method with a given name, due to overloading, so this returns a {@link MethodInfoList} rather than a + * single {@link MethodInfo}). Returns the empty list if no method had a matching name. */ public MethodInfoList get(final String methodName) { boolean hasMethodWithName = false; From 41ee5ce4be1c215857cf8ac7eea3eb4bc6e7c6ad Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 25 Nov 2019 02:49:38 -0700 Subject: [PATCH 0463/1778] Update Javadoc --- src/main/java/io/github/classgraph/MethodInfoList.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/github/classgraph/MethodInfoList.java b/src/main/java/io/github/classgraph/MethodInfoList.java index 880743556..40d489c5c 100644 --- a/src/main/java/io/github/classgraph/MethodInfoList.java +++ b/src/main/java/io/github/classgraph/MethodInfoList.java @@ -139,13 +139,14 @@ public boolean containsName(final String methodName) { /** * Returns a list of all methods matching a given name. (There may be more than one method with a given name, - * due to overloading.) + * due to overloading, so this returns a {@link MethodInfoList} rather than a single {@link MethodInfo}.) * * @param methodName * The name of a method. - * @return A list of {@link MethodInfo} objects in the list with the given name (there may be more than one - * method with a given name, due to overloading, so this returns a {@link MethodInfoList} rather than a - * single {@link MethodInfo}). Returns the empty list if no method had a matching name. + * @return A {@link MethodInfoList} of {@link MethodInfo} objects from this list that have the given name (there + * may be more than one method with a given name, due to overloading, so this returns a + * {@link MethodInfoList} rather than a single {@link MethodInfo}). Returns the empty list if no method + * had a matching name. */ public MethodInfoList get(final String methodName) { boolean hasMethodWithName = false; From 527c17785aef919ec5488f8ed0bf3836866f652c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 25 Nov 2019 02:52:02 -0700 Subject: [PATCH 0464/1778] Add get(resourcePath) method --- .../io/github/classgraph/ResourceList.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/main/java/io/github/classgraph/ResourceList.java b/src/main/java/io/github/classgraph/ResourceList.java index a90f3efa6..1924a6f65 100644 --- a/src/main/java/io/github/classgraph/ResourceList.java +++ b/src/main/java/io/github/classgraph/ResourceList.java @@ -91,6 +91,39 @@ public ResourceList(final Collection resourceCollection) { super(resourceCollection); } + /** + * Returns a list of all resources with the requested path. (There may be more than one resource with a given + * path, from different classpath elements or modules, so this returns a {@link ResourceList} rather than a + * single {@link Resource}.) + * + * @param resourcePath + * The path of a resource + * @return A {@link ResourceList} of {@link Resource} objects in this list that have the given path (there may + * be more than one resource with a given path, from different classpath elements or modules, so this + * returns a {@link ResourceList} rather than a single {@link Resource}.) Returns the empty list if no + * resource with is found with a matching path. + */ + public ResourceList get(final String resourcePath) { + boolean hasResourceWithPath = false; + for (final Resource res : this) { + if (res.getPath().equals(resourcePath)) { + hasResourceWithPath = true; + break; + } + } + if (!hasResourceWithPath) { + return EMPTY_LIST; + } else { + final ResourceList matchingResources = new ResourceList(2); + for (final Resource res : this) { + if (res.getPath().equals(resourcePath)) { + matchingResources.add(res); + } + } + return matchingResources; + } + } + // ------------------------------------------------------------------------------------------------------------- /** From 0ef62dfce718925f5b35ebb15acd34fa35a28888 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 25 Nov 2019 02:55:17 -0700 Subject: [PATCH 0465/1778] Optimize .getResourcesWithPath() by not building a map --- 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 8413dfff9..14e6d865e 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -508,7 +508,7 @@ public ResourceList getResourcesWithPath(final String resourcePath) { return ResourceList.EMPTY_LIST; } else { final String path = FileUtils.sanitizeEntryPath(resourcePath, /* removeInitialSlash = */ true); - final ResourceList resourceList = getAllResourcesAsMap().get(path); + final ResourceList resourceList = allWhitelistedResources.get(path); return (resourceList == null ? new ResourceList(1) : resourceList); } } From c3255883c4d82fbac2cc9211a12f85d7961368cc Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 25 Nov 2019 03:03:35 -0700 Subject: [PATCH 0466/1778] Speed up getResourcesWithPath --- src/main/java/io/github/classgraph/ScanResult.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index 14e6d865e..85f40cdeb 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -46,6 +46,7 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -72,6 +73,9 @@ public final class ScanResult implements Closeable, AutoCloseable { /** A list of all files that were found in whitelisted packages. */ private ResourceList allWhitelistedResourcesCached; + /** 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. */ @@ -508,7 +512,14 @@ public ResourceList getResourcesWithPath(final String resourcePath) { return ResourceList.EMPTY_LIST; } else { final String path = FileUtils.sanitizeEntryPath(resourcePath, /* removeInitialSlash = */ true); - final ResourceList resourceList = allWhitelistedResources.get(path); + ResourceList resourceList; + if (getResourcesWithPathCallCount.incrementAndGet() > 3) { + // If numerous calls are made, produce and cache a HashMap for O(1) access time + resourceList = getAllResourcesAsMap().get(path); + } else { + // If just a few calls are made, use O(N) search through list + resourceList = allWhitelistedResources.get(path); + } return (resourceList == null ? new ResourceList(1) : resourceList); } } From cd213efa8490cbe6862ef1a8f3debd461a6f5603 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 25 Nov 2019 03:44:39 -0700 Subject: [PATCH 0467/1778] Optimize getResourcesWithPath() --- .../github/classgraph/ClasspathElement.java | 15 ++++--- .../java/io/github/classgraph/ScanResult.java | 43 +++++++++++++------ 2 files changed, 39 insertions(+), 19 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElement.java b/src/main/java/io/github/classgraph/ClasspathElement.java index e5d82db4f..850ffa56f 100644 --- a/src/main/java/io/github/classgraph/ClasspathElement.java +++ b/src/main/java/io/github/classgraph/ClasspathElement.java @@ -81,8 +81,11 @@ abstract class ClasspathElement { */ List childClasspathElementsOrdered; - /** The list of all resources found within this classpath element that were whitelisted and not blacklisted. */ - protected final Collection whitelistedResources = new ConcurrentLinkedQueue<>(); + /** + * A map from path to {@link Resource} for resources found within this classpath element that were whitelisted + * and not blacklisted. + */ + protected final Map pathToWhitelistedResource = new ConcurrentHashMap<>(); /** The list of all classfiles found within this classpath element that were whitelisted and not blacklisted. */ protected Collection whitelistedClassfileResources = new ConcurrentLinkedQueue<>(); @@ -259,8 +262,8 @@ protected void addWhitelistedResource(final Resource resource, final ScanSpecPat } if (!isClassfileOnly) { - // Add resource to whitelistedResources, whether for a classfile or non-classfile resource - whitelistedResources.add(resource); + // Put resource into pathToWhitelistedResource map, whether for a classfile or non-classfile resource + pathToWhitelistedResource.put(resource.getPath(), resource); } // Write to log if enabled, and as long as classfile scanning is not disabled, and this is not @@ -300,10 +303,10 @@ protected void addWhitelistedResource(final Resource resource, final ScanSpecPat */ protected void finishScanPaths(final LogNode log) { if (log != null) { - if (whitelistedResources.isEmpty() && whitelistedClassfileResources.isEmpty()) { + if (pathToWhitelistedResource.isEmpty() && whitelistedClassfileResources.isEmpty()) { log.log(scanSpec.enableClassInfo ? "No whitelisted classfiles or resources found" : "Classfile scanning is disabled, and no whitelisted resources found"); - } else if (whitelistedResources.isEmpty()) { + } else if (pathToWhitelistedResource.isEmpty()) { log.log("No whitelisted resources found"); } else if (whitelistedClassfileResources.isEmpty()) { log.log(scanSpec.enableClassInfo ? "No whitelisted classfiles found" diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index 85f40cdeb..41da3c149 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -461,8 +461,10 @@ public ResourceList getAllResources() { // Index Resource objects by path final ResourceList whitelistedResourcesList = new ResourceList(); for (final ClasspathElement classpathElt : classpathOrder) { - if (classpathElt.whitelistedResources != null) { - whitelistedResourcesList.addAll(classpathElt.whitelistedResources); + if (classpathElt.pathToWhitelistedResource != null) { + for (final Resource res : classpathElt.pathToWhitelistedResource.values()) { + whitelistedResourcesList.add(res); + } } } // Set atomically for thread safety @@ -507,20 +509,35 @@ public ResourceList getResourcesWithPath(final String resourcePath) { if (closed.get()) { throw new IllegalArgumentException("Cannot use a ScanResult after it has been closed"); } - final ResourceList allWhitelistedResources = getAllResources(); - if (allWhitelistedResources.isEmpty()) { - return ResourceList.EMPTY_LIST; + final String path = FileUtils.sanitizeEntryPath(resourcePath, /* removeInitialSlash = */ true); + if (getResourcesWithPathCallCount.incrementAndGet() > 3) { + // If numerous calls are made, produce and cache a single HashMap for O(1) access time + return getAllResourcesAsMap().get(path); } else { - final String path = FileUtils.sanitizeEntryPath(resourcePath, /* removeInitialSlash = */ true); - ResourceList resourceList; - if (getResourcesWithPathCallCount.incrementAndGet() > 3) { - // If numerous calls are made, produce and cache a HashMap for O(1) access time - resourceList = getAllResourcesAsMap().get(path); + // If just a few calls are made, directly search for resource with the requested path + boolean resourceFound = false; + for (final ClasspathElement classpathElt : classpathOrder) { + if (classpathElt.pathToWhitelistedResource != null) { + if (classpathElt.pathToWhitelistedResource.containsKey(path)) { + resourceFound = true; + break; + } + } + } + if (resourceFound) { + final ResourceList matchingResources = new ResourceList(2); + for (final ClasspathElement classpathElt : classpathOrder) { + if (classpathElt.pathToWhitelistedResource != null) { + final Resource res = classpathElt.pathToWhitelistedResource.get(path); + if (res != null) { + matchingResources.add(res); + } + } + } + return matchingResources; } else { - // If just a few calls are made, use O(N) search through list - resourceList = allWhitelistedResources.get(path); + return ResourceList.EMPTY_LIST; } - return (resourceList == null ? new ResourceList(1) : resourceList); } } From 38b441a1ac2aa71200124e3058f57319a9426154 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 25 Nov 2019 03:56:13 -0700 Subject: [PATCH 0468/1778] More optimization of resource queries --- .../github/classgraph/ClasspathElement.java | 22 +++++++------ .../java/io/github/classgraph/ScanResult.java | 31 ++++++------------- 2 files changed, 22 insertions(+), 31 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElement.java b/src/main/java/io/github/classgraph/ClasspathElement.java index 850ffa56f..fc8f2942d 100644 --- a/src/main/java/io/github/classgraph/ClasspathElement.java +++ b/src/main/java/io/github/classgraph/ClasspathElement.java @@ -31,7 +31,6 @@ 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; @@ -82,13 +81,16 @@ abstract class ClasspathElement { List childClasspathElementsOrdered; /** - * A map from path to {@link Resource} for resources found within this classpath element that were whitelisted - * and not blacklisted. + * Resources found within this classpath element that were whitelisted and not blacklisted. (Only written by one + * thread, so doesn't need to be a concurrent list.) */ - protected final Map pathToWhitelistedResource = new ConcurrentHashMap<>(); + protected final List whitelistedResources = new ArrayList<>(); - /** The list of all classfiles found within this classpath element that were whitelisted and not blacklisted. */ - protected Collection whitelistedClassfileResources = new ConcurrentLinkedQueue<>(); + /** + * The list of all classfiles found within this classpath element that were whitelisted and not blacklisted. + * (Only written by one thread, so doesn't need to be a concurrent list.) + */ + protected List whitelistedClassfileResources = new ArrayList<>(); /** The map from File to last modified timestamp, if scanFiles is true. */ protected final Map fileToLastModified = new ConcurrentHashMap<>(); @@ -262,8 +264,8 @@ protected void addWhitelistedResource(final Resource resource, final ScanSpecPat } if (!isClassfileOnly) { - // Put resource into pathToWhitelistedResource map, whether for a classfile or non-classfile resource - pathToWhitelistedResource.put(resource.getPath(), resource); + // Add resource to list of whitelisted resources, whether for a classfile or non-classfile resource + whitelistedResources.add(resource); } // Write to log if enabled, and as long as classfile scanning is not disabled, and this is not @@ -303,10 +305,10 @@ protected void addWhitelistedResource(final Resource resource, final ScanSpecPat */ protected void finishScanPaths(final LogNode log) { if (log != null) { - if (pathToWhitelistedResource.isEmpty() && whitelistedClassfileResources.isEmpty()) { + if (whitelistedResources.isEmpty() && whitelistedClassfileResources.isEmpty()) { log.log(scanSpec.enableClassInfo ? "No whitelisted classfiles or resources found" : "Classfile scanning is disabled, and no whitelisted resources found"); - } else if (pathToWhitelistedResource.isEmpty()) { + } else if (whitelistedResources.isEmpty()) { log.log("No whitelisted resources found"); } else if (whitelistedClassfileResources.isEmpty()) { log.log(scanSpec.enableClassInfo ? "No whitelisted classfiles found" diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index 41da3c149..6b283114f 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -461,10 +461,8 @@ public ResourceList getAllResources() { // Index Resource objects by path final ResourceList whitelistedResourcesList = new ResourceList(); for (final ClasspathElement classpathElt : classpathOrder) { - if (classpathElt.pathToWhitelistedResource != null) { - for (final Resource res : classpathElt.pathToWhitelistedResource.values()) { - whitelistedResourcesList.add(res); - } + if (classpathElt.whitelistedResources != null) { + whitelistedResourcesList.addAll(classpathElt.whitelistedResources); } } // Set atomically for thread safety @@ -515,29 +513,20 @@ public ResourceList getResourcesWithPath(final String resourcePath) { return getAllResourcesAsMap().get(path); } else { // If just a few calls are made, directly search for resource with the requested path - boolean resourceFound = false; + ResourceList matchingResources = null; for (final ClasspathElement classpathElt : classpathOrder) { - if (classpathElt.pathToWhitelistedResource != null) { - if (classpathElt.pathToWhitelistedResource.containsKey(path)) { - resourceFound = true; - break; - } - } - } - if (resourceFound) { - final ResourceList matchingResources = new ResourceList(2); - for (final ClasspathElement classpathElt : classpathOrder) { - if (classpathElt.pathToWhitelistedResource != null) { - final Resource res = classpathElt.pathToWhitelistedResource.get(path); - if (res != null) { + if (classpathElt.whitelistedResources != null) { + for (final Resource res : classpathElt.whitelistedResources) { + if (res.getPath().equals(path)) { + if (matchingResources == null) { + matchingResources = new ResourceList(); + } matchingResources.add(res); } } } - return matchingResources; - } else { - return ResourceList.EMPTY_LIST; } + return matchingResources == null ? ResourceList.EMPTY_LIST : matchingResources; } } From 2ee1450fcb888d7241daf6089cc2d18348451f3a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 25 Nov 2019 04:00:29 -0700 Subject: [PATCH 0469/1778] Hide Resource constructor --- src/main/java/io/github/classgraph/Resource.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/Resource.java b/src/main/java/io/github/classgraph/Resource.java index 7d4fb3c03..673239741 100644 --- a/src/main/java/io/github/classgraph/Resource.java +++ b/src/main/java/io/github/classgraph/Resource.java @@ -111,7 +111,7 @@ public Resource(final ClasspathElement classpathElement, final long length) { * @param posixFilePermissions * The POSIX file permissions, or null if unknown. */ - public Resource(final ClasspathElement classpathElement, final long length, final long lastModified, + Resource(final ClasspathElement classpathElement, final long length, final long lastModified, final Set posixFilePermissions) { this.classpathElement = classpathElement; this.length = length; From b5ee1b6db47b1c9e7e81c3efd667f6db48d4f373 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 25 Nov 2019 04:09:58 -0700 Subject: [PATCH 0470/1778] Make lazy the processing of file attributes and last modified time --- .../classgraph/ClasspathElementDir.java | 29 +++++++---- .../classgraph/ClasspathElementModule.java | 11 +++++ .../classgraph/ClasspathElementZip.java | 48 ++++++++++++++++++- .../java/io/github/classgraph/Resource.java | 36 +------------- .../fastzipfilereader/FastZipEntry.java | 15 +++--- .../fastzipfilereader/LogicalZipFile.java | 40 +--------------- 6 files changed, 87 insertions(+), 92 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementDir.java b/src/main/java/io/github/classgraph/ClasspathElementDir.java index d0882c64c..a1d72817f 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementDir.java @@ -156,15 +156,7 @@ void open(final WorkQueue workQueue, final int classpath * @return the resource */ private Resource newResource(final String relativePath, final File classpathResourceFile) { - Set posixFilePermissions = null; - try { - posixFilePermissions = Files.readAttributes(classpathResourceFile.toPath(), PosixFileAttributes.class) - .permissions(); - } catch (UnsupportedOperationException | IOException | SecurityException e) { - // POSIX attributes not supported - } - return new Resource(this, classpathResourceFile.length(), classpathResourceFile.lastModified(), - posixFilePermissions) { + return new Resource(this, classpathResourceFile.length()) { /** The random access file. */ private RandomAccessFile randomAccessFile; @@ -181,6 +173,25 @@ public String getPathRelativeToClasspathElement() { return relativePath; } + @Override + public long getLastModified() { + return classpathResourceFile.lastModified(); + } + + @SuppressWarnings("null") + @Override + public Set getPosixFilePermissions() { + Set posixFilePermissions = null; + try { + posixFilePermissions = Files + .readAttributes(classpathResourceFile.toPath(), PosixFileAttributes.class) + .permissions(); + } catch (UnsupportedOperationException | IOException | SecurityException e) { + // POSIX attributes not supported + } + return posixFilePermissions; + } + @Override public synchronized ByteBuffer read() throws IOException { if (skipClasspathElement) { diff --git a/src/main/java/io/github/classgraph/ClasspathElementModule.java b/src/main/java/io/github/classgraph/ClasspathElementModule.java index 4e09a166d..18e9331bf 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementModule.java +++ b/src/main/java/io/github/classgraph/ClasspathElementModule.java @@ -33,6 +33,7 @@ import java.io.InputStream; import java.net.URI; import java.nio.ByteBuffer; +import java.nio.file.attribute.PosixFilePermission; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -133,6 +134,16 @@ public String getPathRelativeToClasspathElement() { return resourcePath; } + @Override + public long getLastModified() { + return 0L; // Unknown + } + + @Override + public Set getPosixFilePermissions() { + return null; // N/A + } + @Override public synchronized ByteBuffer read() throws IOException { if (skipClasspathElement) { diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index 196da0389..91dc144a5 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -34,6 +34,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.nio.ByteBuffer; +import java.nio.file.attribute.PosixFilePermission; import java.util.AbstractMap.SimpleEntry; import java.util.HashSet; import java.util.Map.Entry; @@ -283,8 +284,7 @@ void open(final WorkQueue workQueue, final int classpath * @return the resource */ private Resource newResource(final FastZipEntry zipEntry, final String pathRelativeToPackageRoot) { - return new Resource(this, zipEntry.uncompressedSize, zipEntry.getLastModifiedTimeMillis(), - zipEntry.posixFilePermissions) { + return new Resource(this, zipEntry.uncompressedSize) { /** * Path with package root prefix and/or any Spring Boot prefix ("BOOT-INF/classes/" or * "WEB-INF/classes/") removed. @@ -299,6 +299,50 @@ public String getPathRelativeToClasspathElement() { return zipEntry.entryName; } + @Override + public long getLastModified() { + return zipEntry.getLastModifiedTimeMillis(); + } + + @Override + public Set getPosixFilePermissions() { + final int fileAttributes = zipEntry.fileAttributes; + Set perms; + if (fileAttributes == 0) { + perms = null; + } else { + perms = new HashSet<>(); + if ((fileAttributes & 0400) > 0) { + perms.add(PosixFilePermission.OWNER_READ); + } + if ((fileAttributes & 0200) > 0) { + perms.add(PosixFilePermission.OWNER_WRITE); + } + if ((fileAttributes & 0100) > 0) { + perms.add(PosixFilePermission.OWNER_EXECUTE); + } + if ((fileAttributes & 0040) > 0) { + perms.add(PosixFilePermission.GROUP_READ); + } + if ((fileAttributes & 0020) > 0) { + perms.add(PosixFilePermission.GROUP_WRITE); + } + if ((fileAttributes & 0010) > 0) { + perms.add(PosixFilePermission.GROUP_EXECUTE); + } + if ((fileAttributes & 0004) > 0) { + perms.add(PosixFilePermission.OTHERS_READ); + } + if ((fileAttributes & 0002) > 0) { + perms.add(PosixFilePermission.OTHERS_WRITE); + } + if ((fileAttributes & 0001) > 0) { + perms.add(PosixFilePermission.OTHERS_EXECUTE); + } + } + return perms; + } + @Override public synchronized InputStream open() throws IOException { if (skipClasspathElement) { diff --git a/src/main/java/io/github/classgraph/Resource.java b/src/main/java/io/github/classgraph/Resource.java index 673239741..e96e06259 100644 --- a/src/main/java/io/github/classgraph/Resource.java +++ b/src/main/java/io/github/classgraph/Resource.java @@ -70,12 +70,6 @@ public abstract class Resource implements Closeable, Comparable { /** The cached result of toString(). */ private String toString; - /** The last modified millis since the epoch, or 0L if it is unknown */ - private final long lastModified; - - /** The file permissions for this resource, or null if unknown */ - private final Set posixFilePermissions; - /** * The {@link LogNode} used to log that the resource was found when classpath element paths are scanned. In the * case of whitelisted classfile resources, sublog entries are added when the classfile's contents are scanned. @@ -95,28 +89,6 @@ public abstract class Resource implements Closeable, Comparable { public Resource(final ClasspathElement classpathElement, final long length) { this.classpathElement = classpathElement; this.length = length; - this.lastModified = 0L; - this.posixFilePermissions = null; - } - - /** - * Constructor. - * - * @param classpathElement - * the classpath element this resource was obtained from. - * @param length - * the length the length of the resource. - * @param lastModified - * The last modified date, in milliseconds since the epoch, or 0 if unknown. - * @param posixFilePermissions - * The POSIX file permissions, or null if unknown. - */ - Resource(final ClasspathElement classpathElement, final long length, final long lastModified, - final Set posixFilePermissions) { - this.classpathElement = classpathElement; - this.length = length; - this.lastModified = lastModified; - this.posixFilePermissions = posixFilePermissions; } // ------------------------------------------------------------------------------------------------------------- @@ -554,9 +526,7 @@ public long getLength() { * @return The millis since the epoch indicating the date / time that this file resource was last modified. * Returns 0L if the last modified date is unknown. */ - public long getLastModified() { - return lastModified; - } + public abstract long getLastModified(); /** * Get the POSIX file permissions for the resource. POSIX file permissions are obtained from the directory @@ -566,9 +536,7 @@ public long getLastModified() { * * @return The set of {@link PosixFilePermission} permission flags for the resource, or null if unknown. */ - public Set getPosixFilePermissions() { - return posixFilePermissions; - } + public abstract Set getPosixFilePermissions(); // ------------------------------------------------------------------------------------------------------------- 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 83dcc3412..dc110aca6 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/FastZipEntry.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/FastZipEntry.java @@ -34,9 +34,7 @@ import java.nio.Buffer; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; -import java.nio.file.attribute.PosixFilePermission; import java.util.Calendar; -import java.util.Set; import java.util.TimeZone; import java.util.concurrent.atomic.AtomicBoolean; import java.util.zip.DataFormatException; @@ -79,8 +77,8 @@ public class FastZipEntry implements Comparable { /** The last modified date in MSDOS format, if {@link FastZipEntry#lastModifiedTimeMillis} is 0L. */ private final int lastModifiedDateMSDOS; - /** The file permissions for this resource, or null if unknown */ - public final Set posixFilePermissions; + /** The file attributes for this resource, or 0 if unknown */ + public final int fileAttributes; /** * The version code (>= 9), or 8 for the base layer or a non-versioned jar (whether JDK 7 or 8 compatible). @@ -124,14 +122,13 @@ public class FastZipEntry implements Comparable { * The last modified date, in MSDOS format, if lastModifiedMillis is 0L. * @param lastModifiedDateMSDOS * The last modified date, in MSDOS format, if lastModifiedMillis is 0L. - * @param posixFilePermissions - * The POSIX file permissions + * @param fileAttributes + * The POSIX file attribute bits from the zip entry. */ FastZipEntry(final LogicalZipFile parentLogicalZipFile, final long locHeaderPos, final String entryName, final boolean isDeflated, final long compressedSize, final long uncompressedSize, final NestedJarHandler nestedJarHandler, final long lastModifiedTimeMillis, - final int lastModifiedTimeMSDOS, final int lastModifiedDateMSDOS, - final Set posixFilePermissions) { + final int lastModifiedTimeMSDOS, final int lastModifiedDateMSDOS, final int fileAttributes) { this.parentLogicalZipFile = parentLogicalZipFile; this.locHeaderPos = locHeaderPos; this.entryName = entryName; @@ -142,7 +139,7 @@ public class FastZipEntry implements Comparable { this.lastModifiedTimeMillis = lastModifiedTimeMillis; this.lastModifiedTimeMSDOS = lastModifiedTimeMSDOS; this.lastModifiedDateMSDOS = lastModifiedDateMSDOS; - this.posixFilePermissions = posixFilePermissions; + this.fileAttributes = fileAttributes; // Get multi-release jar version number, and strip any version prefix int entryVersion = 8; 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 eba0bdddf..b4ad5627b 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java @@ -37,7 +37,6 @@ import java.nio.charset.CharsetDecoder; import java.nio.charset.CodingErrorAction; import java.nio.charset.StandardCharsets; -import java.nio.file.attribute.PosixFilePermission; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Collections; @@ -624,44 +623,9 @@ private void readCentralDirectory(final ZipFileSliceReader zipFileSliceReader, f : zipFileSliceReader.getInt(cenPos + entOff + 24)) & 0xffffffffL; // Get external file attributes - final int externalFileAttributes = entryBytes != null - ? ZipFileSliceReader.getShort(entryBytes, entOff + 40) + final int fileAttributes = entryBytes != null ? ZipFileSliceReader.getShort(entryBytes, entOff + 40) : zipFileSliceReader.getShort(cenPos + entOff + 40); - Set perms; - if (externalFileAttributes == 0) { - perms = null; - } else { - perms = new HashSet<>(); - if ((externalFileAttributes & 0400) > 0) { - perms.add(PosixFilePermission.OWNER_READ); - } - if ((externalFileAttributes & 0200) > 0) { - perms.add(PosixFilePermission.OWNER_WRITE); - } - if ((externalFileAttributes & 0100) > 0) { - perms.add(PosixFilePermission.OWNER_EXECUTE); - } - if ((externalFileAttributes & 0040) > 0) { - perms.add(PosixFilePermission.GROUP_READ); - } - if ((externalFileAttributes & 0020) > 0) { - perms.add(PosixFilePermission.GROUP_WRITE); - } - if ((externalFileAttributes & 0010) > 0) { - perms.add(PosixFilePermission.GROUP_EXECUTE); - } - if ((externalFileAttributes & 0004) > 0) { - perms.add(PosixFilePermission.OTHERS_READ); - } - if ((externalFileAttributes & 0002) > 0) { - perms.add(PosixFilePermission.OTHERS_WRITE); - } - if ((externalFileAttributes & 0001) > 0) { - perms.add(PosixFilePermission.OTHERS_EXECUTE); - } - } - long pos = entryBytes != null ? ZipFileSliceReader.getInt(entryBytes, entOff + 42) : zipFileSliceReader.getInt(cenPos + entOff + 42); @@ -802,7 +766,7 @@ private void readCentralDirectory(final ZipFileSliceReader zipFileSliceReader, f // Add zip entry final FastZipEntry entry = new FastZipEntry(this, locHeaderPos, entryNameSanitized, isDeflated, compressedSize, uncompressedSize, physicalZipFile.nestedJarHandler, lastModifiedMillis, - lastModifiedTimeMSDOS, lastModifiedDateMSDOS, perms); + lastModifiedTimeMSDOS, lastModifiedDateMSDOS, fileAttributes); entries.add(entry); // Record manifest entry From 5a7d550c4707f0930e1282344edfd50631e87a1d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 25 Nov 2019 04:20:38 -0700 Subject: [PATCH 0471/1778] Raise inflate buffer size from 1kb to 1Mb --- .../io/github/classgraph/fastzipfilereader/FastZipEntry.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 dc110aca6..873f1ef5d 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/FastZipEntry.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/FastZipEntry.java @@ -319,7 +319,7 @@ public InputStream open() throws IOException, InterruptedException { private final AtomicBoolean closed = new AtomicBoolean(false); /** The size of the {@link Inflate} buffer to use. */ - private static final int INFLATE_BUF_SIZE = 1024; + private static final int INFLATE_BUF_SIZE = 1024 * 1024; // Open the first 2GB chunk. { From 0ae3353f57d8e44cfe0c971d085a472c8b59db27 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 25 Nov 2019 04:33:40 -0700 Subject: [PATCH 0472/1778] Optimize skip() --- .../fastzipfilereader/FastZipEntry.java | 56 +++++++++++++++---- 1 file changed, 46 insertions(+), 10 deletions(-) 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 873f1ef5d..8efd05caf 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/FastZipEntry.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/FastZipEntry.java @@ -298,7 +298,7 @@ public InputStream open() throws IOException, InterruptedException { private final long dataStartOffsetWithinPhysicalZipFile = getEntryDataStartOffsetWithinPhysicalZipFile(); /** A scratch buffer. */ - private final byte[] scratch = new byte[8192]; + private final byte[] scratch = new byte[64 * 1024]; /** The current 2GB chunk of the zip entry. */ private ByteBuffer currChunkByteBuf; @@ -482,7 +482,6 @@ private int readStored(final byte[] buf, final int off, final int len) try { currChunkByteBuf.get(buf, off + read, numBytesRead); } catch (final BufferUnderflowException e) { - // Should not happen throw new EOFException("Unexpected EOF in stored (non-deflated) zip entry data"); } read += numBytesRead; @@ -490,6 +489,38 @@ private int readStored(final byte[] buf, final int off, final int len) return read; } + /** + * Skip stored (non-deflated) data in ByteBuffer. + * + * @param n + * the number of bytes to skip. + * @throws IOException + * if an I/O exception occurred or the thread was interrupted. + */ + private void skipStored(final long n) throws IOException { + try { + long skipped = 0; + while (skipped < n) { + if (!currChunkByteBuf.hasRemaining() && !readNextChunk()) { + throw new EOFException("Unexpected EOF while skipping (non-deflated) zip entry data"); + } + final long remainingToSkip = n - skipped; + final int remainingInBuf = currChunkByteBuf.remaining(); + final int numBytesToSkip = (int) Math.min(Integer.MAX_VALUE - 8, + Math.min(remainingToSkip, remainingInBuf)); + try { + currChunkByteBuf.position(currChunkByteBuf.position() + numBytesToSkip); + } catch (final BufferUnderflowException e) { + throw new EOFException("Unexpected EOF in stored (non-deflated) zip entry data"); + } + skipped += numBytesToSkip; + } + } catch (final InterruptedException e) { + nestedJarHandler.interruptionChecker.interrupt(); + throw new IOException("Thread was interrupted"); + } + } + @Override public int read(final byte[] buf, final int off, final int len) throws IOException { if (closed.get()) { @@ -543,16 +574,21 @@ public long skip(final long n) throws IOException { if (n < 0) { throw new IllegalArgumentException("Invalid skip value"); } - long total = 0; - while (total < n) { - final int numSkipped = read(scratch, 0, (int) Math.min(n - total, scratch.length)); - if (numSkipped == -1) { - eof = true; - break; + if (isDeflated) { + long total = 0; + while (total < n) { + final int bytesToSkip = (int) Math.min(n - total, scratch.length); + final int numSkipped = read(scratch, 0, bytesToSkip); + if (numSkipped == -1) { + eof = true; + break; + } + total += numSkipped; } - total += numSkipped; + } else { + skipStored(n); } - return total; + return n; } @Override From 0ee3230951ae2d79194ad69ab3c97a63b3b5c6b1 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 25 Nov 2019 04:37:40 -0700 Subject: [PATCH 0473/1778] Set inflate buf size to 8k --- .../io/github/classgraph/fastzipfilereader/FastZipEntry.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 8efd05caf..0a18e9f02 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/FastZipEntry.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/FastZipEntry.java @@ -298,7 +298,7 @@ public InputStream open() throws IOException, InterruptedException { private final long dataStartOffsetWithinPhysicalZipFile = getEntryDataStartOffsetWithinPhysicalZipFile(); /** A scratch buffer. */ - private final byte[] scratch = new byte[64 * 1024]; + private final byte[] scratch = new byte[8 * 1024]; /** The current 2GB chunk of the zip entry. */ private ByteBuffer currChunkByteBuf; @@ -319,7 +319,7 @@ public InputStream open() throws IOException, InterruptedException { private final AtomicBoolean closed = new AtomicBoolean(false); /** The size of the {@link Inflate} buffer to use. */ - private static final int INFLATE_BUF_SIZE = 1024 * 1024; + private static final int INFLATE_BUF_SIZE = 8 * 1024; // Open the first 2GB chunk. { From 5a21060f52fb73a51a7b5d9b764696587390dcf6 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 25 Nov 2019 04:38:11 -0700 Subject: [PATCH 0474/1778] Small fix --- .../io/github/classgraph/fastzipfilereader/FastZipEntry.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 0a18e9f02..e82b08747 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/FastZipEntry.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/FastZipEntry.java @@ -506,7 +506,7 @@ private void skipStored(final long n) throws IOException { } final long remainingToSkip = n - skipped; final int remainingInBuf = currChunkByteBuf.remaining(); - final int numBytesToSkip = (int) Math.min(Integer.MAX_VALUE - 8, + final int numBytesToSkip = (int) Math.min(FileUtils.MAX_BUFFER_SIZE, Math.min(remainingToSkip, remainingInBuf)); try { currChunkByteBuf.position(currChunkByteBuf.position() + numBytesToSkip); From c1241282468c6382ef6585fcd1969c087c02872b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 25 Nov 2019 04:47:12 -0700 Subject: [PATCH 0475/1778] Remove redundant checks --- .../fastzipfilereader/ZipFileSliceReader.java | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSliceReader.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSliceReader.java index d3679b64f..e3e7fb3e1 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSliceReader.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSliceReader.java @@ -158,9 +158,6 @@ static byte[] getBytes(final byte[] arr, final long off, final int len) throws I throw new IndexOutOfBoundsException(); } final int ioff = (int) off; - if (ioff < 0 || ioff > arr.length - len) { - throw new IndexOutOfBoundsException(); - } return Arrays.copyOfRange(arr, ioff, ioff + len); } @@ -204,9 +201,6 @@ static byte getByte(final byte[] arr, final long off) throws IndexOutOfBoundsExc throw new IndexOutOfBoundsException(); } final int ioff = (int) off; - if (ioff < 0 || ioff > arr.length - 1) { - throw new IndexOutOfBoundsException(); - } return (byte) (arr[ioff] & 0xff); } @@ -247,9 +241,6 @@ static int getShort(final byte[] arr, final long off) throws IndexOutOfBoundsExc throw new IndexOutOfBoundsException(); } final int ioff = (int) off; - if (ioff < 0 || ioff > arr.length - 2) { - throw new IndexOutOfBoundsException(); - } return ((arr[ioff + 1] & 0xff) << 8) | (arr[ioff] & 0xff); } @@ -290,9 +281,6 @@ static int getInt(final byte[] arr, final long off) throws IOException { throw new IndexOutOfBoundsException(); } final int ioff = (int) off; - if (ioff < 0 || ioff > arr.length - 4) { - throw new IndexOutOfBoundsException(); - } return ((arr[ioff + 3] & 0xff) << 24) // | ((arr[ioff + 2] & 0xff) << 16) // | ((arr[ioff + 1] & 0xff) << 8) // @@ -339,9 +327,6 @@ static long getLong(final byte[] arr, final long off) throws IOException { throw new IndexOutOfBoundsException(); } final int ioff = (int) off; - if (ioff < 0 || ioff > arr.length - 8) { - throw new IndexOutOfBoundsException(); - } return ((arr[ioff + 7] & 0xffL) << 56) // | ((arr[ioff + 6] & 0xffL) << 48) // | ((arr[ioff + 5] & 0xffL) << 40) // @@ -398,9 +383,6 @@ static String getString(final byte[] arr, final long off, final int lenBytes) th throw new IndexOutOfBoundsException(); } final int ioff = (int) off; - if (ioff < 0 || ioff > arr.length - lenBytes) { - throw new IndexOutOfBoundsException(); - } return new String(arr, ioff, lenBytes, StandardCharsets.UTF_8); } From d35c490a02536da13c2cfe977082cdaf8d47c3d3 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 25 Nov 2019 05:11:50 -0700 Subject: [PATCH 0476/1778] Add TODOs to switch `Method` to `MethodHandle` --- .../classloaderhandler/ClassLoaderHandlerRegistry.java | 3 +++ .../java/nonapi/io/github/classgraph/json/ClassFieldCache.java | 2 ++ 2 files changed, 5 insertions(+) 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 9cd21d0b5..4b8acd8ef 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java @@ -139,6 +139,9 @@ public static class ClassLoaderHandlerRegistryEntry { * The ClassLoaderHandler class. */ private ClassLoaderHandlerRegistryEntry(final Class classLoaderHandlerClass) { + // TODO: replace these with MethodHandles for speed + // TODO: (although MethodHandles are disabled for now, due to Animal Sniffer bug): + // https://github.com/mojohaus/animal-sniffer/issues/67 this.classLoaderHandlerClass = classLoaderHandlerClass; try { canHandleMethod = classLoaderHandlerClass.getDeclaredMethod("canHandle", Class.class, 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 f2d4aff0f..9e69e0bad 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/ClassFieldCache.java +++ b/src/main/java/nonapi/io/github/classgraph/json/ClassFieldCache.java @@ -80,6 +80,8 @@ class ClassFieldCache { /** The default constructor for each concrete type. */ // TODO: replace these with constructor MethodHandles for speed + // TODO: (although MethodHandles are disabled for now, due to Animal Sniffer bug): + // https://github.com/mojohaus/animal-sniffer/issues/67 private final Map, Constructor> defaultConstructorForConcreteType = new HashMap<>(); /** The constructor with size hint for each concrete type. */ From 0e06f0fa4d86eada426f31d72b109a587360753e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 25 Nov 2019 06:03:56 -0700 Subject: [PATCH 0477/1778] [maven-release-plugin] prepare release classgraph-4.8.55 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 87b97d701..65f9d7ea3 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.55-SNAPSHOT + 4.8.55 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.55 From c579a853b7bce7c9c31c2f8b4e638278742de507 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 25 Nov 2019 06:04:03 -0700 Subject: [PATCH 0478/1778] [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 65f9d7ea3..652d6fd61 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.55 + 4.8.56-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.55 + HEAD From 7aa03b27bd1a79bb74cd11cad1b91973833f3f3e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 25 Nov 2019 07:34:46 -0700 Subject: [PATCH 0479/1778] Fix version number --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 652d6fd61..87b97d701 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.56-SNAPSHOT + 4.8.55-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From e8bafed81dd168477ce59a1ed0ba99ed49324ab1 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 25 Nov 2019 07:35:44 -0700 Subject: [PATCH 0480/1778] [maven-release-plugin] prepare release classgraph-4.8.55 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 87b97d701..65f9d7ea3 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.55-SNAPSHOT + 4.8.55 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.55 From 6962103bb076fc8e87521f8405fe11102422f623 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 25 Nov 2019 07:35:51 -0700 Subject: [PATCH 0481/1778] [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 65f9d7ea3..652d6fd61 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.55 + 4.8.56-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.55 + HEAD From 62b13d13c44aa5b96734a3af517cc851fd4d7763 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 25 Nov 2019 07:58:57 -0700 Subject: [PATCH 0482/1778] Fix version number --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 652d6fd61..87b97d701 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.56-SNAPSHOT + 4.8.55-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From cf31f2c8ca908d9997b9775677f1967bae86c7a0 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 25 Nov 2019 08:09:50 -0700 Subject: [PATCH 0483/1778] Remove lifecycle listeners (since there is no shutdown hook now anyway) --- .../lifecycle/ServletLifeCycleListener.java | 75 ------------------- .../lifecycle/SpringLifeCycleListener.java | 52 ------------- 2 files changed, 127 deletions(-) delete mode 100644 src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/ServletLifeCycleListener.java delete mode 100644 src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/SpringLifeCycleListener.java diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/ServletLifeCycleListener.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/ServletLifeCycleListener.java deleted file mode 100644 index c794eb764..000000000 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/ServletLifeCycleListener.java +++ /dev/null @@ -1,75 +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.classloaderhandler.lifecycle; - -import java.util.logging.Logger; - -import javax.servlet.ServletContextEvent; -import javax.servlet.ServletContextListener; -import javax.servlet.annotation.WebListener; - -import io.github.classgraph.ClassGraph; -import io.github.classgraph.ScanResult; - -/** - * Register a {@link WebListener} to respond to servlet context shutdown by closing any remaining open ScanResult - * instances (#376). This creates classfile references to javax.servlet classes, however this class is never - * referenced by any other class in ClassGraph (it is only included in the classpath so that the servlet container - * can locate it using the {@link WebListener} annotation). Therefore ClassGraph has only a compile-time - * ("provides"-scoped) dependency on the servlet container to enable the container to find this class. - */ -@WebListener -public class ServletLifeCycleListener implements ServletContextListener { - /** The logger. */ - private final Logger log = Logger.getLogger(ClassGraph.class.getName()); - - /** - * Context initialized. - * - * @param event - * the event - */ - @Override - public void contextInitialized(final ServletContextEvent event) { - log.info("Servlet context initialized"); - } - - /** - * Context destroyed. - * - * @param event - * the event - */ - @Override - public void contextDestroyed(final ServletContextEvent event) { - // Cleanly close down any open {@link ScanResult} instances. - log.info("Servlet context destroyed -- closing any remaning open ClassGraph ScanResult instances"); - ScanResult.closeAll(); - } -} \ No newline at end of file diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/SpringLifeCycleListener.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/SpringLifeCycleListener.java deleted file mode 100644 index 52811a50b..000000000 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/lifecycle/SpringLifeCycleListener.java +++ /dev/null @@ -1,52 +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.classloaderhandler.lifecycle; - -import javax.servlet.ServletContextListener; - -import org.springframework.boot.context.embedded.ServletListenerRegistrationBean; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -/** - * Register a {@link ServletContextListener} to respond to Spring context shutdown by closing any remaining open - * ScanResult instances (#376). This creates classfile references to Spring and servlet classes, however this class - * is never actually referenced by any other class in ClassGraph (it is only included in the classpath so that - * Spring can locate it using the {@link Configuration} annotation). Therefore ClassGraph has only a compile-time - * ("provides"-scoped) dependency on Spring to enable Spring to find this class. - */ -@Configuration -public class SpringLifeCycleListener { - @Bean - ServletListenerRegistrationBean servletListener() { - final ServletListenerRegistrationBean srb = new ServletListenerRegistrationBean<>(); - srb.setListener(new ServletLifeCycleListener()); - return srb; - } -} From e35603e403083d3a907ee5293a30c4f6929eda41 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 25 Nov 2019 08:12:42 -0700 Subject: [PATCH 0484/1778] Update dependency versions --- pom.xml | 33 +++++++++------------------------ 1 file changed, 9 insertions(+), 24 deletions(-) diff --git a/pom.xml b/pom.xml index 87b97d701..0676d7fda 100644 --- a/pom.xml +++ b/pom.xml @@ -69,25 +69,25 @@ org.junit.jupiter junit-jupiter - 5.5.2 + 5.6.0-M1 test org.openjdk.jmh jmh-core - 1.21 + 1.22 test org.openjdk.jmh jmh-generator-annprocess - 1.21 + 1.22 test org.assertj assertj-core - 3.13.2 + 3.14.0 test @@ -132,21 +132,6 @@ 2.2.300 provided - - - - - javax.servlet - javax.servlet-api - 3.0.1 - provided - - - org.springframework.boot - spring-boot - 1.2.3.RELEASE - provided - @@ -157,7 +142,7 @@ org.apache.maven.plugins maven-enforcer-plugin - 3.0.0-M2 + 3.0.0-M3 org.apache.maven.plugins @@ -172,7 +157,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.0.0-M3 + 3.0.0-M4 org.apache.felix @@ -192,12 +177,12 @@ org.apache.maven.plugins maven-jar-plugin - 3.1.2 + 3.2.0 org.apache.maven.plugins maven-source-plugin - 3.1.0 + 3.2.0 org.apache.maven.plugins @@ -231,7 +216,7 @@ org.codehaus.mojo animal-sniffer-enforcer-rule - 1.17 + 1.18 From dc2a95c355d421594fe184ff894fd22444e3650e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 25 Nov 2019 08:13:33 -0700 Subject: [PATCH 0485/1778] [maven-release-plugin] prepare release classgraph-4.8.55 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 0676d7fda..0c4d86a57 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.55-SNAPSHOT + 4.8.55 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.55 From ffe7e1c0bd05ed7d0f97a40f7d72975013643f5d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 25 Nov 2019 08:13:40 -0700 Subject: [PATCH 0486/1778] [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 0c4d86a57..4c2d0a2b3 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.55 + 4.8.56-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.55 + HEAD From df1adfe81e9e3ceb449b8d2b3ff80199e695372a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 25 Nov 2019 08:16:02 -0700 Subject: [PATCH 0487/1778] Fix Buffer->ByteBuffer covariant return type change --- .../io/github/classgraph/fastzipfilereader/FastZipEntry.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 e82b08747..8911ef9e9 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/FastZipEntry.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/FastZipEntry.java @@ -509,7 +509,7 @@ private void skipStored(final long n) throws IOException { final int numBytesToSkip = (int) Math.min(FileUtils.MAX_BUFFER_SIZE, Math.min(remainingToSkip, remainingInBuf)); try { - currChunkByteBuf.position(currChunkByteBuf.position() + numBytesToSkip); + ((Buffer) currChunkByteBuf).position(currChunkByteBuf.position() + numBytesToSkip); } catch (final BufferUnderflowException e) { throw new EOFException("Unexpected EOF in stored (non-deflated) zip entry data"); } From 6919cc5f1b2c2f9c9edfd6c6abbb1937a0fb43fd Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 25 Nov 2019 08:17:46 -0700 Subject: [PATCH 0488/1778] Fix version number --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4c2d0a2b3..0676d7fda 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.56-SNAPSHOT + 4.8.55-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From cc789417e64c75096a6117f4dc3971b1144b6fe5 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 25 Nov 2019 08:18:15 -0700 Subject: [PATCH 0489/1778] [maven-release-plugin] prepare release classgraph-4.8.55 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 0676d7fda..0c4d86a57 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.55-SNAPSHOT + 4.8.55 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.55 From 9a1fe695f50c5befa75e0f53a170c0f7ce5dd757 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 25 Nov 2019 08:18:22 -0700 Subject: [PATCH 0490/1778] [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 0c4d86a57..4c2d0a2b3 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.55 + 4.8.56-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.55 + HEAD From 036541acc8286920ed575c3d0e9d58205a070fa2 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 25 Nov 2019 08:42:52 -0700 Subject: [PATCH 0491/1778] Reset version number --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4c2d0a2b3..0676d7fda 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.56-SNAPSHOT + 4.8.55-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 1f3ddd8a4f3bb32a09d2919127f4c5b1f8f77ab7 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 25 Nov 2019 12:11:25 -0700 Subject: [PATCH 0492/1778] Unify manifest file generation --- pom.xml | 57 ++++++++++++++++++++++++--------------------------------- 1 file changed, 24 insertions(+), 33 deletions(-) diff --git a/pom.xml b/pom.xml index 0676d7fda..fd851c16a 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,7 @@ - + 4.0.0 io.github.classgraph @@ -159,11 +162,6 @@ maven-surefire-plugin 3.0.0-M4 - - org.apache.felix - maven-bundle-plugin - 4.2.1 - org.moditect moditect-maven-plugin @@ -243,7 +241,8 @@ - + org.codehaus.mojo.signature @@ -346,30 +345,6 @@ maven-surefire-plugin - - - org.apache.felix - maven-bundle-plugin - true - - - Utilities - ${project.groupId}.${project.artifactId} - ${project.description} - Luke Hutchison - - - - - bundle-manifest - process-classes - - manifest - - - - - @@ -378,7 +353,7 @@ moditect-maven-plugin - add-module-infos + add-module-info-execution package add-module-info @@ -393,6 +368,7 @@ + /** ${project.name}: ${project.description} ( ${project.url} ) */ @@ -437,12 +413,27 @@ org.apache.maven.plugins maven-jar-plugin + true - ${project.build.outputDirectory}/META-INF/MANIFEST.MF + true true true + + Utilities + http://opensource.org/licenses/MIT + 2 + ClassGraph + ${project.groupId}.${project.artifactId} + Luke Hutchison + ${project.description} + ${project.version} + io.github.classgraph;version="4.8.55" + + javax.xml.xpath,javax.xml.namespace,javax.xml.parsers,org.w3c.dom,jdk.internal.misc,sun.misc,sun.nio.ch + osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.7))" + From 34481187bf589d806df16a03c33fd803c97f0d45 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 25 Nov 2019 12:12:29 -0700 Subject: [PATCH 0493/1778] [maven-release-plugin] prepare release classgraph-4.8.55 --- pom.xml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index fd851c16a..439ec699b 100644 --- a/pom.xml +++ b/pom.xml @@ -1,12 +1,9 @@ - + 4.0.0 io.github.classgraph classgraph - 4.8.55-SNAPSHOT + 4.8.55 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 - HEAD + classgraph-4.8.55 @@ -241,8 +238,7 @@ - + org.codehaus.mojo.signature From 38b85ed62d11b67c0f5a0c04ed678b61543dba98 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 25 Nov 2019 12:13:18 -0700 Subject: [PATCH 0494/1778] Reset version number --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 439ec699b..09d5c9f7c 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.55 + 4.8.55-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From f3b7d72fa510a6afef9dd2bcda17d4f6e2a1608a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 25 Nov 2019 12:13:30 -0700 Subject: [PATCH 0495/1778] [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 09d5c9f7c..fd4fa1769 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.55-SNAPSHOT + 4.8.56-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.55 + HEAD From 965e189242b672195e96c73e1287d2adcc912371 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 26 Nov 2019 04:59:49 -0700 Subject: [PATCH 0496/1778] Update .gitignore --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 46cc9ac4a..b39e9e349 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,12 @@ +!.gitignore + pom.xml.releaseBackup release.properties *.class !CompiledWithJDK8.class +!module-info.class + /target/ bin/ tmp/ From 3af67fd8976f3b087a6f1cb25ad3b9d71670d3b5 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 26 Nov 2019 05:57:09 -0700 Subject: [PATCH 0497/1778] Remove Moditect dependency for creating module-info.class --- pom.xml | 88 +++++++----------- src/module-info/COMPILING | 3 + .../io/github/classgraph/Placeholder.java | 3 + .../io.github.classgraph/module-info.class | Bin 0 -> 289 bytes .../io.github.classgraph/module-info.java | 49 ++++++++++ 5 files changed, 89 insertions(+), 54 deletions(-) create mode 100644 src/module-info/COMPILING create mode 100644 src/module-info/io.github.classgraph/io/github/classgraph/Placeholder.java create mode 100644 src/module-info/io.github.classgraph/module-info.class create mode 100644 src/module-info/io.github.classgraph/module-info.java diff --git a/pom.xml b/pom.xml index fd4fa1769..4f2ce4c66 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,7 @@ - + 4.0.0 io.github.classgraph @@ -159,11 +162,6 @@ maven-surefire-plugin 3.0.0-M4 - - org.moditect - moditect-maven-plugin - 1.0.0.Beta2 - org.codehaus.mojo build-helper-maven-plugin @@ -232,13 +230,14 @@ check-signatures - test + compile enforce - + org.codehaus.mojo.signature @@ -252,11 +251,11 @@ - org.apache.maven.plugins maven-resources-plugin + copy-license-to-target process-resources @@ -295,6 +294,27 @@ + + + + copy-module-info + package + + copy-resources + + + ${basedir}/target/classes/META-INF/versions/9 + + + ${project.basedir}/src/module-info/io.github.classgraph + false + + module-info.class + + + + + @@ -341,49 +361,6 @@ maven-surefire-plugin - - - - - org.moditect - moditect-maven-plugin - - - add-module-info-execution - package - - add-module-info - - - - - 9 - true - - - - - - - - /** ${project.name}: ${project.description} ( ${project.url} ) - */ - module io.github.classgraph { - exports io.github.classgraph; - exports - nonapi.io.github.classgraph.classloaderhandler.lifecycle; - requires java.xml; - requires jdk.unsupported; - requires java.management; - requires java.logging; - } - - - - - - - org.codehaus.mojo @@ -417,6 +394,7 @@ true + Utilities http://opensource.org/licenses/MIT 2 @@ -425,10 +403,12 @@ Luke Hutchison ${project.description} ${project.version} - io.github.classgraph;version="4.8.55" + 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,jdk.internal.misc,sun.misc,sun.nio.ch - osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.7))" + + true diff --git a/src/module-info/COMPILING b/src/module-info/COMPILING new file mode 100644 index 000000000..e5dc4e111 --- /dev/null +++ b/src/module-info/COMPILING @@ -0,0 +1,3 @@ +# 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 diff --git a/src/module-info/io.github.classgraph/io/github/classgraph/Placeholder.java b/src/module-info/io.github.classgraph/io/github/classgraph/Placeholder.java new file mode 100644 index 000000000..a9a30d332 --- /dev/null +++ b/src/module-info/io.github.classgraph/io/github/classgraph/Placeholder.java @@ -0,0 +1,3 @@ +// See https://stackoverflow.com/a/50204997/3950982 +package io.github.classgraph; +class Placeholder {} diff --git a/src/module-info/io.github.classgraph/module-info.class b/src/module-info/io.github.classgraph/module-info.class new file mode 100644 index 0000000000000000000000000000000000000000..10722691b0b24652c1e55f7285645545e5fdcd3c GIT binary patch literal 289 zcmYjMNpiw45bQDglCa3%KwQEXxaE`=2rPs^mRz<)apYY&@Bkh~VGoC74&Bo|Gu1VZ z=kFcB2I&9^VUmX|I=!;~5D4R+5X-&ZTBix~e@1_NVo`*$)|tSpHQS3^d1Z92eJ!vcOxbk7 x80$?6!IA5CClassGraph: the uber-fast, ultra-lightweight classpath + * and module scanner for JVM languages. + * + * @author Luke Hutchison + */ +// Compile this in JDK 9 compatibility mode +module io.github.classgraph { + exports io.github.classgraph; + // VersionFinder requires java.xml + requires java.xml; + // FileUtils requires jdk.unsupported (for usage of Unsafe) + requires jdk.unsupported; + // ModulePathInfo requires java.management + 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 +} From 47972496b09af7b5b7251897d1f109a78dcfa5c0 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 26 Nov 2019 08:23:49 -0700 Subject: [PATCH 0498/1778] Make adding module-info.class to jar more robust --- pom.xml | 58 +++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 20 deletions(-) diff --git a/pom.xml b/pom.xml index 4f2ce4c66..b97fdff57 100644 --- a/pom.xml +++ b/pom.xml @@ -126,10 +126,11 @@ - + + + + org.eclipse.jdt org.eclipse.jdt.annotation 2.2.300 @@ -172,6 +173,11 @@ maven-jar-plugin 3.2.0 + + org.apache.maven.plugins + maven-antrun-plugin + 1.8 + org.apache.maven.plugins maven-source-plugin @@ -195,7 +201,7 @@ org.apache.maven.plugins maven-release-plugin - + 2.5.3 @@ -263,7 +269,7 @@ copy-resources - ${basedir}/target/classes + ${project.build.outputDirectory} ${basedir} @@ -282,7 +288,7 @@ copy-resources - ${basedir}/target/apidocs + ${project.build.directory}/apidocs ${basedir} @@ -294,25 +300,37 @@ - - + + + + + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + - copy-module-info + add-module-info-to-jar package - copy-resources + run - ${basedir}/target/classes/META-INF/versions/9 - - - ${project.basedir}/src/module-info/io.github.classgraph - false - - module-info.class - - - + + + + + From 743814df1bde4c4b623b098f04743d24ed8b3275 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 26 Nov 2019 08:28:12 -0700 Subject: [PATCH 0499/1778] Override inherited super-pom plugin versions with explicit versions --- pom.xml | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index b97fdff57..f1bae2cc1 100644 --- a/pom.xml +++ b/pom.xml @@ -176,7 +176,7 @@ org.apache.maven.plugins maven-antrun-plugin - 1.8 + 1.8 org.apache.maven.plugins @@ -201,7 +201,29 @@ org.apache.maven.plugins maven-release-plugin - 2.5.3 + 2.5.3 + + + + + org.apache.maven.plugins + maven-clean-plugin + 3.1.0 + + + org.apache.maven.plugins + maven-deploy-plugin + 3.0.0-M1 + + + org.apache.maven.plugins + maven-install-plugin + 3.0.0-M1 + + + org.apache.maven.plugins + maven-site-plugin + 3.8.2 From cd2ff6f3c5a2277d8ece8d990e41ac8e01681a13 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 26 Nov 2019 09:23:13 -0700 Subject: [PATCH 0500/1778] Enable limited usage of MethodHandle, to increase speed of setAccessible --- .../io/github/classgraph/json/JSONUtils.java | 110 ++++++++++-------- 1 file changed, 61 insertions(+), 49 deletions(-) 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 86601c069..b309c81af 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/JSONUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/json/JSONUtils.java @@ -28,6 +28,10 @@ */ 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; @@ -308,20 +312,17 @@ static Class getRawType(final Type type) { } } - // private static MethodHandle canAccess = null; - //private static Method canAccessMethod = null; - // private static MethodHandle isAccessible = null; + // private static MethodHandle canAccessMethodHandle = null; + // private static Method canAccessMethod = null; + private static MethodHandle isAccessibleMethodHandle = null; private static Method isAccessibleMethod = null; - // private static MethodHandle trySetAccessible = null; + private static MethodHandle trySetAccessibleMethodHandle = null; private static Method trySetAccessibleMethod = null; static { - // TODO: MethodHandles are disabled for now, due to Animal Sniffer bug: - // https://github.com/mojohaus/animal-sniffer/issues/67 - - // final Lookup lookup = MethodHandles.lookup(); + final Lookup lookup = MethodHandles.lookup(); // try { // // JDK 9+: use AccessibleObject::canAccess(instance) - // canAccess = lookup.findVirtual(AccessibleObject.class, "canAccess", + // canAccessMethodHandle = lookup.findVirtual(AccessibleObject.class, "canAccess", // MethodType.methodType(boolean.class, Object.class)); // } catch (NoSuchMethodException | IllegalAccessException e) { // // Ignore @@ -331,26 +332,27 @@ static Class getRawType(final Type type) { // } catch (NoSuchMethodException | SecurityException e1) { // // Ignore // } - // try { - // // JDK 7/8: use AccessibleObject::isAccessible() - // isAccessible = lookup.findVirtual(AccessibleObject.class, "isAccessible", - // MethodType.methodType(boolean.class)); - // } catch (NoSuchMethodException | IllegalAccessException e) { - // // 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) - // trySetAccessible = lookup.findVirtual(AccessibleObject.class, "trySetAccessible", - // MethodType.methodType(boolean.class)); - // } catch (NoSuchMethodException | IllegalAccessException e) { - // // 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) { @@ -368,28 +370,30 @@ static Class getRawType(final Type type) { 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 (canAccess != null) { - // // // JDK 9+: use canAccess(instance) - // // try { - // // accessible.set((Boolean) canAccess.invokeExact(fieldOrConstructor, instance)); - // // } catch (Throwable e) { - // // // Ignore - // // } + // // 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 (isAccessible != null) { - // // JDK 7/8: use isAccessible (deprecated in JDK 9+) - // try { - // // accessible.set((Boolean) isAccessible.invoke(fieldOrConstructor)); - // } catch (final Throwable e) { - // // Ignore - // } - // } else - if (isAccessibleMethod != null) { + 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 + accessible.set((Boolean) (Object) isAccessibleMethodHandle + .invoke(new Object[] { fieldOrConstructor })); + } 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)); @@ -401,10 +405,14 @@ static boolean isAccessibleOrMakeAccessible(final AccessibleObject fieldOrConstr // Only set accessible if field or constructor is not yet accessible if (!accessible.get()) { - // if (trySetAccessible != null) { - // accessible.set((Boolean) trySetAccessible.invoke(fieldOrConstructor)); - // } else - if (trySetAccessibleMethod != null) { + if (trySetAccessibleMethodHandle != null) { + try { + accessible.set((Boolean) (Object) trySetAccessibleMethodHandle + .invoke(new Object[] { fieldOrConstructor })); + } catch (Throwable e) { + // Ignore + } + } else if (trySetAccessibleMethod != null) { try { accessible.set((Boolean) trySetAccessibleMethod.invoke(fieldOrConstructor)); } catch (final Throwable e) { @@ -423,10 +431,14 @@ static boolean isAccessibleOrMakeAccessible(final AccessibleObject fieldOrConstr AccessController.doPrivileged(new PrivilegedAction() { @Override public Void run() { - // if (trySetAccessible != null) { - // accessible.set((Boolean) trySetAccessible.invoke(fieldOrConstructor)); - // } else - if (trySetAccessibleMethod != null) { + if (trySetAccessibleMethodHandle != null) { + try { + accessible.set((Boolean) (Object) trySetAccessibleMethodHandle + .invoke(new Object[] { fieldOrConstructor })); + } catch (Throwable e) { + // Ignore + } + } else if (trySetAccessibleMethod != null) { try { accessible.set((Boolean) trySetAccessibleMethod.invoke(fieldOrConstructor)); } catch (final Throwable e) { From 7b6039fbeb26334b9f71431e2da3b56e2c7e37f1 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 26 Nov 2019 10:03:43 -0700 Subject: [PATCH 0501/1778] Simplify code --- src/main/java/io/github/classgraph/MappableInfoList.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/github/classgraph/MappableInfoList.java b/src/main/java/io/github/classgraph/MappableInfoList.java index 206fb6b37..346c992db 100644 --- a/src/main/java/io/github/classgraph/MappableInfoList.java +++ b/src/main/java/io/github/classgraph/MappableInfoList.java @@ -111,10 +111,8 @@ public boolean containsName(final String name) { @SuppressWarnings("null") public T get(final String name) { for (final T i : this) { - if (i != null) { - if (i.getName().equals(name)) { - return i; - } + if (i != null && i.getName().equals(name)) { + return i; } } return null; From 9e65e7f9437c153ba77e78d8feeb564605548916 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 26 Nov 2019 10:04:56 -0700 Subject: [PATCH 0502/1778] Reorder members --- .../io/github/classgraph/json/JSONUtils.java | 96 +++++++++---------- 1 file changed, 48 insertions(+), 48 deletions(-) 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 b309c81af..6ea44ce68 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/JSONUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/json/JSONUtils.java @@ -59,6 +59,14 @@ 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) { @@ -77,6 +85,46 @@ 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. */ @@ -312,54 +360,6 @@ static Class getRawType(final Type type) { } } - // 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 { - 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 - } - } - /** * Return true if the field is accessible, or can be made accessible (and make it accessible if so). * From 134fce5e66f8ecb194c073bbc5e5ff64dec97a61 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 26 Nov 2019 10:43:01 -0700 Subject: [PATCH 0503/1778] Fix version number again --- pom.xml | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/pom.xml b/pom.xml index f1bae2cc1..93d3530df 100644 --- a/pom.xml +++ b/pom.xml @@ -1,12 +1,9 @@ - + 4.0.0 io.github.classgraph classgraph - 4.8.56-SNAPSHOT + 4.8.55-SNAPSHOT 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 - HEAD + classgraph-4.8.56 @@ -264,8 +261,7 @@ - + org.codehaus.mojo.signature @@ -344,13 +340,8 @@ - - + + From 087b669af81603fa1308d87b25c8d18d369dd354 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 26 Nov 2019 10:43:32 -0700 Subject: [PATCH 0504/1778] [maven-release-plugin] prepare release classgraph-4.8.55 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 93d3530df..70b0e8437 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.55-SNAPSHOT + 4.8.55 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.56 + classgraph-4.8.55 From 1d8e7368f4a6c177cb6d0ccd063777bc49a67d86 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 26 Nov 2019 10:43:39 -0700 Subject: [PATCH 0505/1778] [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 70b0e8437..1bfac962a 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.55 + 4.8.56-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.55 + classgraph-4.8.56 From bd4291e881ab6a409ba18dade721f9765c6310b4 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Nov 2019 10:11:31 -0700 Subject: [PATCH 0506/1778] Support custom URL schemes (#384) --- .../java/io/github/classgraph/ClassGraph.java | 21 +- .../classgraph/ClasspathElementZip.java | 14 +- .../java/io/github/classgraph/Scanner.java | 64 +++- .../AntClassLoaderHandler.java | 2 +- .../WeblogicClassLoaderHandler.java | 4 +- ...ebsphereTraditionalClassLoaderHandler.java | 2 +- .../classgraph/classpath/ClasspathOrder.java | 309 +++++++++++------- .../fastzipfilereader/NestedJarHandler.java | 44 ++- .../io/github/classgraph/json/JSONUtils.java | 13 +- .../github/classgraph/scanspec/ScanSpec.java | 30 +- .../io/github/classgraph/utils/JarUtils.java | 2 + .../issues/issue384/Issue384Test.java | 83 +++++ 12 files changed, 409 insertions(+), 179 deletions(-) create mode 100644 src/test/java/io/github/classgraph/issues/issue384/Issue384Test.java diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index a56f38f41..23b994950 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -407,7 +407,12 @@ public ClassGraph removeTemporaryFilesAfterScan() { * @return this (for method chaining). */ public ClassGraph overrideClasspath(final String overrideClasspath) { - scanSpec.overrideClasspath(overrideClasspath); + if (overrideClasspath.isEmpty()) { + throw new IllegalArgumentException("Can't override classpath with an empty path"); + } + for (final String classpathElement : JarUtils.smartPathSplit(overrideClasspath)) { + scanSpec.addClasspathOverride(classpathElement); + } return this; } @@ -424,11 +429,12 @@ public ClassGraph overrideClasspath(final String overrideClasspath) { * @return this (for method chaining). */ public ClassGraph overrideClasspath(final Iterable overrideClasspathElements) { - final String overrideClasspath = JarUtils.pathElementsToPathStr(overrideClasspathElements); - if (overrideClasspath.isEmpty()) { + if (!overrideClasspathElements.iterator().hasNext()) { throw new IllegalArgumentException("Can't override classpath with an empty path"); } - overrideClasspath(overrideClasspath); + for (final Object classpathElement : overrideClasspathElements) { + scanSpec.addClasspathOverride(classpathElement); + } return this; } @@ -445,11 +451,12 @@ public ClassGraph overrideClasspath(final Iterable overrideClasspathElements) * @return this (for method chaining). */ public ClassGraph overrideClasspath(final Object... overrideClasspathElements) { - final String overrideClasspath = JarUtils.pathElementsToPathStr(overrideClasspathElements); - if (overrideClasspath.isEmpty()) { + if (overrideClasspathElements.length == 0) { throw new IllegalArgumentException("Can't override classpath with an empty path"); } - overrideClasspath(overrideClasspath); + for (final Object classpathElement : overrideClasspathElements) { + scanSpec.addClasspathOverride(classpathElement); + } return this; } diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index 91dc144a5..7bb917bce 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -33,6 +33,7 @@ import java.io.InputStream; import java.net.URI; import java.net.URISyntaxException; +import java.net.URL; import java.nio.ByteBuffer; import java.nio.file.attribute.PosixFilePermission; import java.util.AbstractMap.SimpleEntry; @@ -61,7 +62,7 @@ /** A zip/jarfile classpath element. */ class ClasspathElementZip extends ClasspathElement { - /** The raw path for this zipfile. */ + /** The {@link String} representation of the raw path, {@link URL} or {@link URI} for this zipfile. */ private final String rawPath; /** The logical zipfile for this classpath element. */ LogicalZipFile logicalZipFile; @@ -84,8 +85,9 @@ class ClasspathElementZip extends ClasspathElement { /** * A jarfile classpath element. * - * @param rawPath - * the raw path to the jarfile, possibly including "!"-delimited nested paths. + * @param rawPathObj + * the raw path to the jarfile as a {@link String}, possibly including "!"-delimited nested paths, or + * a {@link URL} or {@link URI} for the jarfile * @param classLoader * the classloader * @param nestedJarHandler @@ -93,10 +95,12 @@ class ClasspathElementZip extends ClasspathElement { * @param scanSpec * the scan spec */ - ClasspathElementZip(final String rawPath, final ClassLoader classLoader, + ClasspathElementZip(final Object rawPathObj, final ClassLoader classLoader, final NestedJarHandler nestedJarHandler, final ScanSpec scanSpec) { super(classLoader, scanSpec); - this.rawPath = rawPath; + // Convert the raw path object (String, URL or URI) to a string. + // Any required URL/URI parsing are done in NestedJarHandler. + this.rawPath = rawPathObj.toString(); this.zipFilePath = rawPath; // May change when open() is called this.nestedJarHandler = nestedJarHandler; } diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index df3da68c5..bcb7032f6 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -31,6 +31,10 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Collection; @@ -370,7 +374,7 @@ 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 Entry rawClasspathEntry; + private final Entry rawClasspathEntry; /** The parent classpath element. */ private final ClasspathElement parentClasspathElement; @@ -388,7 +392,7 @@ static class ClasspathEntryWorkUnit { * @param orderWithinParentClasspathElement * the order within parent classpath element */ - public ClasspathEntryWorkUnit(final Entry rawClasspathEntry, + public ClasspathEntryWorkUnit(final Entry rawClasspathEntry, final ClasspathElement parentClasspathElement, final int orderWithinParentClasspathElement) { this.rawClasspathEntry = rawClasspathEntry; this.parentClasspathElement = parentClasspathElement; @@ -400,22 +404,60 @@ public ClasspathEntryWorkUnit(final Entry rawClasspathEntry * The classpath element singleton map. For each classpath element path, canonicalize path, and create a * ClasspathElement singleton. */ - private final SingletonMap, ClasspathElement, IOException> // + private final SingletonMap, ClasspathElement, IOException> // classpathEntryToClasspathElementSingletonMap = // - new SingletonMap, ClasspathElement, IOException>() { + new SingletonMap, ClasspathElement, IOException>() { @Override - public ClasspathElement newInstance(final Entry classpathEntry, + public ClasspathElement newInstance(final Entry classpathEntry, final LogNode log) throws IOException, InterruptedException { - final String classpathEntryPath = classpathEntry.getKey(); + Object classpathEntryObj = classpathEntry.getKey(); final ClassLoader classLoader = classpathEntry.getValue(); - if (classpathEntryPath.regionMatches(true, 0, "http://", 0, 7) - || classpathEntryPath.regionMatches(true, 0, "https://", 0, 8)) { - // For remote URLs, must be a jar - return new ClasspathElementZip(classpathEntryPath, classLoader, nestedJarHandler, scanSpec); + + String classpathEntryPath; + if (classpathEntryObj instanceof URL || classpathEntryObj instanceof URI) { + String scheme = classpathEntryObj instanceof URL ? ((URL) classpathEntryObj).getProtocol() + : ((URI) classpathEntryObj).getScheme(); + if ("jar".equals(scheme)) { + // Strip off "jar:" scheme prefix + try { + classpathEntryObj = classpathEntryObj instanceof URL + ? new URL(((URL) classpathEntryObj).toString().substring(4)) + : new URI(((URI) classpathEntryObj).toString().substring(4)); + scheme = classpathEntryObj instanceof URL ? ((URL) classpathEntryObj).getProtocol() + : ((URI) classpathEntryObj).getScheme(); + } catch (MalformedURLException | URISyntaxException e) { + throw new IOException("Could not strip 'jar:' prefix from " + classpathEntryObj, e); + } + } + if ("file".equals(scheme)) { + // Extract file path, and use below as a path string to determine if this + // classpath element is a file (jar) or directory + classpathEntryPath = classpathEntryObj instanceof URL + ? ((URL) classpathEntryObj).getPath() + : ((URI) classpathEntryObj).getPath(); + } else if ("http".equals(scheme) || "https".equals(scheme)) { + // Jar URL or URI (remote URLs/URIs must be jars) + return new ClasspathElementZip(classpathEntryObj, classLoader, nestedJarHandler, + scanSpec); + } else { + // For custom URL schemes, assume it must be for a jar, not a directory + return new ClasspathElementZip(classpathEntryObj, classLoader, nestedJarHandler, + scanSpec); + } + } else { + // classpathEntryObj is a string + classpathEntryPath = classpathEntryObj.toString(); } + // Normalize path -- strip off any leading "jar:" / "file:", and normalize separators final String pathNormalized = FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, classpathEntryPath); + + // "http:", "https:" or any other URL/URI scheme must indicate a jar + if (JarUtils.URL_SCHEME_PATTERN.matcher(pathNormalized).matches()) { + return new ClasspathElementZip(classpathEntryPath, classLoader, nestedJarHandler, scanSpec); + } + // Strip everything after first "!", to get path of base jarfile or dir final int plingIdx = pathNormalized.indexOf('!'); final String pathToCanonicalize = plingIdx < 0 ? pathNormalized @@ -932,7 +974,7 @@ 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 Entry rawClasspathEntry : classpathFinder.getClasspathOrder().getOrder()) { + for (final Entry rawClasspathEntry : classpathFinder.getClasspathOrder().getOrder()) { rawClasspathEntryWorkUnits .add(new ClasspathEntryWorkUnit(rawClasspathEntry, /* parentClasspathElement = */ null, /* orderWithinParentClasspathElement = */ rawClasspathEntryWorkUnits.size())); 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 7a3ec71da..070c859e2 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/AntClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/AntClassLoaderHandler.java @@ -83,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.addClasspathEntries( + classpathOrder.addClasspathPathStr( (String) ReflectionUtils.invokeMethod(classLoader, "getClasspath", false), classLoader, scanSpec, log); } 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 9e975d8c2..50cffe710 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WeblogicClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WeblogicClassLoaderHandler.java @@ -89,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.addClasspathEntries( // + classpathOrder.addClasspathPathStr( // (String) ReflectionUtils.invokeMethod(classLoader, "getFinderClassPath", false), classLoader, scanSpec, log); - classpathOrder.addClasspathEntries( // + classpathOrder.addClasspathPathStr( // (String) ReflectionUtils.invokeMethod(classLoader, "getClassPath", false), 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 b352ee115..79d18093a 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereTraditionalClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereTraditionalClassLoaderHandler.java @@ -90,6 +90,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) { final String classpath = (String) ReflectionUtils.invokeMethod(classLoader, "getClassPath", false); - classpathOrder.addClasspathEntries(classpath, classLoader, scanSpec, log); + classpathOrder.addClasspathPathStr(classpath, classLoader, scanSpec, log); } } 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 935acc8d9..5cbe4aaa0 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java @@ -30,6 +30,8 @@ import java.io.File; import java.lang.reflect.Array; +import java.net.URI; +import java.net.URL; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.HashSet; @@ -52,8 +54,8 @@ public class ClasspathOrder { /** Unique classpath entries. */ private final Set classpathEntryUniqueResolvedPaths = new HashSet<>(); - /** The classpath order. */ - private final List> order = new ArrayList<>(); + /** The classpath order. Keys are instances of {@link String}, {@link URL} or {@link URI}. */ + private final List> order = new ArrayList<>(); /** * Constructor. @@ -68,9 +70,9 @@ public class ClasspathOrder { /** * Get the order of classpath elements, as an ordered set. * - * @return the classpath order, as (path/URL, ClassLoader) tuples. + * @return the classpath order, as (path/URL/URI, ClassLoader) tuples. */ - public List> getOrder() { + public List> getOrder() { return order; } @@ -84,7 +86,7 @@ public Set getClasspathEntryUniqueResolvedPaths() { } /** - * Test to see if a RelativePath has been filtered out by the user. + * Test to see if a classpath element has been filtered out by the user. * * @param classpathElementPath * the classpath element path @@ -122,27 +124,38 @@ boolean addSystemClasspathEntry(final String pathEntry, final ClassLoader classL /** * Add a classpath entry. * - * @param pathEntry - * the classpath entry -- the path string should already have been run through - * FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, path) + * @param pathElement + * the {@link String} path, {@link URL} or {@link URI} of the classpath element. If a string, the + * path string should already have been run through FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, + * path) + * @param pathElementStr + * the path element in string format * @param classLoader * the classloader * @param scanSpec * the scan spec * @return true, if added and unique */ - private boolean addClasspathEntry(final String pathEntry, final ClassLoader classLoader, - final ScanSpec scanSpec) { - if (scanSpec.overrideClasspath == null // - && (SystemJarFinder.getJreLibOrExtJars().contains(pathEntry) - || pathEntry.equals(SystemJarFinder.getJreRtJarPath()))) { - // JRE lib and ext jars are handled separately, so reject them as duplicates if they are - // returned by a system classloader - return false; - } - if (classpathEntryUniqueResolvedPaths.add(pathEntry)) { - order.add(new SimpleEntry<>(pathEntry, classLoader)); - return true; + private boolean addClasspathEntry(final Object pathElement, final String pathElementStr, + final ClassLoader classLoader, final ScanSpec scanSpec) { + if (pathElement instanceof URL || pathElement instanceof URI) { + // Assume that any custom URLs or URIs passed in are not in lib or ext dir, and are not system jars + if (classpathEntryUniqueResolvedPaths.add(pathElementStr)) { + order.add(new SimpleEntry<>(pathElement, classLoader)); + return true; + } + } else { + if (scanSpec.overrideClasspath == null // + && (SystemJarFinder.getJreLibOrExtJars().contains(pathElementStr) + || pathElementStr.equals(SystemJarFinder.getJreRtJarPath()))) { + // JRE lib and ext jars are handled separately, so reject them as duplicates if they are + // returned by a system classloader + return false; + } + if (classpathEntryUniqueResolvedPaths.add(pathElementStr)) { + order.add(new SimpleEntry<>(pathElementStr, classLoader)); + return true; + } } return false; } @@ -152,7 +165,8 @@ private boolean addClasspathEntry(final String pathEntry, final ClassLoader clas * elements that it knows about. ClassLoaders will be called in order. * * @param pathElement - * the URL or path of the classpath element. + * the {@link String} path, {@link URL} or {@link URI} of the classpath element, or some object whose + * {@link Object#toString()} method can be called to obtain the classpath element. * @param classLoader * the ClassLoader that this classpath element was obtained from. * @param scanSpec @@ -162,113 +176,166 @@ private boolean addClasspathEntry(final String pathEntry, final ClassLoader clas * @return true (and add the classpath element) if pathElement is not null, empty, nonexistent, or filtered out * by user-specified criteria, otherwise return false. */ - public boolean addClasspathEntry(final String pathElement, final ClassLoader classLoader, + public boolean addClasspathEntry(final Object pathElement, final ClassLoader classLoader, final ScanSpec scanSpec, final LogNode log) { - if (pathElement == null || pathElement.isEmpty()) { + if (pathElement == null) { return false; } - // Check for wildcard path element (allowable for local classpaths as of JDK 6) - if (pathElement.endsWith("*")) { - if (pathElement.length() == 1 || // - (pathElement.length() > 2 && pathElement.charAt(pathElement.length() - 1) == '*' - && (pathElement.charAt(pathElement.length() - 2) == File.separatorChar - || (File.separatorChar != '/' - && pathElement.charAt(pathElement.length() - 2) == '/')))) { - // Apply classpath element filters, if any - final String baseDirPath = pathElement.length() == 1 ? "" - : pathElement.substring(0, pathElement.length() - 2); - final String baseDirPathResolved = FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, baseDirPath); - if (!filter(baseDirPath) - || (!baseDirPathResolved.equals(baseDirPath) && !filter(baseDirPathResolved))) { - if (log != null) { - log.log("Classpath element did not match filter criterion, skipping: " + pathElement); - } - return false; + final String pathElementStr = pathElement.toString(); + if (pathElement instanceof URL || pathElement instanceof URI) { + if (!filter(pathElementStr)) { + if (log != null) { + log.log("Classpath element did not match filter criterion, skipping: " + pathElementStr); } + return false; + } + if (addClasspathEntry(pathElement, pathElementStr, classLoader, scanSpec)) { + if (log != null) { + log.log("Found classpath element: " + pathElementStr); + } + return true; + } else { + if (log != null) { + log.log("Ignoring duplicate classpath element: " + pathElementStr); + } + return false; + } + } else { + if (pathElementStr.isEmpty()) { + return false; + } + // 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.CURR_DIR_PATH, + baseDirPath); + if (!filter(baseDirPath) + || (!baseDirPathResolved.equals(baseDirPath) && !filter(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: " + pathElement); + // 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; } - return false; - } - if (!FileUtils.canRead(baseDir)) { + 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.CURR_DIR_PATH, 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("Cannot read directory for wildcard classpath element: " + pathElement); + 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; } - if (!baseDir.isDirectory()) { + } else { + // Non-wildcarded (standard) classpath element + final String pathElementResolved = FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, + pathElementStr); + if (!filter(pathElementStr) + || (!pathElementResolved.equals(pathElementStr) && !filter(pathElementResolved))) { if (log != null) { - log.log("Wildcard is appended to something other than a directory: " + pathElement); + log.log("Classpath element did not match filter criterion, skipping: " + pathElementStr + + (pathElementStr.equals(pathElementResolved) ? "" : " -> " + pathElementResolved)); } 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: " + pathElement); - 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.CURR_DIR_PATH, - fileInDirPath); - if (addClasspathEntry(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)); - } - } - } + 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; } - } 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: " + pathElement); - } - return false; } + } + } + + /** + * Add classpath entries, separated by the system path separator character. + * + * @param overrideClasspath + * the delimited {@link String}, {@link URL} or {@link URI} of classpath elements. + * @param classLoader + * the ClassLoader that this classpath was obtained from. + * @param scanSpec + * the scan spec + * @param log + * the LogNode instance to use if logging in verbose mode. + * @return true (and add the classpath element) if pathElement is not null or empty, otherwise return false. + */ + public boolean addClasspathEntries(final List overrideClasspath, final ClassLoader classLoader, + final ScanSpec scanSpec, final LogNode log) { + if (overrideClasspath == null || overrideClasspath.isEmpty()) { + return false; } else { - // Non-wildcarded (standard) classpath element - final String pathElementResolved = FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, pathElement); - if (!filter(pathElement) - || (!pathElementResolved.equals(pathElement) && !filter(pathElementResolved))) { - if (log != null) { - log.log("Classpath element did not match filter criterion, skipping: " + pathElement - + (pathElement.equals(pathElementResolved) ? "" : " -> " + pathElementResolved)); - } - return false; - } - if (addClasspathEntry(pathElementResolved, classLoader, scanSpec)) { - if (log != null) { - log.log("Found classpath element: " + pathElement - + (pathElement.equals(pathElementResolved) ? "" : " -> " + pathElementResolved)); - } - return true; - } else { - if (log != null) { - log.log("Ignoring duplicate classpath element: " + pathElement - + (pathElement.equals(pathElementResolved) ? "" : " -> " + pathElementResolved)); - } - return false; + for (final Object pathElement : overrideClasspath) { + addClasspathEntry(pathElement, classLoader, scanSpec, log); } + return true; } } @@ -285,7 +352,7 @@ public boolean addClasspathEntry(final String pathElement, final ClassLoader cla * the LogNode instance to use if logging in verbose mode. * @return true (and add the classpath element) if pathElement is not null or empty, otherwise return false. */ - public boolean addClasspathEntries(final String pathStr, final ClassLoader classLoader, final ScanSpec scanSpec, + public boolean addClasspathPathStr(final String pathStr, final ClassLoader classLoader, final ScanSpec scanSpec, final LogNode log) { if (pathStr == null || pathStr.isEmpty()) { return false; @@ -303,10 +370,11 @@ public boolean addClasspathEntries(final String pathStr, final ClassLoader class } /** - * Add classpath entries from an object obtained from reflection. The object may be a String (containing a - * single path, or several paths separated with File.pathSeparator), a List or other Iterable, or an array - * object. In the case of Iterables and arrays, the elements may be any type whose {@code toString()} method - * returns a path or URL string (including the {@code URL} and {@code Path} types). + * Add classpath entries from an object obtained from reflection. The object may be a {@link URL}, a + * {@link URI}, a {@link String} (containing a single path, or several paths separated with File.pathSeparator), + * a List or other Iterable, or an array object. In the case of Iterables and arrays, the elements may be any + * type whose {@code toString()} method returns a path or URL string (including the {@code URL} and {@code Path} + * types). * * @param pathObject * the object containing a classpath string or strings. @@ -322,26 +390,23 @@ public boolean addClasspathEntryObject(final Object pathObject, final ClassLoade final ScanSpec scanSpec, final LogNode log) { boolean valid = false; if (pathObject != null) { - if (pathObject instanceof String) { - valid |= addClasspathEntries((String) pathObject, classLoader, scanSpec, log); + if (pathObject instanceof URL || pathObject instanceof URI) { + valid |= addClasspathEntry(pathObject, classLoader, scanSpec, log); } else if (pathObject instanceof Iterable) { - for (final Object p : (Iterable) pathObject) { - if (p != null) { - valid |= addClasspathEntries(p.toString(), classLoader, scanSpec, log); - } + for (final Object elt : (Iterable) pathObject) { + valid |= addClasspathEntryObject(elt, classLoader, scanSpec, log); } } else { final Class valClass = pathObject.getClass(); if (valClass.isArray()) { for (int j = 0, n = Array.getLength(pathObject); j < n; j++) { final Object elt = Array.get(pathObject, j); - if (elt != null) { - valid |= addClasspathEntryObject(elt, classLoader, scanSpec, log); - } + valid |= addClasspathEntryObject(elt, classLoader, scanSpec, log); } } else { - // Try simply calling toString() as a final fallback, in case this returns something sensible - valid |= addClasspathEntries(pathObject.toString(), classLoader, scanSpec, log); + // Try simply calling toString() as a final fallback, to handle String objects, or to + // try to handle anything else + valid |= addClasspathPathStr(pathObject.toString(), classLoader, scanSpec, log); } } } 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 3ebd6e369..5c8ecfb3c 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java @@ -33,6 +33,8 @@ import java.io.InputStream; import java.io.RandomAccessFile; import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; import java.net.URL; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; @@ -57,6 +59,7 @@ import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.FastPathResolver; import nonapi.io.github.classgraph.utils.FileUtils; +import nonapi.io.github.classgraph.utils.JarUtils; import nonapi.io.github.classgraph.utils.LogNode; /** Open and read jarfiles, which may be nested within other jarfiles. */ @@ -228,12 +231,12 @@ public Entry newInstance(final String nestedJarPathRaw, // 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://", download the jar to a temp file - // or to a ByteBuffer in RAM - final boolean isRemote = nestedJarPath.startsWith("http://") - || nestedJarPath.startsWith("https://"); + // 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:" + // have already been stripped from any URL/URI.) + final boolean isURL = JarUtils.URL_SCHEME_PATTERN.matcher(nestedJarPath).matches(); PhysicalZipFile physicalZipFile; - if (isRemote) { + if (isURL) { // Jarfile is at an http:// or https:// URL if (scanSpec.enableRemoteJarScanning) { // Download jar to a temp file, or if not possible, to a ByteBuffer in RAM @@ -538,13 +541,20 @@ private File makeTempFile(final String filePath, final boolean onlyUseLeafname) private PhysicalZipFile downloadJarFromURL(final String jarURL, final LogNode log) throws IOException, InterruptedException { final LogNode subLog = log == null ? null : log.log(jarURL, "Downloading jar from URL " + jarURL); - final URL url; + InputStream inputStream = null; try { - url = new URL(jarURL); - } catch (final MalformedURLException e) { - throw new IOException("Malformed URL: " + jarURL); - } - try (final InputStream inputStream = url.openStream()) { + URL url = null; + try { + url = new URL(jarURL); + } catch (final MalformedURLException e1) { + try { + url = new URI(jarURL).toURL(); + } catch (final URISyntaxException e2) { + throw new IOException("Could not parse URL: " + jarURL); + } + } + inputStream = url.openStream(); + PhysicalZipFile physicalZipFile = null; try { // Download jar from inputStream to a temporary file @@ -576,12 +586,18 @@ private PhysicalZipFile downloadJarFromURL(final String jarURL, final LogNode lo throw ClassGraphException.newClassGraphException("physicalZipFile should not be null"); } return physicalZipFile; + + } catch (final MalformedURLException e) { + throw new IOException("Malformed URL: " + jarURL); } finally { + if (inputStream != null) { + inputStream.close(); + } if (subLog != null) { subLog.addElapsedTime(); - subLog.log("***** Note that it is time-consuming to scan jars at http(s) addresses, " - + "they must be downloaded for every scan, and the same jars must also be " - + "separately downloaded by the ClassLoader *****"); + 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 *****"); } } } 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 6ea44ce68..dea0923ca 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/JSONUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/json/JSONUtils.java @@ -388,8 +388,7 @@ static boolean isAccessibleOrMakeAccessible(final AccessibleObject fieldOrConstr try { // Have to use double casting and wrap in new Object[] due to Animal Sniffer bug: // https://github.com/mojohaus/animal-sniffer/issues/67 - accessible.set((Boolean) (Object) isAccessibleMethodHandle - .invoke(new Object[] { fieldOrConstructor })); + accessible.set((Boolean) isAccessibleMethodHandle.invoke(new Object[] { fieldOrConstructor })); } catch (final Throwable e) { // Ignore } @@ -407,9 +406,9 @@ static boolean isAccessibleOrMakeAccessible(final AccessibleObject fieldOrConstr if (!accessible.get()) { if (trySetAccessibleMethodHandle != null) { try { - accessible.set((Boolean) (Object) trySetAccessibleMethodHandle - .invoke(new Object[] { fieldOrConstructor })); - } catch (Throwable e) { + accessible.set( + (Boolean) trySetAccessibleMethodHandle.invoke(new Object[] { fieldOrConstructor })); + } catch (final Throwable e) { // Ignore } } else if (trySetAccessibleMethod != null) { @@ -433,9 +432,9 @@ static boolean isAccessibleOrMakeAccessible(final AccessibleObject fieldOrConstr public Void run() { if (trySetAccessibleMethodHandle != null) { try { - accessible.set((Boolean) (Object) trySetAccessibleMethodHandle + accessible.set((Boolean) trySetAccessibleMethodHandle .invoke(new Object[] { fieldOrConstructor })); - } catch (Throwable e) { + } catch (final Throwable e) { // Ignore } } else if (trySetAccessibleMethod != null) { 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 28f5ff141..1c8b8ebd4 100644 --- a/src/main/java/nonapi/io/github/classgraph/scanspec/ScanSpec.java +++ b/src/main/java/nonapi/io/github/classgraph/scanspec/ScanSpec.java @@ -29,6 +29,8 @@ package nonapi.io.github.classgraph.scanspec; import java.lang.reflect.Field; +import java.net.URI; +import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -198,8 +200,11 @@ public class ScanSpec { */ public transient List overrideModuleLayers; - /** If non-null, specifies a classpath to override the default one. */ - public String overrideClasspath; + /** + * If non-null, specifies a list of classpath elements (String, {@link URL} or {@link URI} to use to override + * the default classpath. + */ + public List overrideClasspath; /** If non-null, a list of filter operations to apply to classpath elements. */ public transient List classpathElementFilters; @@ -248,15 +253,22 @@ public void sortPrefixes() { // ------------------------------------------------------------------------------------------------------------- /** - * Override the automatically-detected classpath with a custom search path. You can specify multiple elements, - * separated by File.pathSeparatorChar. If this method is called, nothing but the provided classpath will be - * scanned, i.e. causes ClassLoaders to be ignored, as well as the java.class.path system property. + * Override the automatically-detected classpath with a custom path. You can specify multiple elements in + * separate calls, and if this method is called even once, the default classpath will be overridden, such that + * nothing but the provided classpath will be scanned, i.e. causes ClassLoaders to be ignored, as well as the + * java.class.path system property. * - * @param overrideClasspath - * The classpath to scan. + * @param overrideClasspathElement + * The classpath element to add as an override to the default classpath. */ - public void overrideClasspath(final String overrideClasspath) { - this.overrideClasspath = overrideClasspath; + public void addClasspathOverride(final Object overrideClasspathElement) { + if (this.overrideClasspath == null) { + this.overrideClasspath = new ArrayList<>(); + } + this.overrideClasspath + .add(overrideClasspathElement instanceof String || overrideClasspathElement instanceof URL + || overrideClasspathElement instanceof URI ? overrideClasspathElement + : overrideClasspathElement.toString()); } /** 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 7339183b0..7ac507561 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/JarUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/JarUtils.java @@ -43,6 +43,8 @@ * Jarfile utilities. */ public final class JarUtils { + /** Check if a path has a URL scheme at the beginning. */ + public static final Pattern URL_SCHEME_PATTERN = Pattern.compile("[a-zA-Z][a-zA-Z0-9+-.]*[:].*"); /** The Constant DASH_VERSION. */ private static final Pattern DASH_VERSION = Pattern.compile("-(\\d+(\\.|$))"); diff --git a/src/test/java/io/github/classgraph/issues/issue384/Issue384Test.java b/src/test/java/io/github/classgraph/issues/issue384/Issue384Test.java new file mode 100644 index 000000000..aa7ab3335 --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue384/Issue384Test.java @@ -0,0 +1,83 @@ +/* + * 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.issues.issue384; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLStreamHandler; +import java.util.AbstractMap.SimpleEntry; +import java.util.HashMap; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ScanResult; + +/** + * Test. + */ +public class Issue384Test { + + private static final String SCHEME = "customscheme"; + + private static Map remappedURLs = new HashMap<>(); + + static { + URL.setURLStreamHandlerFactory(protocol -> SCHEME.equals(protocol) ? new URLStreamHandler() { + @Override + protected URLConnection openConnection(final URL url) throws IOException { + // Record that the URL was remapped, so we know this custom URLStreamHandler was called + final String newURL = "file:" + url.getPath(); + remappedURLs.put(url.toString(), newURL); + // Replace scheme with "file://" + return new URL(newURL).openConnection(); + } + } : null); + } + + /** + * Test. + */ + @Test + public void issue384Test() throws MalformedURLException { + final String filePath = Issue384Test.class.getClassLoader().getResource("nested-jars-level1.zip").getPath(); + final String customSchemeURL = SCHEME + ":" + filePath; + final URL url = new URL(customSchemeURL); + try (ScanResult scanResult = new ClassGraph().enableRemoteJarScanning().overrideClasspath(url).scan()) { + assertThat(scanResult.getAllResources().getPaths()).containsExactly("level2.jar"); + assertThat(remappedURLs.entrySet().iterator().next()) + .isEqualTo(new SimpleEntry<>(customSchemeURL, "file:" + filePath)); + } + } +} From 53312223ddf6bbb1ea8606e70f946a3cec4d0f69 Mon Sep 17 00:00:00 2001 From: "Sean C. Sullivan" Date: Wed, 27 Nov 2019 12:51:28 -0500 Subject: [PATCH 0507/1778] add GitHub Actions workflow ci.yml --- .github/workflows/ci.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..95f044f5f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,26 @@ +name: Java CI + +on: + pull_request: + branches: + - master + push: + branches: + - master + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + java: [ '1.8.0', '11.0.x' ] + steps: + - uses: actions/checkout@v1 + - name: Set up JDK + uses: actions/setup-java@v1.2.0 + with: + java-version: ${{ matrix.java }} + - name: print Java version + run: java -version + - name: Build with Maven + run: mvn clean package --file pom.xml From ca883960bd09b2dc68f83bbe05c472b83382170c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Nov 2019 12:41:21 -0700 Subject: [PATCH 0508/1778] Download URLs to RAM by default, with spillover to disk (#384) --- .../classgraph/ClasspathElementDir.java | 75 ++--- .../java/io/github/classgraph/Scanner.java | 3 +- .../fastzipfilereader/NestedJarHandler.java | 302 ++++++++++++++++-- .../fastzipfilereader/PhysicalZipFile.java | 61 ++-- .../io/github/classgraph/utils/FileUtils.java | 16 +- 5 files changed, 332 insertions(+), 125 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementDir.java b/src/main/java/io/github/classgraph/ClasspathElementDir.java index a1d72817f..f33672a78 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementDir.java @@ -29,14 +29,10 @@ package io.github.classgraph; import java.io.File; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; -import java.io.RandomAccessFile; import java.net.URI; import java.nio.ByteBuffer; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.attribute.PosixFileAttributes; import java.nio.file.attribute.PosixFilePermission; @@ -49,6 +45,8 @@ 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.fastzipfilereader.NestedJarHandler.MappedByteBufferResources; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.scanspec.ScanSpec.ScanSpecPathMatch; import nonapi.io.github.classgraph.utils.FileUtils; @@ -66,6 +64,9 @@ class ClasspathElementDir extends ClasspathElement { /** 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. * @@ -73,13 +74,17 @@ class ClasspathElementDir extends ClasspathElement { * the classpath element directory * @param classLoader * the classloader + * @param nestedJarHandler + * the nested jar handler * @param scanSpec * the scan spec */ - ClasspathElementDir(final File classpathEltDir, final ClassLoader classLoader, final ScanSpec scanSpec) { + ClasspathElementDir(final File classpathEltDir, final ClassLoader classLoader, + final NestedJarHandler nestedJarHandler, final ScanSpec scanSpec) { super(classLoader, scanSpec); this.classpathEltDir = classpathEltDir; this.ignorePrefixLen = classpathEltDir.getPath().length() + 1; + this.nestedJarHandler = nestedJarHandler; } /* (non-Javadoc) @@ -153,15 +158,15 @@ void open(final WorkQueue workQueue, final int classpath * the relative path * @param classpathResourceFile * the classpath resource file + * @param nestedJarHandler + * the nested jar handler * @return the resource */ - private Resource newResource(final String relativePath, final File classpathResourceFile) { + private Resource newResource(final String relativePath, final File classpathResourceFile, + final NestedJarHandler nestedJarHandler) { return new Resource(this, classpathResourceFile.length()) { - /** The random access file. */ - private RandomAccessFile randomAccessFile; - - /** The file channel. */ - private FileChannel fileChannel; + /** The mapped file. */ + private MappedByteBufferResources mappedFileResources; @Override public String getPath() { @@ -200,23 +205,8 @@ public synchronized ByteBuffer read() throws IOException { } markAsOpen(); try { - randomAccessFile = new RandomAccessFile(classpathResourceFile, "r"); - fileChannel = randomAccessFile.getChannel(); - MappedByteBuffer buffer = null; - try { - buffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size()); - } catch (final FileNotFoundException e) { - throw e; - } catch (IOException | OutOfMemoryError e) { - // If map failed, try calling System.gc() to free some allocated MappedByteBuffers - // (there is a limit to the number of mapped files -- 64k on Linux) - // See: http://www.mapdb.org/blog/mmap_files_alloc_and_jvm_crash/ - System.gc(); - System.runFinalization(); - // Then try calling map again - buffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size()); - } - byteBuffer = buffer; + mappedFileResources = new MappedByteBufferResources(classpathResourceFile, nestedJarHandler); + byteBuffer = mappedFileResources.getByteBuffer(); length = byteBuffer.remaining(); return byteBuffer; } catch (final IOException | SecurityException | OutOfMemoryError e) { @@ -273,25 +263,9 @@ public synchronized byte[] load() throws IOException { @Override public synchronized void close() { super.close(); // Close inputStream - if (byteBuffer != null) { - FileUtils.closeDirectByteBuffer(byteBuffer, /* log = */ null); - byteBuffer = null; - } - if (fileChannel != null) { - try { - fileChannel.close(); - } catch (final IOException e) { - // Ignore - } - fileChannel = null; - } - if (randomAccessFile != null) { - try { - randomAccessFile.close(); - } catch (final IOException e) { - // Ignore - } - randomAccessFile = null; + if (mappedFileResources != null) { + mappedFileResources.close(/* log = */ null); + mappedFileResources = null; } markAsClosed(); } @@ -309,7 +283,8 @@ public synchronized void close() { @Override Resource getResource(final String relativePath) { final File resourceFile = new File(classpathEltDir, relativePath); - return FileUtils.canReadAndIsFile(resourceFile) ? newResource(relativePath, resourceFile) : null; + return FileUtils.canReadAndIsFile(resourceFile) ? newResource(relativePath, resourceFile, nestedJarHandler) + : null; } /** @@ -419,7 +394,7 @@ private void scanDirRecursively(final File dir, final LogNode log) { || (parentMatchStatus == ScanSpecPathMatch.AT_WHITELISTED_CLASS_PACKAGE && scanSpec.classfileIsSpecificallyWhitelisted(fileInDirRelativePath))) { // Resource is whitelisted - final Resource resource = newResource(fileInDirRelativePath, fileInDir); + final Resource resource = newResource(fileInDirRelativePath, fileInDir, nestedJarHandler); addWhitelistedResource(resource, parentMatchStatus, /* isClassfileOnly = */ false, subLog); // Save last modified time @@ -435,7 +410,7 @@ private void scanDirRecursively(final File dir, final LogNode log) { // Always check for module descriptor in package root, even if package root isn't in whitelist for (final File fileInDir : filesInDir) { if (fileInDir.getName().equals("module-info.class") && fileInDir.isFile()) { - final Resource resource = newResource("module-info.class", fileInDir); + final Resource resource = newResource("module-info.class", fileInDir, nestedJarHandler); addWhitelistedResource(resource, parentMatchStatus, /* isClassfileOnly = */ true, subLog); fileToLastModified.put(fileInDir, fileInDir.lastModified()); } diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index bcb7032f6..bd34bff47 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -505,7 +505,8 @@ public ClasspathElement newInstance(final Entry classpathEn return isJar ? new ClasspathElementZip(canonicalPathNormalized, classLoader, nestedJarHandler, scanSpec) - : new ClasspathElementDir(fileCanonicalized, classLoader, scanSpec); + : new ClasspathElementDir(fileCanonicalized, classLoader, nestedJarHandler, + scanSpec); } } }; 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 5c8ecfb3c..f1198336d 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java @@ -28,21 +28,31 @@ */ package nonapi.io.github.classgraph.fastzipfilereader; +import java.io.BufferedOutputStream; import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.io.RandomAccessFile; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.nio.ByteBuffer; +import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.StandardCopyOption; +import java.nio.file.StandardOpenOption; import java.util.AbstractMap.SimpleEntry; +import java.util.ArrayList; +import java.util.Collections; import java.util.Map.Entry; import java.util.Queue; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicBoolean; @@ -240,7 +250,9 @@ public Entry newInstance(final String nestedJarPathRaw, // Jarfile is at an http:// or https:// URL if (scanSpec.enableRemoteJarScanning) { // Download jar to a temp file, or if not possible, to a ByteBuffer in RAM - physicalZipFile = downloadJarFromURL(nestedJarPath, log); + final LogNode subLog = log == null ? null + : log.log("Downloading jar from URL " + nestedJarPath); + physicalZipFile = downloadJarFromURL(nestedJarPath, subLog); } else { throw new IOException( "Remote jar scanning has not been enabled, cannot scan classpath element: " @@ -419,6 +431,14 @@ public RecyclableInflater newInstance() throws RuntimeException { } }; + /** {@link MappedByteBuffer} instances that are currently mapped. */ + private final Set mappedByteBuffers = Collections + .newSetFromMap(new ConcurrentHashMap()); + + /** {@link MappedByteBufferResources} instances that were allocated for downloading jars from URLs. */ + private final Set mappedByteBufferResources = Collections + .newSetFromMap(new ConcurrentHashMap()); + /** Any temporary files created while scanning. */ private ConcurrentLinkedDeque tempFiles = new ConcurrentLinkedDeque<>(); @@ -426,10 +446,16 @@ public RecyclableInflater newInstance() throws RuntimeException { public static final String TEMP_FILENAME_LEAF_SEPARATOR = "---"; /** - * The threshold uncompressed size at which nested deflated jars are inflated to a temporary file on disk, + * The maximum size of a jar that is downloaded from a {@link URL}'s {@link InputStream} to RAM, before the + * content is spilled over to a temporary file on disk. + */ + private static final int MAX_JAR_RAM_SIZE = 64 * 1024 * 1024; + + /** + * The maximum uncompressed size above which nested deflated jars are inflated to a temporary file on disk, * rather than to RAM. */ - private static final int INFLATE_TO_DISK_THRESHOLD = 32 * 1024 * 1024; + private static final int INFLATE_TO_DISK_THRESHOLD = MAX_JAR_RAM_SIZE; /** True if {@link #close(LogNode)} has been called. */ private final AtomicBoolean closed = new AtomicBoolean(false); @@ -479,6 +505,220 @@ private PhysicalZipFile canonicalFileToPhysicalZipFile(final File canonicalFile, // ------------------------------------------------------------------------------------------------------------- + /** Resources for a mapped file. */ + public static class MappedByteBufferResources { + private File file; + private RandomAccessFile raf; + private FileChannel fileChannel; + private ByteBuffer byteBuffer; + private boolean byteBufferIsMapped; + private NestedJarHandler nestedJarHandler; + + /** + * Map a {@link ByteBuffer} from a {@link File}. + * + * @param file + * the file + * @param nestedJarHandler + * the nested jar handler + * @throws IOException + * On I/O exception. + */ + public MappedByteBufferResources(final File file, final NestedJarHandler nestedJarHandler) + throws IOException { + this.file = file; + this.nestedJarHandler = nestedJarHandler; + if (byteBuffer != null) { + throw new IllegalArgumentException("Already open"); + } + try { + raf = new RandomAccessFile(file, "r"); + fileChannel = raf.getChannel(); + byteBuffer = nestedJarHandler.mapFileChannelToByteBuffer(fileChannel, 0L, raf.length()); + byteBufferIsMapped = true; + + } catch (final IOException | SecurityException e) { + if (fileChannel != null) { + fileChannel.close(); + fileChannel = null; + } + if (raf != null) { + raf.close(); + raf = null; + } + throw e; + } + } + + /** + * Wrap a {@link ByteBuffer} that does not need to be unmapped (for cases where a {@link URL} was fetched + * but it fit in RAM, so didn't need to spill over to disk). + * + * @param byteBuffer + * the byte buffer. + */ + public MappedByteBufferResources(final ByteBuffer byteBuffer) { + this.byteBuffer = byteBuffer; + // Don't set fileChannel or raf, they are unneeded. + // byteBufferIsMapped will remain false. + } + + /** + * Get the mapped file (or null if a {@link ByteBuffer} was wrapped instead). + * + * @return the mapped file + */ + public File getFile() { + return file; + } + + /** + * Get the byte buffer. + * + * @return the byte buffer + */ + public ByteBuffer getByteBuffer() { + return byteBuffer; + } + + /** + * Free resources. + * + * @param log + * the log + */ + public void close(final LogNode log) { + if (byteBufferIsMapped && byteBuffer != null) { + nestedJarHandler.unmapByteBuffer(byteBuffer, log); + byteBuffer = null; + } + if (fileChannel != null) { + try { + fileChannel.close(); + fileChannel = null; + } catch (final IOException e) { + // Ignore + } + } + if (raf != null) { + try { + raf.close(); + raf = null; + } catch (final IOException e) { + // Ignore + } + } + } + } + + // ------------------------------------------------------------------------------------------------------------- + + /** + * Map a {@link FileChannel} to a {@link MappedByteBuffer}. + * + * @param fileChannel + * the file channel + * @param position + * the start position to map. + * @param size + * the number of bytes to map. + * @return the {@link MappedByteBuffer}. + * @throws IOException + * If an I/O exception occurs. + */ + public MappedByteBuffer mapFileChannelToByteBuffer(final FileChannel fileChannel, final long position, + final long size) throws IOException { + MappedByteBuffer byteBuffer; + try { + // Try mapping the FileChannel + byteBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, position, size); + } catch (final FileNotFoundException e) { + throw e; + } catch (IOException | OutOfMemoryError e) { + // If map failed, try calling System.gc() to free some allocated MappedByteBuffers + // (there is a limit to the number of mapped files -- 64k on Linux) + // See: http://www.mapdb.org/blog/mmap_files_alloc_and_jvm_crash/ + System.gc(); + System.runFinalization(); + // Then try calling map again + byteBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, position, size); + } + + // If succeeded, add the ByteBuffer to the set of opened ByteBuffers so it can be cleanly closed + mappedByteBuffers.add(byteBuffer); + return byteBuffer; + } + + /** + * Unmap a previously-mapped {@link ByteBuffer}. + * + * @param byteBuffer + * the {@link ByteBuffer}. + * @param log + * the log. + */ + public void unmapByteBuffer(final ByteBuffer byteBuffer, final LogNode log) { + if (mappedByteBuffers.remove(byteBuffer)) { + FileUtils.closeDirectByteBuffer(byteBuffer, log); + } + } + + // ------------------------------------------------------------------------------------------------------------- + + /** + * 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}. + * @param maxRAMBufferSize + * the max RAM to use before spilling over to a temp file on disk. + * @param sourceURL + * the source URL that inputStream was opened from. + * @param log + * the log + * @return The {@link MappedByteBufferResources} contaning the {@link ByteBuffer} obtained by fetching the + * {@link InputStream}. + * @throws IOException + * If the contents could not be read. + */ + private MappedByteBufferResources readAllBytesAsByteBufferWithSpilloverToDisk(final InputStream inputStream, + final int maxRAMBufferSize, final String sourceURL, final LogNode log) throws IOException { + if (maxRAMBufferSize > FileUtils.MAX_BUFFER_SIZE) { + throw new IOException("InputStream is too large to read"); + } + final byte[] buf = new byte[maxRAMBufferSize]; + final int bufLength = buf.length; + + int totBytesRead = 0; + int bytesRead = 0; + while ((bytesRead = inputStream.read(buf, totBytesRead, bufLength - totBytesRead)) > 0) { + // Fill buffer until nothing more can be read + totBytesRead += bytesRead; + } + if (bytesRead < 0) { + // Successfully reached end of stream -- wrap array with ByteBuffer and return it + return new MappedByteBufferResources(ByteBuffer.wrap(buf, 0, totBytesRead)); + } else { + // bytesRead == 0 => ran out of buffer space, spill over to disk + final File tempFile = makeTempFile(sourceURL, /* onlyUseLeafname = */ true); + if (log != null) { + log.log("Could not fit downloaded URL into max RAM buffer size of " + maxRAMBufferSize + + " bytes, downloading to temporary file: " + sourceURL + " -> " + tempFile); + } + Files.write(tempFile.toPath(), buf, StandardOpenOption.WRITE); + try (OutputStream os = new BufferedOutputStream(new FileOutputStream(tempFile, /* append = */ true))) { + bytesRead = 0; + while ((bytesRead = inputStream.read(buf, 0, buf.length)) > 0) { + os.write(buf, 0, bytesRead); + } + } + return new MappedByteBufferResources(tempFile, this); + } + } + + // ------------------------------------------------------------------------------------------------------------- + /** * Get the leafname of a path. * @@ -513,7 +753,7 @@ private String sanitizeFilename(final String filename) { * @throws IOException * If the temporary file could not be created. */ - private File makeTempFile(final String filePath, final boolean onlyUseLeafname) throws IOException { + public File makeTempFile(final String filePath, final boolean onlyUseLeafname) throws IOException { final File tempFile = File.createTempFile("ClassGraph--", TEMP_FILENAME_LEAF_SEPARATOR + sanitizeFilename(onlyUseLeafname ? leafname(filePath) : filePath)); tempFile.deleteOnExit(); @@ -540,7 +780,6 @@ private File makeTempFile(final String filePath, final boolean onlyUseLeafname) */ private PhysicalZipFile downloadJarFromURL(final String jarURL, final LogNode log) throws IOException, InterruptedException { - final LogNode subLog = log == null ? null : log.log(jarURL, "Downloading jar from URL " + jarURL); InputStream inputStream = null; try { URL url = null; @@ -553,37 +792,32 @@ private PhysicalZipFile downloadJarFromURL(final String jarURL, final LogNode lo throw new IOException("Could not parse URL: " + jarURL); } } + + // Fetch the jar contents from the URL's InputStream. + // If it doesn't fit in RAM, spill over to disk. inputStream = url.openStream(); + final MappedByteBufferResources bufResources = readAllBytesAsByteBufferWithSpilloverToDisk(inputStream, + MAX_JAR_RAM_SIZE, jarURL, log); + mappedByteBufferResources.add(bufResources); PhysicalZipFile physicalZipFile = null; - try { - // Download jar from inputStream to a temporary file - final File tempFile = makeTempFile(jarURL, /* onlyUseLeafname = */ true); - Files.copy(inputStream, tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING); - if (subLog != null) { - subLog.log("Downloaded jar to temporary file " + tempFile); - } + final File tempFile = bufResources.getFile(); + if (tempFile != null) { + // InputStream would not fit within the RAM limit, so spilled over to disk // Wrap temp file in a PhysicalZipFile physicalZipFile = canonicalFileToPhysicalZipFile(tempFile, log); + if (physicalZipFile == null) { + // Should not happen + throw ClassGraphException.newClassGraphException("physicalZipFile should not be null"); + } // Add temp file to queue of physical zipfiles that have to be unmapped on close additionalAllocatedPhysicalZipFiles.add(physicalZipFile); - } catch (final SecurityException | UnsupportedOperationException | IOException e) { - // Temp file could not be written, so this is a read-only filesystem, or temp dir does not exist, - // or temp dir has run out of space. Download the jar to a ByteBuffer in RAM instead. - if (log != null) { - log.log("Could not download jar to temp file (" + e + "), downloading to ByteBuffer instead"); - } - // Read inputStream into a ByteBuffer - final ByteBuffer byteBuffer = FileUtils.readAllBytesAsByteBuffer(inputStream, -1L); + } else { // Wrap ByteBuffer in a PhysicalZipFile. (No need to add to additionalAllocatedPhysicalZipFiles, // since byteBuffer is not a DirectByteBuffer, it just wraps a standard Java byte array, so the // DirectByteBuffer cleaner does not need to be called on close.) - physicalZipFile = new PhysicalZipFile(byteBuffer, /* outermostFile = */ null, jarURL, - NestedJarHandler.this); - } - if (physicalZipFile == null) { - // Should not happen - throw ClassGraphException.newClassGraphException("physicalZipFile should not be null"); + physicalZipFile = new PhysicalZipFile(bufResources.getByteBuffer(), /* outermostFile = */ null, + jarURL, NestedJarHandler.this); } return physicalZipFile; @@ -593,9 +827,9 @@ private PhysicalZipFile downloadJarFromURL(final String jarURL, final LogNode lo if (inputStream != null) { inputStream.close(); } - if (subLog != null) { - subLog.addElapsedTime(); - subLog.log("***** Note that it is time-consuming to scan jars at non-\"file:\" URLs, " + if (log != null) { + log.addElapsedTime(); + log.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 *****"); } @@ -679,6 +913,16 @@ public void close(final LogNode log) { tempFiles.clear(); tempFiles = null; } + if (mappedByteBufferResources != null) { + for (final MappedByteBufferResources bufRes : new ArrayList<>(mappedByteBufferResources)) { + bufRes.close(log); + } + } + if (mappedByteBuffers != null) { + for (final ByteBuffer byteBuffer : new ArrayList<>(mappedByteBuffers)) { + unmapByteBuffer(byteBuffer, log); + } + } } } } 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 49c4bc780..b39985724 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/PhysicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/PhysicalZipFile.java @@ -30,7 +30,6 @@ import java.io.Closeable; import java.io.File; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; @@ -57,7 +56,7 @@ class PhysicalZipFile implements Closeable { private RandomAccessFile raf; /** The {@link FileChannel}. */ - private FileChannel fc; + private FileChannel fileChannel; /** The file length. */ final long fileLen; @@ -105,16 +104,16 @@ class PhysicalZipFile implements Closeable { if (fileLen == 0L) { throw new IOException("Zipfile is empty: " + file); } - fc = raf.getChannel(); + fileChannel = raf.getChannel(); } catch (final IOException | SecurityException e) { + if (fileChannel != null) { + fileChannel.close(); + fileChannel = null; + } if (raf != null) { raf.close(); raf = null; } - if (fc != null) { - fc.close(); - fc = null; - } throw e; } @@ -128,21 +127,7 @@ public ByteBuffer newInstance(final Integer chunkIdxI, final LogNode log) throws // Map the indexed 2GB chunk of the file to a MappedByteBuffer final long pos = chunkIdxI.longValue() * FileUtils.MAX_BUFFER_SIZE; final long chunkSize = Math.min(FileUtils.MAX_BUFFER_SIZE, fileLen - pos); - MappedByteBuffer buffer = null; - try { - buffer = fc.map(FileChannel.MapMode.READ_ONLY, pos, chunkSize); - } catch (final FileNotFoundException e) { - throw e; - } catch (IOException | OutOfMemoryError e) { - // If map failed, try calling System.gc() to free some allocated MappedByteBuffers - // (there is a limit to the number of mapped files -- 64k on Linux) - // See: http://www.mapdb.org/blog/mmap_files_alloc_and_jvm_crash/ - System.gc(); - System.runFinalization(); - // Then try calling map again - buffer = fc.map(FileChannel.MapMode.READ_ONLY, pos, chunkSize); - } - return buffer; + return nestedJarHandler.mapFileChannelToByteBuffer(fileChannel, pos, chunkSize); } }; } @@ -260,10 +245,23 @@ public boolean equals(final Object obj) { @Override public void close() { if (!closed.getAndSet(true)) { - if (fc != null) { + if (chunkIdxToByteBuffer != null) { + chunkIdxToByteBuffer.clear(); + chunkIdxToByteBuffer = null; + } + if (mappedByteBuffersCached != null) { + for (int i = 0; i < mappedByteBuffersCached.length; i++) { + if (mappedByteBuffersCached[i] != null) { + nestedJarHandler.unmapByteBuffer(mappedByteBuffersCached[i], /* log = */ null); + mappedByteBuffersCached[i] = null; + } + } + mappedByteBuffersCached = null; + } + if (fileChannel != null) { try { - fc.close(); - fc = null; + fileChannel.close(); + fileChannel = null; } catch (final IOException e) { // Ignore } @@ -276,19 +274,6 @@ public void close() { // Ignore } } - if (chunkIdxToByteBuffer != null) { - chunkIdxToByteBuffer.clear(); - chunkIdxToByteBuffer = null; - } - if (mappedByteBuffersCached != null) { - for (int i = 0; i < mappedByteBuffersCached.length; i++) { - if (mappedByteBuffersCached[i] != null) { - FileUtils.closeDirectByteBuffer(mappedByteBuffersCached[i], /* log = */ null); - mappedByteBuffersCached[i] = null; - } - } - mappedByteBuffersCached = null; - } nestedJarHandler = 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 1f1673e58..7873e74ac 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -181,12 +181,11 @@ private static SimpleEntry readAllBytes(final InputStream input // lengths do not become a memory allocation attack vector : Math.min((int) fileSizeHint, MAX_INITIAL_BUFFER_SIZE); byte[] buf = new byte[bufferSize]; - int bufLength = buf.length; int totBytesRead = 0; for (int bytesRead;;) { - // Fill buffer -- may fill more or fewer bytes than buffer size while ((bytesRead = inputStream.read(buf, totBytesRead, bufLength - totBytesRead)) > 0) { + // Fill buffer until nothing more can be read totBytesRead += bytesRead; } if (bytesRead < 0) { @@ -214,18 +213,18 @@ private static SimpleEntry readAllBytes(final InputStream input * * @param inputStream * The {@link InputStream}. - * @param fileSize + * @param fileSizeHint * The file size, if known, otherwise -1L. * @return The contents of the {@link InputStream} as a byte array. * @throws IOException * If the contents could not be read. */ - public static byte[] readAllBytesAsArray(final InputStream inputStream, final long fileSize) + public static byte[] readAllBytesAsArray(final InputStream inputStream, final long fileSizeHint) throws IOException { - final SimpleEntry ent = readAllBytes(inputStream, fileSize); + final SimpleEntry ent = readAllBytes(inputStream, fileSizeHint); final byte[] buf = ent.getKey(); final int bufBytesUsed = ent.getValue(); - return (buf.length == bufBytesUsed) ? buf : Arrays.copyOf(buf, bufBytesUsed); + return bufBytesUsed == buf.length ? buf : Arrays.copyOf(buf, bufBytesUsed); } /** @@ -241,7 +240,10 @@ public static byte[] readAllBytesAsArray(final InputStream inputStream, final lo */ public static ByteBuffer readAllBytesAsByteBuffer(final InputStream inputStream, final long fileSizeHint) throws IOException { - return ByteBuffer.wrap(readAllBytesAsArray(inputStream, fileSizeHint)); + final SimpleEntry ent = readAllBytes(inputStream, fileSizeHint); + final byte[] buf = ent.getKey(); + final int bufBytesUsed = ent.getValue(); + return ByteBuffer.wrap(buf, 0, bufBytesUsed); } /** From 35031264cfe5fcb791172bee45504ccabe16f1cc Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Nov 2019 12:43:08 -0700 Subject: [PATCH 0509/1778] Update Javadoc --- 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 23b994950..21e4713d5 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -1043,7 +1043,7 @@ public ClassGraph blacklistClasspathElementsContainingResourcePath(final String. } /** - * Enable classpath elements to be fetched from remote http/https URLs to local temporary files and scanned. + * Enable classpath elements to be fetched from remote ("http:"/"https:") URLs (or URLs with custom schemes). * This option is disabled by default, as this may present a security vulnerability, since classes from * downloaded jars can be subsequently loaded using {@link ClassInfo#loadClass}. * From 28d8c3ffb5707ddff7725682dd9610c88fcd0a24 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Nov 2019 13:01:38 -0700 Subject: [PATCH 0510/1778] Fix commandline compilation issues --- .../classgraph/ClasspathElementDir.java | 15 +++---- .../classgraph/ClasspathElementZip.java | 11 ++--- .../java/io/github/classgraph/Scanner.java | 44 ++++++++++--------- .../classgraph/classpath/ClasspathOrder.java | 40 +++++++++++++---- .../io/github/classgraph/json/JSONUtils.java | 18 +++++--- 5 files changed, 80 insertions(+), 48 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementDir.java b/src/main/java/io/github/classgraph/ClasspathElementDir.java index f33672a78..f4a02b317 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementDir.java @@ -36,13 +36,13 @@ import java.nio.file.Files; import java.nio.file.attribute.PosixFileAttributes; import java.nio.file.attribute.PosixFilePermission; -import java.util.AbstractMap.SimpleEntry; import java.util.Arrays; import java.util.HashSet; import java.util.Set; 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.WorkQueue; import nonapi.io.github.classgraph.fastzipfilereader.LogicalZipFile; import nonapi.io.github.classgraph.fastzipfilereader.NestedJarHandler; @@ -119,7 +119,7 @@ void open(final WorkQueue workQueue, final int classpath } workQueue.addWorkUnit(new ClasspathEntryWorkUnit( /* rawClasspathEntry = */ // - new SimpleEntry<>(file.getPath(), classLoader), + new ClasspathElementAndClassLoader(file.getPath(), classLoader), /* parentClasspathElement = */ this, /* orderWithinParentClasspathElement = */ childClasspathEntryIdx++)); } @@ -133,12 +133,11 @@ void open(final WorkQueue workQueue, final int classpath if (log != null) { log(classpathElementIdx, "Found package root: " + packageRootDir, log); } - workQueue - .addWorkUnit(new ClasspathEntryWorkUnit( - /* rawClasspathEntry = */ new SimpleEntry<>(packageRootDir.getPath(), - classLoader), - /* parentClasspathElement = */ this, - /* orderWithinParentClasspathElement = */ childClasspathEntryIdx++)); + workQueue.addWorkUnit(new ClasspathEntryWorkUnit( + /* rawClasspathEntry = */ new ClasspathElementAndClassLoader(packageRootDir.getPath(), + classLoader), + /* parentClasspathElement = */ this, + /* orderWithinParentClasspathElement = */ childClasspathEntryIdx++)); } } } catch (final SecurityException e) { diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index 7bb917bce..f531726e8 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -36,7 +36,6 @@ import java.net.URL; import java.nio.ByteBuffer; import java.nio.file.attribute.PosixFilePermission; -import java.util.AbstractMap.SimpleEntry; import java.util.HashSet; import java.util.Map.Entry; import java.util.Set; @@ -44,6 +43,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.NullSingletonException; import nonapi.io.github.classgraph.concurrency.WorkQueue; import nonapi.io.github.classgraph.fastzipfilereader.FastZipEntry; @@ -195,7 +195,8 @@ void open(final WorkQueue workQueue, final int classpath subLog.log("Found nested lib jar: " + entryPath); } workQueue.addWorkUnit(new ClasspathEntryWorkUnit( - /* rawClasspathEntry = */ new SimpleEntry<>(entryPath, classLoader), + /* rawClasspathEntry = */ new ClasspathElementAndClassLoader(entryPath, + classLoader), /* parentClasspathElement = */ this, /* orderWithinParentClasspathElement = */ childClasspathEntryIdx++)); @@ -236,8 +237,8 @@ void open(final WorkQueue workQueue, final int classpath // Schedule child classpath element for scanning workQueue.addWorkUnit( // new ClasspathEntryWorkUnit( - /* rawClasspathEntry = */ new SimpleEntry<>(childClassPathEltPathWithPrefix, - classLoader), + /* rawClasspathEntry = */ new ClasspathElementAndClassLoader( + childClassPathEltPathWithPrefix, classLoader), /* parentClasspathElement = */ this, /* orderWithinParentClasspathElement = */ childClasspathEntryIdx++)); @@ -268,7 +269,7 @@ void open(final WorkQueue workQueue, final int classpath // Schedule child classpath element for scanning workQueue.addWorkUnit(new ClasspathEntryWorkUnit( /* rawClasspathEntry = */ // - new SimpleEntry<>(childClassPathEltPath, classLoader), + new ClasspathElementAndClassLoader(childClassPathEltPath, classLoader), /* 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 bd34bff47..2fd62ea1f 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -59,6 +59,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.ModuleFinder; import nonapi.io.github.classgraph.concurrency.AutoCloseableExecutorService; import nonapi.io.github.classgraph.concurrency.InterruptionChecker; @@ -374,7 +375,7 @@ 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 Entry rawClasspathEntry; + private final ClasspathElementAndClassLoader rawClasspathEntry; /** The parent classpath element. */ private final ClasspathElement parentClasspathElement; @@ -392,7 +393,7 @@ static class ClasspathEntryWorkUnit { * @param orderWithinParentClasspathElement * the order within parent classpath element */ - public ClasspathEntryWorkUnit(final Entry rawClasspathEntry, + public ClasspathEntryWorkUnit(final ClasspathElementAndClassLoader rawClasspathEntry, final ClasspathElement parentClasspathElement, final int orderWithinParentClasspathElement) { this.rawClasspathEntry = rawClasspathEntry; this.parentClasspathElement = parentClasspathElement; @@ -404,15 +405,13 @@ public ClasspathEntryWorkUnit(final Entry rawClasspathEntry * The classpath element singleton map. For each classpath element path, canonicalize path, and create a * ClasspathElement singleton. */ - private final SingletonMap, ClasspathElement, IOException> // + private final SingletonMap // classpathEntryToClasspathElementSingletonMap = // - new SingletonMap, ClasspathElement, IOException>() { + new SingletonMap() { @Override - public ClasspathElement newInstance(final Entry classpathEntry, + public ClasspathElement newInstance(final ClasspathElementAndClassLoader classpathEntry, final LogNode log) throws IOException, InterruptedException { - Object classpathEntryObj = classpathEntry.getKey(); - final ClassLoader classLoader = classpathEntry.getValue(); - + Object classpathEntryObj = classpathEntry.classpathElement; String classpathEntryPath; if (classpathEntryObj instanceof URL || classpathEntryObj instanceof URI) { String scheme = classpathEntryObj instanceof URL ? ((URL) classpathEntryObj).getProtocol() @@ -437,12 +436,12 @@ public ClasspathElement newInstance(final Entry classpathEn : ((URI) classpathEntryObj).getPath(); } else if ("http".equals(scheme) || "https".equals(scheme)) { // Jar URL or URI (remote URLs/URIs must be jars) - return new ClasspathElementZip(classpathEntryObj, classLoader, nestedJarHandler, - scanSpec); + return new ClasspathElementZip(classpathEntryObj, classpathEntry.classLoader, + nestedJarHandler, scanSpec); } else { // For custom URL schemes, assume it must be for a jar, not a directory - return new ClasspathElementZip(classpathEntryObj, classLoader, nestedJarHandler, - scanSpec); + return new ClasspathElementZip(classpathEntryObj, classpathEntry.classLoader, + nestedJarHandler, scanSpec); } } else { // classpathEntryObj is a string @@ -455,7 +454,8 @@ public ClasspathElement newInstance(final Entry classpathEn // "http:", "https:" or any other URL/URI scheme must indicate a jar if (JarUtils.URL_SCHEME_PATTERN.matcher(pathNormalized).matches()) { - return new ClasspathElementZip(classpathEntryPath, classLoader, nestedJarHandler, scanSpec); + return new ClasspathElementZip(classpathEntryPath, classpathEntry.classLoader, + nestedJarHandler, scanSpec); } // Strip everything after first "!", to get path of base jarfile or dir @@ -493,7 +493,8 @@ public ClasspathElement newInstance(final Entry classpathEn // only recurse once, since File::getCanonicalFile and FastPathResolver::resolve are // idempotent) try { - return this.get(new SimpleEntry<>(canonicalPathNormalized, classLoader), log); + return this.get(new ClasspathElementAndClassLoader(canonicalPathNormalized, + classpathEntry.classLoader), log); } catch (final NullSingletonException e) { throw new IOException("Cannot get classpath element for canonical path " + canonicalPathNormalized + " : " + e); @@ -503,10 +504,10 @@ public ClasspathElement newInstance(final Entry classpathEn // been seen -- instantiate a ClasspathElementZip or ClasspathElementDir singleton // for the classpath element path return isJar - ? new ClasspathElementZip(canonicalPathNormalized, classLoader, nestedJarHandler, - scanSpec) - : new ClasspathElementDir(fileCanonicalized, classLoader, nestedJarHandler, - scanSpec); + ? new ClasspathElementZip(canonicalPathNormalized, classpathEntry.classLoader, + nestedJarHandler, scanSpec) + : new ClasspathElementDir(fileCanonicalized, classpathEntry.classLoader, + nestedJarHandler, scanSpec); } } }; @@ -568,8 +569,8 @@ public void processWorkUnit(final ClasspathEntryWorkUnit workUnit, final int wor } } catch (final IOException | SecurityException e) { if (log != null) { - log.log("Skipping invalid classpath element " + workUnit.rawClasspathEntry.getKey() + " : " - + e); + log.log("Skipping invalid classpath element " + workUnit.rawClasspathEntry.classpathElement + + " : " + e); } } } @@ -975,7 +976,8 @@ 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 Entry rawClasspathEntry : classpathFinder.getClasspathOrder().getOrder()) { + for (final ClasspathElementAndClassLoader rawClasspathEntry : classpathFinder.getClasspathOrder() + .getOrder()) { rawClasspathEntryWorkUnits .add(new ClasspathEntryWorkUnit(rawClasspathEntry, /* parentClasspathElement = */ null, /* orderWithinParentClasspathElement = */ rawClasspathEntryWorkUnits.size())); 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 5cbe4aaa0..662cc54ae 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java @@ -32,11 +32,9 @@ import java.lang.reflect.Array; import java.net.URI; import java.net.URL; -import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.HashSet; import java.util.List; -import java.util.Map.Entry; import java.util.Set; import io.github.classgraph.ClassGraph.ClasspathElementFilter; @@ -55,7 +53,31 @@ public class ClasspathOrder { private final Set classpathEntryUniqueResolvedPaths = new HashSet<>(); /** The classpath order. Keys are instances of {@link String}, {@link URL} or {@link URI}. */ - private final List> order = new ArrayList<>(); + private final List order = new ArrayList<>(); + + /** + * A classpath element and the {@link ClassLoader} it was obtained from. + */ + public static class ClasspathElementAndClassLoader { + /** The classpath element (a {@link String}, {@link URL} or {@link URI}). */ + public final Object classpathElement; + + /** The classloader the classpath element was obtained from. */ + public final ClassLoader classLoader; + + /** + * Constructor. + * + * @param classpathElement + * the classpath element (a {@link String}, {@link URL} or {@link URI}). + * @param classLoader + * the classloader the classpath element was obtained from. + */ + public ClasspathElementAndClassLoader(final Object classpathElement, final ClassLoader classLoader) { + this.classpathElement = classpathElement; + this.classLoader = classLoader; + } + } /** * Constructor. @@ -68,11 +90,11 @@ public class ClasspathOrder { } /** - * Get the order of classpath elements, as an ordered set. + * Get the order of classpath elements, uniquified and in order. * - * @return the classpath order, as (path/URL/URI, ClassLoader) tuples. + * @return the classpath order. */ - public List> getOrder() { + public List getOrder() { return order; } @@ -115,7 +137,7 @@ private boolean filter(final String classpathElementPath) { */ boolean addSystemClasspathEntry(final String pathEntry, final ClassLoader classLoader) { if (classpathEntryUniqueResolvedPaths.add(pathEntry)) { - order.add(new SimpleEntry<>(pathEntry, classLoader)); + order.add(new ClasspathElementAndClassLoader(pathEntry, classLoader)); return true; } return false; @@ -141,7 +163,7 @@ private boolean addClasspathEntry(final Object pathElement, final String pathEle if (pathElement instanceof URL || pathElement instanceof URI) { // Assume that any custom URLs or URIs passed in are not in lib or ext dir, and are not system jars if (classpathEntryUniqueResolvedPaths.add(pathElementStr)) { - order.add(new SimpleEntry<>(pathElement, classLoader)); + order.add(new ClasspathElementAndClassLoader(pathElement, classLoader)); return true; } } else { @@ -153,7 +175,7 @@ private boolean addClasspathEntry(final Object pathElement, final String pathEle return false; } if (classpathEntryUniqueResolvedPaths.add(pathElementStr)) { - order.add(new SimpleEntry<>(pathElementStr, classLoader)); + order.add(new ClasspathElementAndClassLoader(pathElementStr, classLoader)); return true; } } 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 dea0923ca..b057bec5e 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/JSONUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/json/JSONUtils.java @@ -388,7 +388,9 @@ static boolean isAccessibleOrMakeAccessible(final AccessibleObject fieldOrConstr try { // Have to use double casting and wrap in new Object[] due to Animal Sniffer bug: // https://github.com/mojohaus/animal-sniffer/issues/67 - accessible.set((Boolean) isAccessibleMethodHandle.invoke(new Object[] { fieldOrConstructor })); + final Object invokeResult = (Object) isAccessibleMethodHandle + .invoke(new Object[] { fieldOrConstructor }); + accessible.set((Boolean) invokeResult); } catch (final Throwable e) { // Ignore } @@ -406,8 +408,11 @@ static boolean isAccessibleOrMakeAccessible(final AccessibleObject fieldOrConstr if (!accessible.get()) { if (trySetAccessibleMethodHandle != null) { try { - accessible.set( - (Boolean) trySetAccessibleMethodHandle.invoke(new Object[] { fieldOrConstructor })); + // 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 } @@ -432,8 +437,11 @@ static boolean isAccessibleOrMakeAccessible(final AccessibleObject fieldOrConstr public Void run() { if (trySetAccessibleMethodHandle != null) { try { - accessible.set((Boolean) trySetAccessibleMethodHandle - .invoke(new Object[] { fieldOrConstructor })); + // 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 } From 3f37cbc94eb0515942ab60f4504e6d17b574b296 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Nov 2019 13:16:42 -0700 Subject: [PATCH 0511/1778] Refactoring --- .../fastzipfilereader/NestedJarHandler.java | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 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 f1198336d..631260705 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java @@ -780,22 +780,19 @@ public File makeTempFile(final String filePath, final boolean onlyUseLeafname) t */ private PhysicalZipFile downloadJarFromURL(final String jarURL, final LogNode log) throws IOException, InterruptedException { - InputStream inputStream = null; + URL url = null; try { - URL url = null; + url = new URL(jarURL); + } catch (final MalformedURLException e1) { try { - url = new URL(jarURL); - } catch (final MalformedURLException e1) { - try { - url = new URI(jarURL).toURL(); - } catch (final URISyntaxException e2) { - throw new IOException("Could not parse URL: " + jarURL); - } + url = new URI(jarURL).toURL(); + } catch (final URISyntaxException e2) { + throw new IOException("Could not parse URL: " + jarURL); } - + } + try (InputStream inputStream = url.openStream()) { // Fetch the jar contents from the URL's InputStream. // If it doesn't fit in RAM, spill over to disk. - inputStream = url.openStream(); final MappedByteBufferResources bufResources = readAllBytesAsByteBufferWithSpilloverToDisk(inputStream, MAX_JAR_RAM_SIZE, jarURL, log); mappedByteBufferResources.add(bufResources); @@ -824,9 +821,6 @@ private PhysicalZipFile downloadJarFromURL(final String jarURL, final LogNode lo } catch (final MalformedURLException e) { throw new IOException("Malformed URL: " + jarURL); } finally { - if (inputStream != null) { - inputStream.close(); - } if (log != null) { log.addElapsedTime(); log.log("***** Note that it is time-consuming to scan jars at non-\"file:\" URLs, " From d3dc48ee66736f24fe3c08979cb233d7273c0106 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Nov 2019 13:18:21 -0700 Subject: [PATCH 0512/1778] [maven-release-plugin] prepare release classgraph-4.8.56 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1bfac962a..a9ff56236 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.56-SNAPSHOT + 4.8.56 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From cebb447f36d6f4f618591de5cc7cb3e499993c98 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Nov 2019 13:18:27 -0700 Subject: [PATCH 0513/1778] [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 a9ff56236..cadefcfb3 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.56 + 4.8.57-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From b403e20be936c1a8d9a8d45ad29c2f289f8759e1 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Nov 2019 13:33:12 -0700 Subject: [PATCH 0514/1778] mvn -> ./mvnw --- .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 95f044f5f..6f00444f2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,4 +23,4 @@ jobs: - name: print Java version run: java -version - name: Build with Maven - run: mvn clean package --file pom.xml + run: ./mvnw clean package --file pom.xml From 936e15b075c4e234b87327e84e3a12caacd5b126 Mon Sep 17 00:00:00 2001 From: "Sean C. Sullivan" Date: Wed, 27 Nov 2019 15:42:04 -0500 Subject: [PATCH 0515/1778] add JDK 13 to GitHub CI workflow --- .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 6f00444f2..ce71bfe59 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - java: [ '1.8.0', '11.0.x' ] + java: [ '1.8.0', '11.0.x', '13.0.x' ] steps: - uses: actions/checkout@v1 - name: Set up JDK From 50ba3a9b03a956f9c87fe88fab503e18bc2a00af Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Nov 2019 13:44:02 -0700 Subject: [PATCH 0516/1778] Run tests --- .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 6f00444f2..a74a145a8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,4 +23,4 @@ jobs: - name: print Java version run: java -version - name: Build with Maven - run: ./mvnw clean package --file pom.xml + run: ./mvnw clean test package --file pom.xml From 2a951939887278a168f251e326650eff74dab4af Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Nov 2019 13:46:50 -0700 Subject: [PATCH 0517/1778] Also test on JDK 12 --- .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 afadf4ba1..2a7e9a339 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - java: [ '1.8.0', '11.0.x', '13.0.x' ] + java: [ '1.8.0', '11.0.x', '12.0.x', '13.0.x' ] steps: - uses: actions/checkout@v1 - name: Set up JDK From ee7a0f6627fd947fea1b020553955a2986334b17 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Nov 2019 19:14:23 -0700 Subject: [PATCH 0518/1778] Refactoring --- .../fastzipfilereader/NestedJarHandler.java | 151 +++++++++--------- 1 file changed, 74 insertions(+), 77 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 631260705..269601af7 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java @@ -512,25 +512,89 @@ public static class MappedByteBufferResources { private FileChannel fileChannel; private ByteBuffer byteBuffer; private boolean byteBufferIsMapped; - private NestedJarHandler nestedJarHandler; + private final NestedJarHandler nestedJarHandler; /** - * Map a {@link ByteBuffer} from a {@link File}. + * 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}. + * @param maxRAMBufferSize + * the max RAM to use before spilling over to a temp file on disk. + * @param sourceURL + * the source URL that inputStream was opened from. + * @param log + * the log + * @throws IOException + * If the contents could not be read. + */ + public MappedByteBufferResources(final InputStream inputStream, final int maxRAMBufferSize, + final String sourceURL, final NestedJarHandler nestedJarHandler, final LogNode log) + throws IOException { + this.nestedJarHandler = nestedJarHandler; + if (maxRAMBufferSize > FileUtils.MAX_BUFFER_SIZE) { + throw new IOException("InputStream is too large to read"); + } + final byte[] buf = new byte[maxRAMBufferSize]; + final int bufLength = buf.length; + + int totBytesRead = 0; + int bytesRead = 0; + while ((bytesRead = inputStream.read(buf, totBytesRead, bufLength - totBytesRead)) > 0) { + // Fill buffer until nothing more can be read + totBytesRead += bytesRead; + } + if (bytesRead < 0) { + // Successfully reached end of stream -- wrap array with ByteBuffer and return it + byteBuffer = ByteBuffer.wrap(buf, 0, totBytesRead); + // Don't set file, fileChannel or raf, they are unneeded. + // byteBufferIsMapped will remain false. + } else { + // bytesRead == 0 => ran out of buffer space, spill over to disk + final File tempFile = nestedJarHandler.makeTempFile(sourceURL, /* onlyUseLeafname = */ true); + if (log != null) { + log.log("Could not fit downloaded URL into max RAM buffer size of " + maxRAMBufferSize + + " bytes, downloading to temporary file: " + sourceURL + " -> " + tempFile); + } + Files.write(tempFile.toPath(), buf, StandardOpenOption.WRITE); + try (OutputStream os = new BufferedOutputStream( + new FileOutputStream(tempFile, /* append = */ true))) { + for (int bytesReadCtd; (bytesReadCtd = inputStream.read(buf, 0, buf.length)) > 0;) { + os.write(buf, 0, bytesReadCtd); + } + } + // Map the file to a MappedByteBuffer + mapFile(tempFile); + } + } + + /** + * Map a {@link File} to a {@link MappedByteBuffer}. * * @param file * the file * @param nestedJarHandler * the nested jar handler * @throws IOException - * On I/O exception. + * If the contents could not be read. */ public MappedByteBufferResources(final File file, final NestedJarHandler nestedJarHandler) throws IOException { - this.file = file; this.nestedJarHandler = nestedJarHandler; - if (byteBuffer != null) { - throw new IllegalArgumentException("Already open"); - } + mapFile(file); + } + + /** + * Map a {@link File} to a {@link MappedByteBuffer}. + * + * @param file + * the file + * @throws IOException + * Signals that an I/O exception has occurred. + */ + private void mapFile(final File file) throws IOException { + this.file = file; try { raf = new RandomAccessFile(file, "r"); fileChannel = raf.getChannel(); @@ -551,20 +615,7 @@ public MappedByteBufferResources(final File file, final NestedJarHandler nestedJ } /** - * Wrap a {@link ByteBuffer} that does not need to be unmapped (for cases where a {@link URL} was fetched - * but it fit in RAM, so didn't need to spill over to disk). - * - * @param byteBuffer - * the byte buffer. - */ - public MappedByteBufferResources(final ByteBuffer byteBuffer) { - this.byteBuffer = byteBuffer; - // Don't set fileChannel or raf, they are unneeded. - // byteBufferIsMapped will remain false. - } - - /** - * Get the mapped file (or null if a {@link ByteBuffer} was wrapped instead). + * Get the mapped file (or null if an in-memory {@link ByteBuffer} was wrapped instead). * * @return the mapped file */ @@ -665,60 +716,6 @@ public void unmapByteBuffer(final ByteBuffer byteBuffer, final LogNode log) { // ------------------------------------------------------------------------------------------------------------- - /** - * 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}. - * @param maxRAMBufferSize - * the max RAM to use before spilling over to a temp file on disk. - * @param sourceURL - * the source URL that inputStream was opened from. - * @param log - * the log - * @return The {@link MappedByteBufferResources} contaning the {@link ByteBuffer} obtained by fetching the - * {@link InputStream}. - * @throws IOException - * If the contents could not be read. - */ - private MappedByteBufferResources readAllBytesAsByteBufferWithSpilloverToDisk(final InputStream inputStream, - final int maxRAMBufferSize, final String sourceURL, final LogNode log) throws IOException { - if (maxRAMBufferSize > FileUtils.MAX_BUFFER_SIZE) { - throw new IOException("InputStream is too large to read"); - } - final byte[] buf = new byte[maxRAMBufferSize]; - final int bufLength = buf.length; - - int totBytesRead = 0; - int bytesRead = 0; - while ((bytesRead = inputStream.read(buf, totBytesRead, bufLength - totBytesRead)) > 0) { - // Fill buffer until nothing more can be read - totBytesRead += bytesRead; - } - if (bytesRead < 0) { - // Successfully reached end of stream -- wrap array with ByteBuffer and return it - return new MappedByteBufferResources(ByteBuffer.wrap(buf, 0, totBytesRead)); - } else { - // bytesRead == 0 => ran out of buffer space, spill over to disk - final File tempFile = makeTempFile(sourceURL, /* onlyUseLeafname = */ true); - if (log != null) { - log.log("Could not fit downloaded URL into max RAM buffer size of " + maxRAMBufferSize - + " bytes, downloading to temporary file: " + sourceURL + " -> " + tempFile); - } - Files.write(tempFile.toPath(), buf, StandardOpenOption.WRITE); - try (OutputStream os = new BufferedOutputStream(new FileOutputStream(tempFile, /* append = */ true))) { - bytesRead = 0; - while ((bytesRead = inputStream.read(buf, 0, buf.length)) > 0) { - os.write(buf, 0, bytesRead); - } - } - return new MappedByteBufferResources(tempFile, this); - } - } - - // ------------------------------------------------------------------------------------------------------------- - /** * Get the leafname of a path. * @@ -793,8 +790,8 @@ private PhysicalZipFile downloadJarFromURL(final String jarURL, final LogNode lo try (InputStream inputStream = url.openStream()) { // Fetch the jar contents from the URL's InputStream. // If it doesn't fit in RAM, spill over to disk. - final MappedByteBufferResources bufResources = readAllBytesAsByteBufferWithSpilloverToDisk(inputStream, - MAX_JAR_RAM_SIZE, jarURL, log); + final MappedByteBufferResources bufResources = new MappedByteBufferResources(inputStream, + MAX_JAR_RAM_SIZE, jarURL, this, log); mappedByteBufferResources.add(bufResources); PhysicalZipFile physicalZipFile = null; From cec84c33e9aa81ec089b3df62a0ffad0c791e9f2 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Nov 2019 23:44:07 -0700 Subject: [PATCH 0519/1778] Code simplification --- .../fastzipfilereader/NestedJarHandler.java | 163 +++++++----------- .../io/github/classgraph/json/JSONUtils.java | 2 +- 2 files changed, 59 insertions(+), 106 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 269601af7..382ec705b 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java @@ -44,7 +44,6 @@ import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; -import java.nio.file.StandardCopyOption; import java.nio.file.StandardOpenOption; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; @@ -93,8 +92,11 @@ public PhysicalZipFile newInstance(final File canonicalFile, final LogNode log) } }; - /** {@link PhysicalZipFile} instances created to extract nested jarfiles to disk or RAM. */ - private Queue additionalAllocatedPhysicalZipFiles = new ConcurrentLinkedQueue<>(); + /** The allocated {@link PhysicalZipFile} instances. */ + private Queue allocatedPhysicalZipFiles = new ConcurrentLinkedQueue<>(); + + /** The allocated {@link LogicalZipFile} instances. */ + private final Queue allocatedLogicalZipFiles = new ConcurrentLinkedQueue<>(); /** * A singleton map from a {@link FastZipEntry} to the {@link ZipFileSlice} wrapping either the zip entry data, @@ -117,85 +119,39 @@ public ZipFileSlice newInstance(final FastZipEntry childZipEntry, final LogNode // If child entry is deflated i.e. (for a deflated nested zipfile), must inflate // the contents of the entry before its central directory can be read (most of // the time nested zipfiles are stored, not deflated, so this should be rare) - if ((childZipEntry.uncompressedSize < 0L - || childZipEntry.uncompressedSize >= INFLATE_TO_DISK_THRESHOLD - // Also check compressed size for safety, in case uncompressed size is wrong - || childZipEntry.compressedSize >= INFLATE_TO_DISK_THRESHOLD)) { - // If child entry's size is unknown or the file is large, inflate to disk - File tempFile = null; - try { - // Create temp file - tempFile = makeTempFile(childZipEntry.entryName, /* onlyUseLeafname = */ true); - - // Inflate zip entry to temp file - if (log != null) { - log.log("Deflating zip entry to temporary file: " + childZipEntry - + " ; uncompressed size: " + childZipEntry.uncompressedSize + " ; temp file: " - + tempFile); - } - try (InputStream inputStream = childZipEntry.open()) { - Files.copy(inputStream, tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING); - } - - // Get or create a PhysicalZipFile instance for the new temp file - final PhysicalZipFile physicalZipFile = canonicalFileToPhysicalZipFile(tempFile, log); - additionalAllocatedPhysicalZipFiles.add(physicalZipFile); - - // Create a new logical slice of the whole physical zipfile - childZipEntrySlice = new ZipFileSlice(physicalZipFile); - - } catch (final IllegalArgumentException | IOException e) { - // Could not make temp file, or failed to extract entire contents of entry - if (log != null) { - log.log("Deflating zip entry to temporary file failed: " + e); - } - if (tempFile != null) { - // Delete temp file, in case it contains partially-extracted data - // due to running out of disk space - try { - Files.delete(tempFile.toPath()); - } catch (final IOException | SecurityException e2) { - if (log != null) { - log.log("Removing temporary file failed: " + e2); - } - } - } - childZipEntrySlice = null; - } - } else { - childZipEntrySlice = null; + if (log != null) { + log.log("Deflating nested zip entry: " + childZipEntry + " ; uncompressed size: " + + childZipEntry.uncompressedSize); } - if (childZipEntrySlice == null) { - // If the uncompressed size known and small, or inflating to temp file failed, - // inflate to a ByteBuffer in memory instead - if (childZipEntry.uncompressedSize > FileUtils.MAX_BUFFER_SIZE) { - // Impose 2GB limit (i.e. a max of one ByteBuffer chunk) on inflation to memory - throw new IOException("Uncompressed size of zip entry (" + childZipEntry.uncompressedSize - + ") is too large to inflate to memory: " + childZipEntry.entryName); - } - // Open the zip entry to fetch inflated data, and read the whole contents of the - // InputStream to a byte[] array, then wrap it in a ByteBuffer - if (log != null) { - log.log("Deflating zip entry to RAM: " + childZipEntry + " ; uncompressed size: " - + childZipEntry.uncompressedSize); - } - ByteBuffer byteBuffer; - try (InputStream inputStream = childZipEntry.open()) { - byteBuffer = ByteBuffer - .wrap(FileUtils.readAllBytesAsArray(inputStream, childZipEntry.uncompressedSize)); + // Read the InputStream for the child zip entry to a RAM buffer, or spill to disk if it's too large + final MappedByteBufferResources bufResources = new MappedByteBufferResources(childZipEntry.open(), + childZipEntry.entryName, NestedJarHandler.this, log); + mappedByteBufferResources.add(bufResources); + + PhysicalZipFile physicalZipFile = null; + final File tempFile = bufResources.getFile(); + if (tempFile != null) { + // InputStream would not fit within the RAM limit, so spilled over to disk + // Wrap temp file in a PhysicalZipFile + physicalZipFile = canonicalFileToPhysicalZipFile(tempFile, log); + if (physicalZipFile == null) { + // Should not happen + throw ClassGraphException.newClassGraphException("physicalZipFile should not be null"); } + // Create a new logical slice of the whole physical zipfile + childZipEntrySlice = new ZipFileSlice(physicalZipFile); + } else { // Create a new PhysicalZipFile that wraps the ByteBuffer as if the buffer had been // mmap'd to a file on disk - final PhysicalZipFile physicalZipFileInRam = new PhysicalZipFile(byteBuffer, + physicalZipFile = new PhysicalZipFile(bufResources.getByteBuffer(), /* outermostFile = */ childZipEntry.parentLogicalZipFile.physicalZipFile.getFile(), childZipEntry.getPath(), NestedJarHandler.this); - additionalAllocatedPhysicalZipFiles.add(physicalZipFileInRam); - - // Create a new logical slice of the whole physical in-memory zipfile - childZipEntrySlice = new ZipFileSlice(physicalZipFileInRam, childZipEntry); + // Create a new logical slice of the in-memory extracted inner zipfile + childZipEntrySlice = new ZipFileSlice(physicalZipFile, childZipEntry); } + allocatedPhysicalZipFiles.add(physicalZipFile); } return childZipEntrySlice; } @@ -218,9 +174,6 @@ public LogicalZipFile newInstance(final ZipFileSlice zipFileSlice, final LogNode } }; - /** All allocated LogicalZipFile instances. */ - private final Queue allocatedLogicalZipFiles = new ConcurrentLinkedQueue<>(); - /** * 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. @@ -451,12 +404,6 @@ public RecyclableInflater newInstance() throws RuntimeException { */ private static final int MAX_JAR_RAM_SIZE = 64 * 1024 * 1024; - /** - * The maximum uncompressed size above which nested deflated jars are inflated to a temporary file on disk, - * rather than to RAM. - */ - private static final int INFLATE_TO_DISK_THRESHOLD = MAX_JAR_RAM_SIZE; - /** True if {@link #close(LogNode)} has been called. */ private final AtomicBoolean closed = new AtomicBoolean(false); @@ -508,6 +455,7 @@ private PhysicalZipFile canonicalFileToPhysicalZipFile(final File canonicalFile, /** Resources for a mapped file. */ public static class MappedByteBufferResources { private File file; + private boolean fileIsTempFile; private RandomAccessFile raf; private FileChannel fileChannel; private ByteBuffer byteBuffer; @@ -520,23 +468,18 @@ public static class MappedByteBufferResources { * * @param inputStream * The {@link InputStream}. - * @param maxRAMBufferSize - * the max RAM to use before spilling over to a temp file on disk. - * @param sourceURL - * the source URL that inputStream was opened from. + * @param tempFileBaseName + * the source URL or zip entry that inputStream was opened from (used to name temporary file, if + * needed). * @param log - * the log + * the log. * @throws IOException * If the contents could not be read. */ - public MappedByteBufferResources(final InputStream inputStream, final int maxRAMBufferSize, - final String sourceURL, final NestedJarHandler nestedJarHandler, final LogNode log) - throws IOException { + public MappedByteBufferResources(final InputStream inputStream, final String tempFileBaseName, + final NestedJarHandler nestedJarHandler, final LogNode log) throws IOException { this.nestedJarHandler = nestedJarHandler; - if (maxRAMBufferSize > FileUtils.MAX_BUFFER_SIZE) { - throw new IOException("InputStream is too large to read"); - } - final byte[] buf = new byte[maxRAMBufferSize]; + final byte[] buf = new byte[MAX_JAR_RAM_SIZE]; final int bufLength = buf.length; int totBytesRead = 0; @@ -552,10 +495,11 @@ public MappedByteBufferResources(final InputStream inputStream, final int maxRAM // byteBufferIsMapped will remain false. } else { // bytesRead == 0 => ran out of buffer space, spill over to disk - final File tempFile = nestedJarHandler.makeTempFile(sourceURL, /* onlyUseLeafname = */ true); + final File tempFile = nestedJarHandler.makeTempFile(tempFileBaseName, /* onlyUseLeafname = */ true); + fileIsTempFile = true; if (log != null) { - log.log("Could not fit downloaded URL into max RAM buffer size of " + maxRAMBufferSize - + " bytes, downloading to temporary file: " + sourceURL + " -> " + tempFile); + log.log("Could not fit downloaded URL into max RAM buffer size of " + MAX_JAR_RAM_SIZE + + " bytes, downloading to temporary file: " + tempFileBaseName + " -> " + tempFile); } Files.write(tempFile.toPath(), buf, StandardOpenOption.WRITE); try (OutputStream os = new BufferedOutputStream( @@ -659,6 +603,16 @@ public void close(final LogNode log) { // Ignore } } + if (file != null && fileIsTempFile) { + try { + Files.delete(file.toPath()); + } catch (final IOException e) { + if (log != null) { + log.log("Could not delete temporary file " + file + " : " + e); + } + } + file = null; + } } } @@ -790,8 +744,8 @@ private PhysicalZipFile downloadJarFromURL(final String jarURL, final LogNode lo try (InputStream inputStream = url.openStream()) { // Fetch the jar contents from the URL's InputStream. // If it doesn't fit in RAM, spill over to disk. - final MappedByteBufferResources bufResources = new MappedByteBufferResources(inputStream, - MAX_JAR_RAM_SIZE, jarURL, this, log); + final MappedByteBufferResources bufResources = new MappedByteBufferResources(inputStream, jarURL, this, + log); mappedByteBufferResources.add(bufResources); PhysicalZipFile physicalZipFile = null; @@ -804,8 +758,6 @@ private PhysicalZipFile downloadJarFromURL(final String jarURL, final LogNode lo // Should not happen throw ClassGraphException.newClassGraphException("physicalZipFile should not be null"); } - // Add temp file to queue of physical zipfiles that have to be unmapped on close - additionalAllocatedPhysicalZipFiles.add(physicalZipFile); } else { // Wrap ByteBuffer in a PhysicalZipFile. (No need to add to additionalAllocatedPhysicalZipFiles, // since byteBuffer is not a DirectByteBuffer, it just wraps a standard Java byte array, so the @@ -813,6 +765,7 @@ private PhysicalZipFile downloadJarFromURL(final String jarURL, final LogNode lo physicalZipFile = new PhysicalZipFile(bufResources.getByteBuffer(), /* outermostFile = */ null, jarURL, NestedJarHandler.this); } + allocatedPhysicalZipFiles.add(physicalZipFile); return physicalZipFile; } catch (final MalformedURLException e) { @@ -875,13 +828,13 @@ public void close(final LogNode log) { canonicalFileToPhysicalZipFileMap.clear(); canonicalFileToPhysicalZipFileMap = null; } - if (additionalAllocatedPhysicalZipFiles != null) { - for (PhysicalZipFile physicalZipFile; (physicalZipFile = additionalAllocatedPhysicalZipFiles + if (allocatedPhysicalZipFiles != null) { + for (PhysicalZipFile physicalZipFile; (physicalZipFile = allocatedPhysicalZipFiles .poll()) != null;) { physicalZipFile.close(); } - additionalAllocatedPhysicalZipFiles.clear(); - additionalAllocatedPhysicalZipFiles = null; + allocatedPhysicalZipFiles.clear(); + allocatedPhysicalZipFiles = null; } if (fastZipEntryToZipFileSliceMap != null) { fastZipEntryToZipFileSliceMap.clear(); 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 b057bec5e..ace5e212f 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/JSONUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/json/JSONUtils.java @@ -388,7 +388,7 @@ static boolean isAccessibleOrMakeAccessible(final AccessibleObject fieldOrConstr 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 = (Object) isAccessibleMethodHandle + final Object invokeResult = isAccessibleMethodHandle .invoke(new Object[] { fieldOrConstructor }); accessible.set((Boolean) invokeResult); } catch (final Throwable e) { From bdf4054a99e513690c9fd7ec0f94b9de94eda41c Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Thu, 28 Nov 2019 12:29:21 +0000 Subject: [PATCH 0520/1778] Enable remote jar scanning for URLs inside URLClassLoader. --- .../java/io/github/classgraph/Scanner.java | 5 +- .../URLClassLoaderHandler.java | 2 +- .../classgraph/features/CustomURLScheme.java | 55 +++++++++++++++ .../issues/issue384/Issue384Test.java | 34 +++------ .../issues/issue387/Issue387Test.java | 70 +++++++++++++++++++ 5 files changed, 138 insertions(+), 28 deletions(-) create mode 100644 src/test/java/io/github/classgraph/features/CustomURLScheme.java create mode 100644 src/test/java/io/github/classgraph/issues/issue387/Issue387Test.java diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 2fd62ea1f..259f30d08 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -35,6 +35,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.net.URL; +import java.net.URLDecoder; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Collection; @@ -431,9 +432,9 @@ public ClasspathElement newInstance(final ClasspathElementAndClassLoader classpa if ("file".equals(scheme)) { // Extract file path, and use below as a path string to determine if this // classpath element is a file (jar) or directory - classpathEntryPath = classpathEntryObj instanceof URL + classpathEntryPath = URLDecoder.decode(classpathEntryObj instanceof URL ? ((URL) classpathEntryObj).getPath() - : ((URI) classpathEntryObj).getPath(); + : ((URI) classpathEntryObj).getPath(), "UTF-8"); } else if ("http".equals(scheme) || "https".equals(scheme)) { // Jar URL or URI (remote URLs/URIs must be jars) return new ClasspathElementZip(classpathEntryObj, classpathEntry.classLoader, 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 cb3a62795..bf7399876 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/URLClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/URLClassLoaderHandler.java @@ -89,7 +89,7 @@ public static void findClasspathOrder(final ClassLoader classLoader, final Class if (urls != null) { for (final URL url : urls) { if (url != null) { - classpathOrder.addClasspathEntry(url.toString(), classLoader, scanSpec, log); + classpathOrder.addClasspathEntry(url, classLoader, scanSpec, log); } } } diff --git a/src/test/java/io/github/classgraph/features/CustomURLScheme.java b/src/test/java/io/github/classgraph/features/CustomURLScheme.java new file mode 100644 index 000000000..4fe063bd5 --- /dev/null +++ b/src/test/java/io/github/classgraph/features/CustomURLScheme.java @@ -0,0 +1,55 @@ +/* + * 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.features; + +import java.io.IOException; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLStreamHandler; +import java.util.HashMap; +import java.util.Map; + +public class CustomURLScheme { + public static final String SCHEME = "customscheme"; + + public static Map remappedURLs = new HashMap<>(); + + static { + URL.setURLStreamHandlerFactory(protocol -> SCHEME.equals(protocol) ? new URLStreamHandler() { + @Override + protected URLConnection openConnection(final URL url) throws IOException { + // Record that the URL was remapped, so we know this custom URLStreamHandler was called + final String newURL = "file:" + url.getPath(); + remappedURLs.put(url.toString(), newURL); + // Replace scheme with "file://" + return new URL(newURL).openConnection(); + } + } : null); + } +} \ No newline at end of file diff --git a/src/test/java/io/github/classgraph/issues/issue384/Issue384Test.java b/src/test/java/io/github/classgraph/issues/issue384/Issue384Test.java index aa7ab3335..1a6569a2d 100644 --- a/src/test/java/io/github/classgraph/issues/issue384/Issue384Test.java +++ b/src/test/java/io/github/classgraph/issues/issue384/Issue384Test.java @@ -30,15 +30,12 @@ import static org.assertj.core.api.Assertions.assertThat; -import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; -import java.net.URLConnection; -import java.net.URLStreamHandler; import java.util.AbstractMap.SimpleEntry; -import java.util.HashMap; -import java.util.Map; +import io.github.classgraph.features.CustomURLScheme; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; @@ -47,36 +44,23 @@ /** * Test. */ -public class Issue384Test { - - private static final String SCHEME = "customscheme"; - - private static Map remappedURLs = new HashMap<>(); - - static { - URL.setURLStreamHandlerFactory(protocol -> SCHEME.equals(protocol) ? new URLStreamHandler() { - @Override - protected URLConnection openConnection(final URL url) throws IOException { - // Record that the URL was remapped, so we know this custom URLStreamHandler was called - final String newURL = "file:" + url.getPath(); - remappedURLs.put(url.toString(), newURL); - // Replace scheme with "file://" - return new URL(newURL).openConnection(); - } - } : null); +class Issue384Test { + @BeforeAll + static void setup() { + new CustomURLScheme(); } /** * Test. */ @Test - public void issue384Test() throws MalformedURLException { + void issue384Test() throws MalformedURLException { final String filePath = Issue384Test.class.getClassLoader().getResource("nested-jars-level1.zip").getPath(); - final String customSchemeURL = SCHEME + ":" + filePath; + final String customSchemeURL = CustomURLScheme.SCHEME + ":" + filePath; final URL url = new URL(customSchemeURL); try (ScanResult scanResult = new ClassGraph().enableRemoteJarScanning().overrideClasspath(url).scan()) { assertThat(scanResult.getAllResources().getPaths()).containsExactly("level2.jar"); - assertThat(remappedURLs.entrySet().iterator().next()) + assertThat(CustomURLScheme.remappedURLs.entrySet().iterator().next()) .isEqualTo(new SimpleEntry<>(customSchemeURL, "file:" + filePath)); } } diff --git a/src/test/java/io/github/classgraph/issues/issue387/Issue387Test.java b/src/test/java/io/github/classgraph/issues/issue387/Issue387Test.java new file mode 100644 index 000000000..4765524e6 --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue387/Issue387Test.java @@ -0,0 +1,70 @@ +/* + * 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.issues.issue387; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.net.*; +import java.util.AbstractMap.SimpleEntry; + +import io.github.classgraph.features.CustomURLScheme; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ScanResult; + +/** + * Test. + */ +class Issue387Test { + @BeforeAll + static void setup() { + new CustomURLScheme(); + } + + /** + * Test. + */ + @Test + void issue387Test() throws MalformedURLException { + final String filePath = Issue387Test.class.getClassLoader().getResource("nested-jars-level1.zip").getPath(); + final String customSchemeURL = CustomURLScheme.SCHEME + ":" + filePath; + final URL url = new URL(customSchemeURL); + final URLClassLoader classLoader = new URLClassLoader(new URL[] { url }, null); + try (ScanResult scanResult = new ClassGraph() + .enableRemoteJarScanning() + .overrideClassLoaders(classLoader) + .scan()) { + assertThat(scanResult.getAllResources().getPaths()).containsExactly("level2.jar"); + assertThat(CustomURLScheme.remappedURLs.entrySet().iterator().next()) + .isEqualTo(new SimpleEntry<>(customSchemeURL, "file:" + filePath)); + } + } +} From 45878055543db46251ee707aba1d85de2c3b34d6 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 28 Nov 2019 08:13:45 -0700 Subject: [PATCH 0521/1778] Require 2+ chars for URL schemes so Windows drives don't break (#388) --- .../java/nonapi/io/github/classgraph/utils/JarUtils.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) 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 7ac507561..2da19d120 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/JarUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/JarUtils.java @@ -43,8 +43,11 @@ * Jarfile utilities. */ public final class JarUtils { - /** Check if a path has a URL scheme at the beginning. */ - public static final Pattern URL_SCHEME_PATTERN = Pattern.compile("[a-zA-Z][a-zA-Z0-9+-.]*[:].*"); + /** + * Check if a path has a URL scheme at the beginning. Require at least 2 chars in a URL scheme, so that Windows + * drive designations don't get treated as URL schemes. + */ + public static final Pattern URL_SCHEME_PATTERN = Pattern.compile("[a-zA-Z][a-zA-Z0-9+-.]+[:].*"); /** The Constant DASH_VERSION. */ private static final Pattern DASH_VERSION = Pattern.compile("-(\\d+(\\.|$))"); From 732c3b93ac6c677aae4220d2ebf4fcec099b55cc Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 28 Nov 2019 08:40:38 -0700 Subject: [PATCH 0522/1778] Add `ClassGraph.enableURLScheme(String scheme)` (#387) --- .../java/io/github/classgraph/ClassGraph.java | 27 ++++++++++++++++--- .../java/io/github/classgraph/ScanResult.java | 2 +- .../fastzipfilereader/NestedJarHandler.java | 26 ++++++++++-------- .../github/classgraph/scanspec/ScanSpec.java | 25 ++++++++++++++--- 4 files changed, 61 insertions(+), 19 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index 21e4713d5..b0365d36c 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -1044,13 +1044,34 @@ public ClassGraph blacklistClasspathElementsContainingResourcePath(final String. /** * Enable classpath elements to be fetched from remote ("http:"/"https:") URLs (or URLs with custom schemes). - * This option is disabled by default, as this may present a security vulnerability, since classes from - * downloaded jars can be subsequently loaded using {@link ClassInfo#loadClass}. + * Equivalent to: + * + *

+ * {@code new ClassGraph().enableURLScheme("http").enableURLScheme("https");} + * + *

+ * Scanning from http(s) URLs is disabled by default, as this may present a security vulnerability, since + * classes from downloaded jars can be subsequently loaded using {@link ClassInfo#loadClass}. * * @return this (for method chaining). */ public ClassGraph enableRemoteJarScanning() { - scanSpec.enableRemoteJarScanning = true; + scanSpec.enableURLScheme("http"); + scanSpec.enableURLScheme("https"); + return this; + } + + /** + * Enable classpath elements to be fetched from {@link URL} connections with the specified URL scheme (also + * works for any custom URL schemes that have been defined, as long as they have more than two characters, in + * order to not conflict with Windows drive letters). + * + * @param scheme + * the URL scheme string, e.g. "resource" for a custom "resource:" URL scheme. + * @return this (for method chaining). + */ + public ClassGraph enableURLScheme(final String scheme) { + scanSpec.enableURLScheme(scheme); return this; } diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index 6b283114f..80a115b7f 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -139,7 +139,7 @@ public final class ScanResult implements Closeable, AutoCloseable { // ------------------------------------------------------------------------------------------------------------- /** The current serialization format. */ - private static final String CURRENT_SERIALIZATION_FORMAT = "9"; + private static final String CURRENT_SERIALIZATION_FORMAT = "10"; /** A class to hold a serialized ScanResult along with the ScanSpec that was used to scan. */ private static class SerializationFormat { 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 382ec705b..6c07b1456 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java @@ -200,19 +200,23 @@ public Entry newInstance(final String nestedJarPathRaw, final boolean isURL = JarUtils.URL_SCHEME_PATTERN.matcher(nestedJarPath).matches(); PhysicalZipFile physicalZipFile; if (isURL) { - // Jarfile is at an http:// or https:// URL - if (scanSpec.enableRemoteJarScanning) { - // Download jar to a temp file, or if not possible, to a ByteBuffer in RAM - final LogNode subLog = log == null ? null - : log.log("Downloading jar from URL " + nestedJarPath); - physicalZipFile = downloadJarFromURL(nestedJarPath, subLog); - } else { - throw new IOException( - "Remote jar scanning has not been enabled, cannot scan classpath element: " - + nestedJarPath); + 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 + // schemes were already stripped by FastPathResolver.resolve(nestedJarPathRaw)) + throw new IOException("Scanning of URL scheme \"" + scheme + + "\" has not been enabled -- cannot scan classpath element: " + + nestedJarPath); } + + // Download jar from URL to a ByteBuffer in RAM, or to a temp file on disk + final LogNode subLog = log == null ? null + : log.log("Downloading jar from URL " + nestedJarPath); + physicalZipFile = downloadJarFromURL(nestedJarPath, subLog); + } else { - // Jarfile should be a local file + // Jarfile should be a local file -- wrap in a PhysicalZipFile instance try { final File canonicalFile = new File(nestedJarPath).getCanonicalFile(); physicalZipFile = canonicalFileToPhysicalZipFile(canonicalFile, log); 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 1c8b8ebd4..2c16749cb 100644 --- a/src/main/java/nonapi/io/github/classgraph/scanspec/ScanSpec.java +++ b/src/main/java/nonapi/io/github/classgraph/scanspec/ScanSpec.java @@ -33,7 +33,9 @@ import java.net.URL; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; import io.github.classgraph.ClassGraph.ClasspathElementFilter; import io.github.classgraph.ClassGraphException; @@ -171,11 +173,10 @@ public class ScanSpec { public boolean extendScanningUpwardsToExternalClasses = true; /** - * If true, enable http(s) classpath elements to be fetched to local temporary files and scanned. Disabled by - * default as this may present a security vulnerability, since classes from downloaded jars can be subsequently - * loaded using {@link ClassInfo#loadClass}. + * URL schemes that are allowed in classpath elements (not counting the optional "jar:" prefix and/or "file:", + * which are automatically allowed). */ - public boolean enableRemoteJarScanning; + public Set allowedURLSchemes; // ------------------------------------------------------------------------------------------------------------- @@ -302,6 +303,22 @@ public void addClassLoader(final ClassLoader classLoader) { } } + /** + * Allow a specified URL scheme in classpath elements. + * + * @param scheme + * the scheme, e.g. "http". + */ + public void enableURLScheme(final String scheme) { + if (scheme == null || scheme.length() < 2) { + throw new IllegalArgumentException("URL schemes must contain at least two characters"); + } + if (allowedURLSchemes == null) { + allowedURLSchemes = new HashSet<>(); + } + allowedURLSchemes.add(scheme); + } + /** * Completely override the list of ClassLoaders to scan. (This only works if overrideClasspath() is not called.) * Causes the java.class.path system property to be ignored. From b29aa80ca1f9a4a0e03c7e97422d9e33211d284d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 28 Nov 2019 08:45:10 -0700 Subject: [PATCH 0523/1778] Add Javadoc --- .../java/io/github/classgraph/features/CustomURLScheme.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/test/java/io/github/classgraph/features/CustomURLScheme.java b/src/test/java/io/github/classgraph/features/CustomURLScheme.java index 4fe063bd5..4bb200b7f 100644 --- a/src/test/java/io/github/classgraph/features/CustomURLScheme.java +++ b/src/test/java/io/github/classgraph/features/CustomURLScheme.java @@ -35,9 +35,14 @@ import java.util.HashMap; import java.util.Map; +/** + * CustomURLScheme. + */ public class CustomURLScheme { + /** URL scheme. */ public static final String SCHEME = "customscheme"; + /** Any URLs that were remapped. */ public static Map remappedURLs = new HashMap<>(); static { From c76973c2f152db30d7760c8c6250964cb3c0187e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 28 Nov 2019 08:48:15 -0700 Subject: [PATCH 0524/1778] Ignore empty classpath elements --- .../io/github/classgraph/classpath/ClasspathOrder.java | 6 +++--- 1 file changed, 3 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 662cc54ae..9adc82b45 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java @@ -204,6 +204,9 @@ public boolean addClasspathEntry(final Object pathElement, final ClassLoader cla return false; } final String pathElementStr = pathElement.toString(); + if (pathElementStr.isEmpty()) { + return false; + } if (pathElement instanceof URL || pathElement instanceof URI) { if (!filter(pathElementStr)) { if (log != null) { @@ -223,9 +226,6 @@ public boolean addClasspathEntry(final Object pathElement, final ClassLoader cla return false; } } else { - if (pathElementStr.isEmpty()) { - return false; - } // Check for wildcard path element (allowable for local classpaths as of JDK 6) if (pathElementStr.endsWith("*")) { if (pathElementStr.length() == 1 || // From 630b0967ca833668aaa6cc8dcd7decb0226a80d3 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 28 Nov 2019 10:31:29 -0700 Subject: [PATCH 0525/1778] Fix unit test --- .../java/io/github/classgraph/issues/issue387/Issue387Test.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/io/github/classgraph/issues/issue387/Issue387Test.java b/src/test/java/io/github/classgraph/issues/issue387/Issue387Test.java index 4765524e6..c4f5706b3 100644 --- a/src/test/java/io/github/classgraph/issues/issue387/Issue387Test.java +++ b/src/test/java/io/github/classgraph/issues/issue387/Issue387Test.java @@ -59,7 +59,7 @@ void issue387Test() throws MalformedURLException { final URL url = new URL(customSchemeURL); final URLClassLoader classLoader = new URLClassLoader(new URL[] { url }, null); try (ScanResult scanResult = new ClassGraph() - .enableRemoteJarScanning() + .enableURLScheme(CustomURLScheme.SCHEME) .overrideClassLoaders(classLoader) .scan()) { assertThat(scanResult.getAllResources().getPaths()).containsExactly("level2.jar"); From e9cc109dedd16423276141ac6cabd7bd0e167e8b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 28 Nov 2019 10:32:38 -0700 Subject: [PATCH 0526/1778] Fix unit test --- .../io/github/classgraph/issues/issue384/Issue384Test.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/java/io/github/classgraph/issues/issue384/Issue384Test.java b/src/test/java/io/github/classgraph/issues/issue384/Issue384Test.java index 1a6569a2d..00c76f71f 100644 --- a/src/test/java/io/github/classgraph/issues/issue384/Issue384Test.java +++ b/src/test/java/io/github/classgraph/issues/issue384/Issue384Test.java @@ -58,7 +58,8 @@ void issue384Test() throws MalformedURLException { final String filePath = Issue384Test.class.getClassLoader().getResource("nested-jars-level1.zip").getPath(); final String customSchemeURL = CustomURLScheme.SCHEME + ":" + filePath; final URL url = new URL(customSchemeURL); - try (ScanResult scanResult = new ClassGraph().enableRemoteJarScanning().overrideClasspath(url).scan()) { + try (ScanResult scanResult = new ClassGraph().enableURLScheme(CustomURLScheme.SCHEME).overrideClasspath(url) + .scan()) { assertThat(scanResult.getAllResources().getPaths()).containsExactly("level2.jar"); assertThat(CustomURLScheme.remappedURLs.entrySet().iterator().next()) .isEqualTo(new SimpleEntry<>(customSchemeURL, "file:" + filePath)); From 1e02989422144722f37e7e5c96817d5e7f6e9e26 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 28 Nov 2019 10:33:31 -0700 Subject: [PATCH 0527/1778] Simplify URL and URI handling --- .../java/io/github/classgraph/Scanner.java | 22 +++++-------- .../FelixClassLoaderHandler.java | 9 +++-- .../classgraph/classpath/ClasspathOrder.java | 33 +++++++++++++------ 3 files changed, 35 insertions(+), 29 deletions(-) diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 259f30d08..87a6e8075 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -32,8 +32,6 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.net.MalformedURLException; -import java.net.URI; -import java.net.URISyntaxException; import java.net.URL; import java.net.URLDecoder; import java.util.AbstractMap.SimpleEntry; @@ -414,27 +412,23 @@ public ClasspathElement newInstance(final ClasspathElementAndClassLoader classpa final LogNode log) throws IOException, InterruptedException { Object classpathEntryObj = classpathEntry.classpathElement; String classpathEntryPath; - if (classpathEntryObj instanceof URL || classpathEntryObj instanceof URI) { - String scheme = classpathEntryObj instanceof URL ? ((URL) classpathEntryObj).getProtocol() - : ((URI) classpathEntryObj).getScheme(); + if (classpathEntryObj instanceof URL) { + final URL classpathEntryURL = (URL) classpathEntryObj; + String scheme = classpathEntryURL.getProtocol(); if ("jar".equals(scheme)) { // Strip off "jar:" scheme prefix try { - classpathEntryObj = classpathEntryObj instanceof URL - ? new URL(((URL) classpathEntryObj).toString().substring(4)) - : new URI(((URI) classpathEntryObj).toString().substring(4)); - scheme = classpathEntryObj instanceof URL ? ((URL) classpathEntryObj).getProtocol() - : ((URI) classpathEntryObj).getScheme(); - } catch (MalformedURLException | URISyntaxException e) { + classpathEntryObj = new URL( + URLDecoder.decode(classpathEntryURL.toString(), "UTF-8").substring(4)); + scheme = classpathEntryURL.getProtocol(); + } catch (MalformedURLException e) { throw new IOException("Could not strip 'jar:' prefix from " + classpathEntryObj, e); } } if ("file".equals(scheme)) { // Extract file path, and use below as a path string to determine if this // classpath element is a file (jar) or directory - classpathEntryPath = URLDecoder.decode(classpathEntryObj instanceof URL - ? ((URL) classpathEntryObj).getPath() - : ((URI) classpathEntryObj).getPath(), "UTF-8"); + classpathEntryPath = URLDecoder.decode(classpathEntryURL.getPath(), "UTF-8"); } else if ("http".equals(scheme) || "https".equals(scheme)) { // Jar URL or URI (remote URLs/URIs must be jars) return new ClasspathElementZip(classpathEntryObj, classpathEntry.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 88e325fe7..e5ff8f5fd 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FelixClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/FelixClassLoaderHandler.java @@ -91,9 +91,8 @@ public static void findClassLoaderOrder(final ClassLoader classLoader, final Cla * the content object * @return the content location */ - private static String getContentLocation(final Object content) { - final File file = (File) ReflectionUtils.invokeMethod(content, "getFile", false); - return file != null ? file.toURI().toString() : null; + private static File getContentLocation(final Object content) { + return (File) ReflectionUtils.invokeMethod(content, "getFile", false); } /** @@ -122,7 +121,7 @@ private static void addBundle(final Object bundleWiring, final ClassLoader class final Object revision = ReflectionUtils.invokeMethod(bundleWiring, "getRevision", false); // Get the contents final Object content = ReflectionUtils.invokeMethod(revision, "getContent", false); - final String location = content != null ? getContentLocation(content) : null; + final File location = content != null ? getContentLocation(content) : null; if (location != null) { // Add the bundle object classpathOrderOut.addClasspathEntry(location, classLoader, scanSpec, log); @@ -133,7 +132,7 @@ private static void addBundle(final Object bundleWiring, final ClassLoader class if (embeddedContent != null) { for (final Object embedded : embeddedContent) { if (embedded != content) { - final String embeddedLocation = embedded != null ? getContentLocation(embedded) : null; + final File embeddedLocation = embedded != null ? getContentLocation(embedded) : null; if (embeddedLocation != null) { classpathOrderOut.addClasspathEntry(embeddedLocation, classLoader, scanSpec, log); } 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 9adc82b45..95472b8ed 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java @@ -30,6 +30,7 @@ import java.io.File; import java.lang.reflect.Array; +import java.net.MalformedURLException; import java.net.URI; import java.net.URL; import java.util.ArrayList; @@ -52,14 +53,14 @@ public class ClasspathOrder { /** Unique classpath entries. */ private final Set classpathEntryUniqueResolvedPaths = new HashSet<>(); - /** The classpath order. Keys are instances of {@link String}, {@link URL} or {@link URI}. */ + /** The classpath order. Keys are instances of {@link String} or {@link URL}. */ private final List order = new ArrayList<>(); /** * A classpath element and the {@link ClassLoader} it was obtained from. */ public static class ClasspathElementAndClassLoader { - /** The classpath element (a {@link String}, {@link URL} or {@link URI}). */ + /** The classpath element (a {@link String} or {@link URL}). */ public final Object classpathElement; /** The classloader the classpath element was obtained from. */ @@ -69,7 +70,7 @@ public static class ClasspathElementAndClassLoader { * Constructor. * * @param classpathElement - * the classpath element (a {@link String}, {@link URL} or {@link URI}). + * the classpath element (a {@link String} or {@link URL}). * @param classLoader * the classloader the classpath element was obtained from. */ @@ -207,14 +208,26 @@ public boolean addClasspathEntry(final Object pathElement, final ClassLoader cla if (pathElementStr.isEmpty()) { return false; } - if (pathElement instanceof URL || pathElement instanceof URI) { + if (pathElement instanceof URL || pathElement instanceof URI || pathElement instanceof File) { if (!filter(pathElementStr)) { if (log != null) { log.log("Classpath element did not match filter criterion, skipping: " + pathElementStr); } return false; } - if (addClasspathEntry(pathElement, pathElementStr, classLoader, scanSpec)) { + // For URL objects, use the object itself (so that URL scheme handling can be undertaken later); + // for URI objects, convert to URL; for File objects, use the toString result (the path) + final Object classpathElementObj; + try { + classpathElementObj = pathElement instanceof File ? pathElementStr + : pathElement instanceof URI ? ((URI) pathElement).toURL() : pathElement; + } catch (MalformedURLException e) { + if (log != null) { + log.log("Cannot convert from URI to URL: " + pathElementStr); + } + return false; + } + if (addClasspathEntry(classpathElementObj, pathElementStr, classLoader, scanSpec)) { if (log != null) { log.log("Found classpath element: " + pathElementStr); } @@ -340,7 +353,7 @@ public boolean addClasspathEntry(final Object pathElement, final ClassLoader cla * Add classpath entries, separated by the system path separator character. * * @param overrideClasspath - * the delimited {@link String}, {@link URL} or {@link URI} of classpath elements. + * a list of delimited path {@link String}, {@link URL}, {@link URI} or {@link File} objects. * @param classLoader * the ClassLoader that this classpath was obtained from. * @param scanSpec @@ -393,10 +406,10 @@ public boolean addClasspathPathStr(final String pathStr, final ClassLoader class /** * Add classpath entries from an object obtained from reflection. The object may be a {@link URL}, a - * {@link URI}, a {@link String} (containing a single path, or several paths separated with File.pathSeparator), - * a List or other Iterable, or an array object. In the case of Iterables and arrays, the elements may be any - * type whose {@code toString()} method returns a path or URL string (including the {@code URL} and {@code Path} - * types). + * {@link URI}, a {@link File} or a {@link String} (containing a single classpath element path, or several paths + * separated with File.pathSeparator), a List or other Iterable, or an array object. In the case of Iterables + * and arrays, the elements may be any type whose {@code toString()} method returns a path or URL string + * (including the {@code URL} and {@code Path} types). * * @param pathObject * the object containing a classpath string or strings. From 56b7fd18920e38e239e86a327366eae5a66e55e5 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 28 Nov 2019 10:37:28 -0700 Subject: [PATCH 0528/1778] Refactoring --- src/main/java/io/github/classgraph/Scanner.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 87a6e8075..0f77147c1 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -431,11 +431,11 @@ public ClasspathElement newInstance(final ClasspathElementAndClassLoader classpa classpathEntryPath = URLDecoder.decode(classpathEntryURL.getPath(), "UTF-8"); } else if ("http".equals(scheme) || "https".equals(scheme)) { // Jar URL or URI (remote URLs/URIs must be jars) - return new ClasspathElementZip(classpathEntryObj, classpathEntry.classLoader, + return new ClasspathElementZip(classpathEntryURL, classpathEntry.classLoader, nestedJarHandler, scanSpec); } else { // For custom URL schemes, assume it must be for a jar, not a directory - return new ClasspathElementZip(classpathEntryObj, classpathEntry.classLoader, + return new ClasspathElementZip(classpathEntryURL, classpathEntry.classLoader, nestedJarHandler, scanSpec); } } else { From 034df7eccc5742d8740a847a1a022a24136addd5 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 28 Nov 2019 10:41:22 -0700 Subject: [PATCH 0529/1778] Schemes must be lowercase --- .../java/nonapi/io/github/classgraph/scanspec/ScanSpec.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 2c16749cb..483434c00 100644 --- a/src/main/java/nonapi/io/github/classgraph/scanspec/ScanSpec.java +++ b/src/main/java/nonapi/io/github/classgraph/scanspec/ScanSpec.java @@ -316,7 +316,7 @@ public void enableURLScheme(final String scheme) { if (allowedURLSchemes == null) { allowedURLSchemes = new HashSet<>(); } - allowedURLSchemes.add(scheme); + allowedURLSchemes.add(scheme.toLowerCase()); } /** From 18dd4beac909bdaef2820aa066e284a8cfe0221d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 28 Nov 2019 10:51:24 -0700 Subject: [PATCH 0530/1778] Source > Cleanup --- .../classgraph/issues/issue384/Issue384Test.java | 2 +- .../classgraph/issues/issue387/Issue387Test.java | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/test/java/io/github/classgraph/issues/issue384/Issue384Test.java b/src/test/java/io/github/classgraph/issues/issue384/Issue384Test.java index 00c76f71f..9cfd42c93 100644 --- a/src/test/java/io/github/classgraph/issues/issue384/Issue384Test.java +++ b/src/test/java/io/github/classgraph/issues/issue384/Issue384Test.java @@ -34,12 +34,12 @@ import java.net.URL; import java.util.AbstractMap.SimpleEntry; -import io.github.classgraph.features.CustomURLScheme; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; +import io.github.classgraph.features.CustomURLScheme; /** * Test. diff --git a/src/test/java/io/github/classgraph/issues/issue387/Issue387Test.java b/src/test/java/io/github/classgraph/issues/issue387/Issue387Test.java index c4f5706b3..3c2b294a5 100644 --- a/src/test/java/io/github/classgraph/issues/issue387/Issue387Test.java +++ b/src/test/java/io/github/classgraph/issues/issue387/Issue387Test.java @@ -30,15 +30,17 @@ import static org.assertj.core.api.Assertions.assertThat; -import java.net.*; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; import java.util.AbstractMap.SimpleEntry; -import io.github.classgraph.features.CustomURLScheme; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; +import io.github.classgraph.features.CustomURLScheme; /** * Test. @@ -58,10 +60,8 @@ void issue387Test() throws MalformedURLException { final String customSchemeURL = CustomURLScheme.SCHEME + ":" + filePath; final URL url = new URL(customSchemeURL); final URLClassLoader classLoader = new URLClassLoader(new URL[] { url }, null); - try (ScanResult scanResult = new ClassGraph() - .enableURLScheme(CustomURLScheme.SCHEME) - .overrideClassLoaders(classLoader) - .scan()) { + try (ScanResult scanResult = new ClassGraph().enableURLScheme(CustomURLScheme.SCHEME) + .overrideClassLoaders(classLoader).scan()) { assertThat(scanResult.getAllResources().getPaths()).containsExactly("level2.jar"); assertThat(CustomURLScheme.remappedURLs.entrySet().iterator().next()) .isEqualTo(new SimpleEntry<>(customSchemeURL, "file:" + filePath)); From a5414a216e9d0a5d733c157da456bd8c71a1ed82 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 28 Nov 2019 11:19:09 -0700 Subject: [PATCH 0531/1778] Make smart path splitting support custom URL schemes --- .../java/io/github/classgraph/ClassGraph.java | 2 +- .../io/github/classgraph/ModulePathInfo.java | 3 +- .../java/io/github/classgraph/Scanner.java | 14 ++++---- .../classgraph/classpath/ClasspathFinder.java | 3 +- .../classgraph/classpath/ClasspathOrder.java | 4 +-- .../classgraph/classpath/SystemJarFinder.java | 2 +- .../io/github/classgraph/utils/JarUtils.java | 32 ++++++++++++++++--- 7 files changed, 42 insertions(+), 18 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index b0365d36c..4fe18c03b 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -410,7 +410,7 @@ public ClassGraph overrideClasspath(final String overrideClasspath) { if (overrideClasspath.isEmpty()) { throw new IllegalArgumentException("Can't override classpath with an empty path"); } - for (final String classpathElement : JarUtils.smartPathSplit(overrideClasspath)) { + for (final String classpathElement : JarUtils.smartPathSplit(overrideClasspath, scanSpec)) { scanSpec.addClasspathOverride(classpathElement); } return this; diff --git a/src/main/java/io/github/classgraph/ModulePathInfo.java b/src/main/java/io/github/classgraph/ModulePathInfo.java index d5d1192f1..436be5e47 100644 --- a/src/main/java/io/github/classgraph/ModulePathInfo.java +++ b/src/main/java/io/github/classgraph/ModulePathInfo.java @@ -141,7 +141,8 @@ public ModulePathInfo() { argField.add(argParam); } else { // Split arg param into parts - for (final String argPart : JarUtils.smartPathSplit(argParam, sepChar)) { + for (final String argPart : JarUtils.smartPathSplit(argParam, sepChar, + /* scanSpec = */ null)) { argField.add(argPart); } } diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 0f77147c1..f5acfff04 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -413,15 +413,15 @@ public ClasspathElement newInstance(final ClasspathElementAndClassLoader classpa Object classpathEntryObj = classpathEntry.classpathElement; String classpathEntryPath; if (classpathEntryObj instanceof URL) { - final URL classpathEntryURL = (URL) classpathEntryObj; + URL classpathEntryURL = (URL) classpathEntryObj; String scheme = classpathEntryURL.getProtocol(); if ("jar".equals(scheme)) { // Strip off "jar:" scheme prefix try { - classpathEntryObj = new URL( + classpathEntryURL = new URL( URLDecoder.decode(classpathEntryURL.toString(), "UTF-8").substring(4)); scheme = classpathEntryURL.getProtocol(); - } catch (MalformedURLException e) { + } catch (final MalformedURLException e) { throw new IOException("Could not strip 'jar:' prefix from " + classpathEntryObj, e); } } @@ -790,14 +790,14 @@ private void preprocessClasspathElementsByType(final List fina // A / pair in the value of an Add-Opens attribute has the same // meaning as the command-line option --add-opens /=ALL-UNNAMED." if (classpathEltZip.logicalZipFile.addExportsManifestEntryValue != null) { - for (final String addExports : JarUtils - .smartPathSplit(classpathEltZip.logicalZipFile.addExportsManifestEntryValue, ' ')) { + for (final String addExports : JarUtils.smartPathSplit( + classpathEltZip.logicalZipFile.addExportsManifestEntryValue, ' ', scanSpec)) { scanSpec.modulePathInfo.addExports.add(addExports + "=ALL-UNNAMED"); } } if (classpathEltZip.logicalZipFile.addOpensManifestEntryValue != null) { - for (final String addOpens : JarUtils - .smartPathSplit(classpathEltZip.logicalZipFile.addOpensManifestEntryValue, ' ')) { + for (final String addOpens : JarUtils.smartPathSplit( + classpathEltZip.logicalZipFile.addOpensManifestEntryValue, ' ', scanSpec)) { scanSpec.modulePathInfo.addOpens.add(addOpens + "=ALL-UNNAMED"); } } 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 9b9af717c..f8c2f1cf0 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java @@ -217,7 +217,8 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { // parent classloader and not in a child classloader (and don't use java.class.path at all if // overrideClassLoaders is true or overrideClasspath is set) if (scanSpec.overrideClassLoaders == null && scanSpec.overrideClasspath == null) { - final String[] pathElements = JarUtils.smartPathSplit(System.getProperty("java.class.path")); + final String[] pathElements = JarUtils.smartPathSplit(System.getProperty("java.class.path"), + scanSpec); if (pathElements.length > 0) { final LogNode sysPropLog = classpathFinderLog == null ? null : classpathFinderLog.log("Getting classpath entries from java.class.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 95472b8ed..8d5aa848a 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java @@ -221,7 +221,7 @@ public boolean addClasspathEntry(final Object pathElement, final ClassLoader cla try { classpathElementObj = pathElement instanceof File ? pathElementStr : pathElement instanceof URI ? ((URI) pathElement).toURL() : pathElement; - } catch (MalformedURLException e) { + } catch (final MalformedURLException e) { if (log != null) { log.log("Cannot convert from URI to URL: " + pathElementStr); } @@ -392,7 +392,7 @@ public boolean addClasspathPathStr(final String pathStr, final ClassLoader class if (pathStr == null || pathStr.isEmpty()) { return false; } else { - final String[] parts = JarUtils.smartPathSplit(pathStr); + final String[] parts = JarUtils.smartPathSplit(pathStr, scanSpec); if (parts.length == 0) { return false; } else { 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 0f44cb106..be2116d8e 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/SystemJarFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/SystemJarFinder.java @@ -124,7 +124,7 @@ private static boolean addJREPath(final File dir) { } final String javaExtDirs = VersionFinder.getProperty("java.ext.dirs"); if (javaExtDirs != null && !javaExtDirs.isEmpty()) { - for (final String javaExtDir : JarUtils.smartPathSplit(javaExtDirs)) { + for (final String javaExtDir : JarUtils.smartPathSplit(javaExtDirs, /* scanSpec = */ null)) { if (!javaExtDir.isEmpty()) { addJREPath(new File(javaExtDir)); } 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 2da19d120..99fd5ba7e 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/JarUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/JarUtils.java @@ -38,6 +38,7 @@ import io.github.classgraph.ClassGraphException; import nonapi.io.github.classgraph.fastzipfilereader.NestedJarHandler; +import nonapi.io.github.classgraph.scanspec.ScanSpec; /** * Jarfile utilities. @@ -102,26 +103,30 @@ private JarUtils() { /** * Split a path on File.pathSeparator (':' on Linux, ';' on Windows), but also allow for the use of URLs with * protocol specifiers, e.g. "http://domain/jar1.jar:http://domain/jar2.jar". - * + * * @param pathStr * The path to split. + * @param scanSpec + * the scan spec * @return The path element substrings. */ - public static String[] smartPathSplit(final String pathStr) { - return smartPathSplit(pathStr, File.pathSeparatorChar); + public static String[] smartPathSplit(final String pathStr, final ScanSpec scanSpec) { + return smartPathSplit(pathStr, File.pathSeparatorChar, scanSpec); } /** * Split a path on the given separator char. If the separator char is ':', also allow for the use of URLs with * protocol specifiers, e.g. "http://domain/jar1.jar:http://domain/jar2.jar". - * + * * @param pathStr * The path to split. * @param separatorChar * The separator char to use. + * @param scanSpec + * the scan spec * @return The path element substrings. */ - public static String[] smartPathSplit(final String pathStr, final char separatorChar) { + public static String[] smartPathSplit(final String pathStr, final char separatorChar, final ScanSpec scanSpec) { if (pathStr == null || pathStr.isEmpty()) { return new String[0]; } @@ -153,6 +158,23 @@ public static String[] smartPathSplit(final String pathStr, final char separator break; } } + if (!foundNonPathSeparator && scanSpec != null && scanSpec.allowedURLSchemes != null + && !scanSpec.allowedURLSchemes.isEmpty()) { + // If custom URL schemes have been registered, allow those to be used as delimiters too + for (final String scheme : scanSpec.allowedURLSchemes) { + // Skip schemes already handled by the faster matching code above + if (!scheme.equals("http") && !scheme.equals("https") && !scheme.equals("jar") + && !scheme.equals("file")) { + final int schemeLen = scheme.length(); + final int startIdx = i - schemeLen; + if (pathStr.regionMatches(true, startIdx, scheme, 0, schemeLen) + && (startIdx == 0 || pathStr.charAt(startIdx - 1) == ':')) { + foundNonPathSeparator = true; + break; + } + } + } + } if (!foundNonPathSeparator) { // The ':' character is a valid path separator splitPoints.add(i); From 3e3023fb1d424c05de8c6ee9f1cd3f5680c25200 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 28 Nov 2019 12:39:56 -0700 Subject: [PATCH 0532/1778] Performance fix --- .../fastzipfilereader/NestedJarHandler.java | 19 ++++++++++++------- .../classgraph/json/ReferenceEqualityKey.java | 11 ++++++++++- 2 files changed, 22 insertions(+), 8 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 6c07b1456..61041d8ff 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java @@ -64,6 +64,7 @@ import nonapi.io.github.classgraph.concurrency.InterruptionChecker; import nonapi.io.github.classgraph.concurrency.SingletonMap; import nonapi.io.github.classgraph.concurrency.SingletonMap.NullSingletonException; +import nonapi.io.github.classgraph.json.ReferenceEqualityKey; import nonapi.io.github.classgraph.recycler.Recycler; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.FastPathResolver; @@ -388,9 +389,12 @@ public RecyclableInflater newInstance() throws RuntimeException { } }; - /** {@link MappedByteBuffer} instances that are currently mapped. */ - private final Set mappedByteBuffers = Collections - .newSetFromMap(new ConcurrentHashMap()); + /** + * {@link MappedByteBuffer} instances that are currently mapped. (Use {@link ReferenceEqualityKey} so that the + * entire contents of the buffers are not compared by {@link ByteBuffer#equals(Object)}). + */ + private final Set> mappedByteBuffers = Collections + .newSetFromMap(new ConcurrentHashMap, Boolean>()); /** {@link MappedByteBufferResources} instances that were allocated for downloading jars from URLs. */ private final Set mappedByteBufferResources = Collections @@ -654,7 +658,7 @@ public MappedByteBuffer mapFileChannelToByteBuffer(final FileChannel fileChannel } // If succeeded, add the ByteBuffer to the set of opened ByteBuffers so it can be cleanly closed - mappedByteBuffers.add(byteBuffer); + mappedByteBuffers.add(new ReferenceEqualityKey(byteBuffer)); return byteBuffer; } @@ -667,7 +671,7 @@ public MappedByteBuffer mapFileChannelToByteBuffer(final FileChannel fileChannel * the log. */ public void unmapByteBuffer(final ByteBuffer byteBuffer, final LogNode log) { - if (mappedByteBuffers.remove(byteBuffer)) { + if (mappedByteBuffers.remove(new ReferenceEqualityKey(byteBuffer))) { FileUtils.closeDirectByteBuffer(byteBuffer, log); } } @@ -867,8 +871,9 @@ public void close(final LogNode log) { } } if (mappedByteBuffers != null) { - for (final ByteBuffer byteBuffer : new ArrayList<>(mappedByteBuffers)) { - unmapByteBuffer(byteBuffer, log); + for (final ReferenceEqualityKey byteBufferRef : new ArrayList<>( + mappedByteBuffers)) { + unmapByteBuffer(byteBufferRef.get(), log); } } } diff --git a/src/main/java/nonapi/io/github/classgraph/json/ReferenceEqualityKey.java b/src/main/java/nonapi/io/github/classgraph/json/ReferenceEqualityKey.java index b0901154f..3d088cbed 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/ReferenceEqualityKey.java +++ b/src/main/java/nonapi/io/github/classgraph/json/ReferenceEqualityKey.java @@ -35,7 +35,7 @@ * @param * the key type */ -class ReferenceEqualityKey { +public class ReferenceEqualityKey { /** The wrapped key. */ private final K wrappedKey; @@ -50,6 +50,15 @@ public ReferenceEqualityKey(final K wrappedKey) { this.wrappedKey = wrappedKey; } + /** + * Get the wrapped key. + * + * @return the wrapped key. + */ + public K get() { + return wrappedKey; + } + /* (non-Javadoc) * @see java.lang.Object#hashCode() */ From 2b1723d0922a3b6a08c2ac0238f494814a15687c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 28 Nov 2019 13:49:55 -0700 Subject: [PATCH 0533/1778] Source > Cleanup --- 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 f5acfff04..3708e7599 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -410,7 +410,7 @@ public ClasspathEntryWorkUnit(final ClasspathElementAndClassLoader rawClasspathE @Override public ClasspathElement newInstance(final ClasspathElementAndClassLoader classpathEntry, final LogNode log) throws IOException, InterruptedException { - Object classpathEntryObj = classpathEntry.classpathElement; + final Object classpathEntryObj = classpathEntry.classpathElement; String classpathEntryPath; if (classpathEntryObj instanceof URL) { URL classpathEntryURL = (URL) classpathEntryObj; From 93016fa8532245c7876efef0e1b736759a366312 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 28 Nov 2019 13:50:29 -0700 Subject: [PATCH 0534/1778] Improve error reporting --- .../java/nonapi/io/github/classgraph/utils/FileUtils.java | 6 ++++-- 1 file changed, 4 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 7873e74ac..c83ebb780 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -568,7 +568,8 @@ private static void lookupCleanMethodPrivileged() { } catch (final SecurityException e) { throw ClassGraphException.newClassGraphException( "You need to grant classgraph RuntimePermission(\"accessClassInPackage.sun.misc\") " - + "and ReflectPermission(\"suppressAccessChecks\")"); + + "and ReflectPermission(\"suppressAccessChecks\")", + e); } catch (final ReflectiveOperationException | LinkageError ex) { // Ignore } @@ -591,7 +592,8 @@ private static void lookupCleanMethodPrivileged() { throw ClassGraphException.newClassGraphException( "You need to grant classgraph RuntimePermission(\"accessClassInPackage.sun.misc\"), " + "RuntimePermission(\"accessClassInPackage.jdk.internal.misc\") " - + "and ReflectPermission(\"suppressAccessChecks\")"); + + "and ReflectPermission(\"suppressAccessChecks\")", + e); } catch (final ReflectiveOperationException | LinkageError ex) { // Ignore } From 0e55f0b2228527fab2535946df617d491f5235c2 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 28 Nov 2019 13:50:37 -0700 Subject: [PATCH 0535/1778] Fix performance regression --- .../io/github/classgraph/json/ReferenceEqualityKey.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/json/ReferenceEqualityKey.java b/src/main/java/nonapi/io/github/classgraph/json/ReferenceEqualityKey.java index 3d088cbed..b5e12a43c 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/ReferenceEqualityKey.java +++ b/src/main/java/nonapi/io/github/classgraph/json/ReferenceEqualityKey.java @@ -51,7 +51,7 @@ public ReferenceEqualityKey(final K wrappedKey) { } /** - * Get the wrapped key. + * Get the wrapped key. * * @return the wrapped key. */ @@ -65,7 +65,9 @@ public K get() { @Override public int hashCode() { final K key = wrappedKey; - return key == null ? 0 : key.hashCode(); + // Don't call key.hashCode(), because that can be an expensive (deep) hashing method, + // e.g. for ByteBuffer, it is based on the entire contents of the buffer + return key == null ? 0 : System.identityHashCode(key); } /* (non-Javadoc) From 880ded2998afd0dd5370227e542d46cc3c33b0d0 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 28 Nov 2019 13:52:52 -0700 Subject: [PATCH 0536/1778] [maven-release-plugin] prepare release classgraph-4.8.57 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index cadefcfb3..2c5fd85ec 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.57-SNAPSHOT + 4.8.57 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.56 + classgraph-4.8.57 From 441aee5a68315350cc4a907b1a4f40a86bc5a3bf Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 28 Nov 2019 13:52:59 -0700 Subject: [PATCH 0537/1778] [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 2c5fd85ec..d5f1fa05a 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.57 + 4.8.58-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.57 + classgraph-4.8.56 From 182e728af1f14193fcebca20b07d2dce5c1759c3 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 28 Nov 2019 14:12:02 -0700 Subject: [PATCH 0538/1778] Run CI tests on Windows and MacOS too --- .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 2a7e9a339..1ee6863d9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,7 +10,7 @@ on: jobs: build: - runs-on: ubuntu-latest + runs-on: [ ubuntu-latest, windows-latest, macOS-latest ] strategy: matrix: java: [ '1.8.0', '11.0.x', '12.0.x', '13.0.x' ] From 6973c9e3d148aeb03cb130c1e970177afbbef7bb Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 28 Nov 2019 14:13:55 -0700 Subject: [PATCH 0539/1778] Correct case --- .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 1ee6863d9..7a0f83913 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,7 +10,7 @@ on: jobs: build: - runs-on: [ ubuntu-latest, windows-latest, macOS-latest ] + runs-on: [ ubuntu-latest, windows-latest, macos-latest ] strategy: matrix: java: [ '1.8.0', '11.0.x', '12.0.x', '13.0.x' ] From 8dcb9a53235d958be19dac2f200aceaef28cee81 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 28 Nov 2019 14:16:32 -0700 Subject: [PATCH 0540/1778] Fix syntax --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7a0f83913..2b2d29ca2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,9 +10,10 @@ on: jobs: build: - runs-on: [ ubuntu-latest, windows-latest, macos-latest ] + runs-on: {{ matrix.os }} strategy: matrix: + os: [ ubuntu-latest, windows-latest, macos-latest ] java: [ '1.8.0', '11.0.x', '12.0.x', '13.0.x' ] steps: - uses: actions/checkout@v1 From e2632bd562bf3fb33ea1d6a6f14a9aa280d88234 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 28 Nov 2019 14:17:30 -0700 Subject: [PATCH 0541/1778] Fix syntax --- .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 2b2d29ca2..1a7b39fc6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,7 +10,7 @@ on: jobs: build: - runs-on: {{ matrix.os }} + runs-on: ${{ matrix.os }} strategy: matrix: os: [ ubuntu-latest, windows-latest, macos-latest ] From 82e59011e6a8e1fcb326e03976ca8503011685a3 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 28 Nov 2019 14:22:02 -0700 Subject: [PATCH 0542/1778] Try manually enabling tests --- .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 1a7b39fc6..4773738d4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,4 +24,4 @@ jobs: - name: print Java version run: java -version - name: Build with Maven - run: ./mvnw clean test package --file pom.xml + run: ./mvnw -Dmaven.test.skip=false clean package --file pom.xml From 734a252f6b522ce34cec38faab75c7a1e734a2d8 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 28 Nov 2019 14:26:07 -0700 Subject: [PATCH 0543/1778] Try enabling unit tests in CI --- .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 4773738d4..401b76210 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,4 +24,4 @@ jobs: - name: print Java version run: java -version - name: Build with Maven - run: ./mvnw -Dmaven.test.skip=false clean package --file pom.xml + run: ./mvnw clean test From 9de9e5fa941e3d6651229c01b240adb1676fe9eb Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 28 Nov 2019 14:28:10 -0700 Subject: [PATCH 0544/1778] Add "package" target --- .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 401b76210..cb877773b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,4 +24,4 @@ jobs: - name: print Java version run: java -version - name: Build with Maven - run: ./mvnw clean test + run: ./mvnw clean test package From 86a2ff4824206fe71c3b4876acb3ce0188f9623a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 28 Nov 2019 14:31:12 -0700 Subject: [PATCH 0545/1778] Remove "package" again (only one target supported?) --- .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 cb877773b..401b76210 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,4 +24,4 @@ jobs: - name: print Java version run: java -version - name: Build with Maven - run: ./mvnw clean test package + run: ./mvnw clean test From c3c0cf761cd0ac225880a8cd8178e0cbcb5d14c9 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 29 Nov 2019 01:37:04 -0700 Subject: [PATCH 0546/1778] Make method private --- .../github/classgraph/fastzipfilereader/NestedJarHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 61041d8ff..59b61ef7d 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java @@ -712,7 +712,7 @@ private String sanitizeFilename(final String filename) { * @throws IOException * If the temporary file could not be created. */ - public File makeTempFile(final String filePath, final boolean onlyUseLeafname) throws IOException { + private File makeTempFile(final String filePath, final boolean onlyUseLeafname) throws IOException { final File tempFile = File.createTempFile("ClassGraph--", TEMP_FILENAME_LEAF_SEPARATOR + sanitizeFilename(onlyUseLeafname ? leafname(filePath) : filePath)); tempFile.deleteOnExit(); From 2e8199014b279fed4097a3fa1a302c9fc3eabd2c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 29 Nov 2019 03:25:34 -0700 Subject: [PATCH 0547/1778] Refactoring --- .../classgraph/ClasspathElementDir.java | 20 +- .../fastzipfilereader/FastZipEntry.java | 4 +- .../MappedByteBufferResources.java | 371 ++++++++++++++++++ .../fastzipfilereader/NestedJarHandler.java | 270 +------------ .../fastzipfilereader/PhysicalZipFile.java | 177 +++------ .../fastzipfilereader/ZipFileSlice.java | 14 +- .../fastzipfilereader/ZipFileSliceReader.java | 7 +- .../io/github/classgraph/utils/FileUtils.java | 2 +- 8 files changed, 478 insertions(+), 387 deletions(-) create mode 100644 src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java diff --git a/src/main/java/io/github/classgraph/ClasspathElementDir.java b/src/main/java/io/github/classgraph/ClasspathElementDir.java index f4a02b317..cea8ee364 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementDir.java @@ -45,8 +45,8 @@ import nonapi.io.github.classgraph.classpath.ClasspathOrder.ClasspathElementAndClassLoader; import nonapi.io.github.classgraph.concurrency.WorkQueue; import nonapi.io.github.classgraph.fastzipfilereader.LogicalZipFile; +import nonapi.io.github.classgraph.fastzipfilereader.MappedByteBufferResources; import nonapi.io.github.classgraph.fastzipfilereader.NestedJarHandler; -import nonapi.io.github.classgraph.fastzipfilereader.NestedJarHandler.MappedByteBufferResources; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.scanspec.ScanSpec.ScanSpecPathMatch; import nonapi.io.github.classgraph.utils.FileUtils; @@ -205,10 +205,18 @@ public synchronized ByteBuffer read() throws IOException { markAsOpen(); try { mappedFileResources = new MappedByteBufferResources(classpathResourceFile, nestedJarHandler); - byteBuffer = mappedFileResources.getByteBuffer(); + if (mappedFileResources.numChunks() > 1) { + // We could provide another method that fetches a chunk other than chunk 0, but the need + // to read files larger than 2GB is probably limited (it's not even supported for zipfiles), + // and the caller can use an InputStream if necessary. + throw new IOException( + "File is larger than 2GB, cannot use read() method, use open() instead"); + } + // Fetch chunk 0 (the first ~2GB of the file) + byteBuffer = mappedFileResources.getByteBuffer(0); length = byteBuffer.remaining(); return byteBuffer; - } catch (final IOException | SecurityException | OutOfMemoryError e) { + } catch (final IOException | SecurityException | OutOfMemoryError | InterruptedException e) { close(); throw new IOException("Could not open " + this, e); } @@ -226,7 +234,7 @@ synchronized InputStreamOrByteBufferAdapter openOrRead() throws IOException { @Override public synchronized InputStream open() throws IOException { - if (length >= FileUtils.FILECHANNEL_FILE_SIZE_THRESHOLD) { + if (length >= FileUtils.FILECHANNEL_FILE_SIZE_THRESHOLD && length <= FileUtils.MAX_BUFFER_SIZE) { read(); return inputStream = new InputStreamResourceCloser(this, byteBufferToInputStream()); } else { @@ -245,7 +253,9 @@ public synchronized InputStream open() throws IOException { public synchronized byte[] load() throws IOException { try { final byte[] byteArray; - if (length >= FileUtils.FILECHANNEL_FILE_SIZE_THRESHOLD) { + if (length > FileUtils.MAX_BUFFER_SIZE) { + throw new IOException("File is larger than 2GB, cannot read into array"); + } else if (length >= FileUtils.FILECHANNEL_FILE_SIZE_THRESHOLD) { read(); byteArray = byteBufferToByteArray(); } else { 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 8911ef9e9..8730de4f6 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/FastZipEntry.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/FastZipEntry.java @@ -346,7 +346,7 @@ public InputStream open() throws IOException, InterruptedException { /** Advance to the next 2GB chunk. */ private boolean readNextChunk() throws IOException, InterruptedException { currChunkIdx++; - if (currChunkIdx >= parentLogicalZipFile.physicalZipFile.numMappedByteBuffers) { + if (currChunkIdx >= parentLogicalZipFile.physicalZipFile.numChunks()) { // Ran out of chunks return false; } @@ -532,7 +532,7 @@ public int read(final byte[] buf, final int off, final int len) throws IOExcepti throw new IndexOutOfBoundsException(); } else if (len == 0) { return 0; - } else if (parentLogicalZipFile.physicalZipFile.fileLen == 0) { + } else if (parentLogicalZipFile.physicalZipFile.length() == 0) { return -1; } try { diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java new file mode 100644 index 000000000..238c6fd6e --- /dev/null +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.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 nonapi.io.github.classgraph.fastzipfilereader; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.RandomAccessFile; +import java.net.URL; +import java.nio.ByteBuffer; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.file.Files; +import java.nio.file.StandardOpenOption; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReferenceArray; + +import nonapi.io.github.classgraph.concurrency.SingletonMap; +import nonapi.io.github.classgraph.concurrency.SingletonMap.NullSingletonException; +import nonapi.io.github.classgraph.utils.FileUtils; +import nonapi.io.github.classgraph.utils.LogNode; + +/** Resources for a mapped file. */ +public class MappedByteBufferResources { + /** If true, a file was mapped from a {@link FileChannel}. */ + private File mappedFile; + + /** If true, the mapped file was created as a temp file when the InputStream wouldn't fit in RAM. */ + private boolean mappedFileIsTempFile; + + /** The raf. */ + private RandomAccessFile raf; + + /** The file channel. */ + private FileChannel fileChannel; + + /** The total length. */ + private long length; + + /** The cached mapped byte buffers for each 2GB chunk. */ + private AtomicReferenceArray byteBufferChunksCached; + + /** A singleton map from chunk index to byte buffer, ensuring that any given chunk is only mapped once. */ + private SingletonMap chunkIdxToByteBufferSingletonMap; + + /** The nested jar handler. */ + private final NestedJarHandler nestedJarHandler; + + /** Set to true once this {@link PhysicalZipFile} is closed. */ + private final AtomicBoolean closed = new AtomicBoolean(false); + + /** + * The maximum size of a jar that is downloaded from a {@link URL}'s {@link InputStream} to RAM, before the + * content is spilled over to a temporary file on disk. + */ + private static final int MAX_JAR_RAM_SIZE = 64 * 1024 * 1024; + + /** + * 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}. + * @param tempFileBaseName + * the source URL or zip entry that inputStream was opened from (used to name temporary file, if + * needed). + * @param nestedJarHandler + * the nested jar handler + * @param log + * the log. + * @throws IOException + * If the contents could not be read. + */ + public MappedByteBufferResources(final InputStream inputStream, final String tempFileBaseName, + final NestedJarHandler nestedJarHandler, final LogNode log) throws IOException { + this.nestedJarHandler = nestedJarHandler; + final byte[] buf = new byte[MAX_JAR_RAM_SIZE]; + final int bufLength = buf.length; + + int totBytesRead = 0; + int bytesRead = 0; + while ((bytesRead = inputStream.read(buf, totBytesRead, bufLength - totBytesRead)) > 0) { + // Fill buffer until nothing more can be read + totBytesRead += bytesRead; + } + if (bytesRead < 0) { + // Successfully reached end of stream -- wrap array buffer with ByteBuffer + wrapByteBuffer(ByteBuffer.wrap(buf, 0, totBytesRead)); + + } else { + // bytesRead == 0 => ran out of buffer space, spill over to disk + final File tempFile = nestedJarHandler.makeTempFile(tempFileBaseName, /* onlyUseLeafname = */ true); + mappedFileIsTempFile = true; + if (log != null) { + log.log("Could not fit downloaded URL into max RAM buffer size of " + MAX_JAR_RAM_SIZE + + " bytes, downloading to temporary file: " + tempFileBaseName + " -> " + tempFile); + } + Files.write(tempFile.toPath(), buf, StandardOpenOption.WRITE); + try (OutputStream os = new BufferedOutputStream(new FileOutputStream(tempFile, /* append = */ true))) { + for (int bytesReadCtd; (bytesReadCtd = inputStream.read(buf, 0, buf.length)) > 0;) { + os.write(buf, 0, bytesReadCtd); + } + } + // Map the file to a MappedByteBuffer + mapFile(tempFile); + } + } + + /** + * Wrap an existing ByteBuffer. + * + * @param byteBuffer + * the byte buffer + * @param nestedJarHandler + * the nested jar handler + * @throws IOException + * If the contents could not be read. + */ + public MappedByteBufferResources(final ByteBuffer byteBuffer, final NestedJarHandler nestedJarHandler) + throws IOException { + this.nestedJarHandler = nestedJarHandler; + wrapByteBuffer(byteBuffer); + } + + /** + * Map a {@link File} to a {@link MappedByteBuffer}. + * + * @param file + * the file + * @param nestedJarHandler + * the nested jar handler + * @throws IOException + * If the contents could not be read. + */ + public MappedByteBufferResources(final File file, final NestedJarHandler nestedJarHandler) throws IOException { + this.nestedJarHandler = nestedJarHandler; + mapFile(file); + } + + /** + * Wrap an existing single-chunk {@link ByteBuffer}. + * + * @param byteBuffer + * the {@link ByteBuffer}. + */ + private void wrapByteBuffer(final ByteBuffer byteBuffer) { + length = byteBuffer.remaining(); + + // Put the ByteBuffer into the cache, so that the singleton map code for file mapping is never called + byteBufferChunksCached = new AtomicReferenceArray(1); + byteBufferChunksCached.set(0, byteBuffer); + + // Don't set file, fileChannel or raf, they are unneeded. + // byteBufferIsMapped will remain false. + } + + /** + * Map a {@link File} to a {@link MappedByteBuffer}. + * + * @param file + * the file + * @throws IOException + * Signals that an I/O exception has occurred. + */ + private void mapFile(final File file) throws IOException { + this.mappedFile = file; + try { + raf = new RandomAccessFile(file, "r"); + length = raf.length(); + fileChannel = raf.getChannel(); + + } catch (final IOException | SecurityException e) { + if (fileChannel != null) { + fileChannel.close(); + fileChannel = null; + } + if (raf != null) { + raf.close(); + raf = null; + } + throw e; + } + + // Implement an array of MappedByteBuffers to support jarfiles >2GB in size: + // https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6347833 + final int numByteBufferChunks = (int) ((length + FileUtils.MAX_BUFFER_SIZE) / FileUtils.MAX_BUFFER_SIZE); + byteBufferChunksCached = new AtomicReferenceArray(numByteBufferChunks); + chunkIdxToByteBufferSingletonMap = new SingletonMap() { + @Override + public ByteBuffer newInstance(final Integer chunkIdxI, final LogNode log) throws IOException { + // Map the indexed 2GB chunk of the file to a MappedByteBuffer + final long pos = chunkIdxI.longValue() * FileUtils.MAX_BUFFER_SIZE; + final long chunkSize = Math.min(FileUtils.MAX_BUFFER_SIZE, length - pos); + + if (fileChannel == null) { + // Should not happen + throw new IOException("Cannot map a null FileChannel"); + } + + MappedByteBuffer byteBuffer; + try { + // Try mapping the FileChannel + byteBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, pos, chunkSize); + } catch (final FileNotFoundException e) { + throw e; + } catch (IOException | OutOfMemoryError e) { + // If map failed, try calling System.gc() to free some allocated MappedByteBuffers + // (there is a limit to the number of mapped files -- 64k on Linux) + // See: http://www.mapdb.org/blog/mmap_files_alloc_and_jvm_crash/ + System.gc(); + System.runFinalization(); + // Then try calling map again + byteBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, pos, chunkSize); + } + + // Record that the byte buffer has been mapped + nestedJarHandler.addMappedByteBuffer(byteBuffer); + + return byteBuffer; + } + }; + + } + + /** + * Get a mmap'd chunk of the file, where chunkIdx denotes which 2GB chunk of the file to return (0 for the first + * 2GB of the file, or for files smaller than 2GB; 1 for the 2-4GB chunk, etc.). + * + * @param chunkIdx + * The index of the 2GB chunk to read, between 0 and {@link #numChunks()} - 1. + * @return The {@link MappedByteBuffer} for the requested file chunk, up to 2GB in size. + * @throws IOException + * If the chunk could not be mmap'd. + * @throws InterruptedException + * If the thread was interrupted. + */ + public ByteBuffer getByteBuffer(final int chunkIdx) throws IOException, InterruptedException { + if (closed.get()) { + throw new IOException(getClass().getSimpleName() + " already closed"); + } + if (chunkIdx < 0 || chunkIdx >= numChunks()) { + throw new IOException("Chunk index out of range"); + } + // Fast path: only look up singleton map if mappedByteBuffersCached is null + ByteBuffer cachedBuf = byteBufferChunksCached.get(chunkIdx); + if (cachedBuf == null) { + // This 2GB chunk has not yet been read -- mmap it and cache it. + // (Use a singleton map so that the mmap doesn't happen more than once) + if (chunkIdxToByteBufferSingletonMap == null) { + // Should not happen + throw new IOException("chunkIdxToByteBufferSingletonMap is null"); + } + try { + cachedBuf = chunkIdxToByteBufferSingletonMap.get(chunkIdx, /* log = */ null); + byteBufferChunksCached.set(chunkIdx, cachedBuf); + } catch (final NullSingletonException e) { + throw new IOException("Cannot get ByteBuffer chunk " + chunkIdx + " : " + e); + } + } + return cachedBuf; + } + + /** + * Get the mapped file (or null if an in-memory {@link ByteBuffer} was wrapped instead). + * + * @return the mapped file + */ + public File getMappedFile() { + return mappedFile; + } + + /** + * Get the length of the mapped file, or the initial remaining bytes in the wrapped ByteBuffer if a buffer was + * wrapped. + */ + public long length() { + return length; + } + + /** + * Get the number of 2GB chunks that are available in this mapped file or wrapped ByteBuffer. + */ + public int numChunks() { + return byteBufferChunksCached == null ? 0 : byteBufferChunksCached.length(); + } + + /** + * Free resources. + * + * @param log + * the log + */ + public void close(final LogNode log) { + if (!closed.getAndSet(true)) { + if (chunkIdxToByteBufferSingletonMap != null) { + chunkIdxToByteBufferSingletonMap.clear(); + chunkIdxToByteBufferSingletonMap = null; + } + if (byteBufferChunksCached != null) { + if (mappedFile != null) { + for (int i = 0; i < byteBufferChunksCached.length(); i++) { + final ByteBuffer mappedByteBuffer = byteBufferChunksCached.get(i); + if (mappedByteBuffer != null) { + nestedJarHandler.unmapByteBuffer(mappedByteBuffer, /* log = */ null); + byteBufferChunksCached.set(i, null); + } + } + } + byteBufferChunksCached = null; + } + if (fileChannel != null) { + try { + fileChannel.close(); + fileChannel = null; + } catch (final IOException e) { + // Ignore + } + } + if (raf != null) { + try { + raf.close(); + raf = null; + } catch (final IOException e) { + // Ignore + } + } + if (mappedFile != null) { + if (mappedFileIsTempFile) { + try { + Files.delete(mappedFile.toPath()); + } catch (final IOException e) { + if (log != null) { + log.log("Could not delete temporary file " + mappedFile + " : " + e); + } + } + } + mappedFile = null; + } + } + } +} \ No newline at end of file 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 59b61ef7d..44db0585e 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java @@ -28,13 +28,9 @@ */ package nonapi.io.github.classgraph.fastzipfilereader; -import java.io.BufferedOutputStream; import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; import java.io.RandomAccessFile; import java.net.MalformedURLException; import java.net.URI; @@ -44,7 +40,6 @@ import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; -import java.nio.file.StandardOpenOption; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Collections; @@ -89,7 +84,8 @@ public PhysicalZipFile newInstance(final File canonicalFile, final LogNode log) throw ClassGraphException .newClassGraphException(NestedJarHandler.class.getSimpleName() + " already closed"); } - return new PhysicalZipFile(canonicalFile, NestedJarHandler.this); + final PhysicalZipFile physicalZipFile = new PhysicalZipFile(canonicalFile, NestedJarHandler.this); + return physicalZipFile; } }; @@ -126,33 +122,12 @@ public ZipFileSlice newInstance(final FastZipEntry childZipEntry, final LogNode } // Read the InputStream for the child zip entry to a RAM buffer, or spill to disk if it's too large - final MappedByteBufferResources bufResources = new MappedByteBufferResources(childZipEntry.open(), + final PhysicalZipFile physicalZipFile = new PhysicalZipFile(childZipEntry.open(), childZipEntry.entryName, NestedJarHandler.this, log); - mappedByteBufferResources.add(bufResources); - - PhysicalZipFile physicalZipFile = null; - final File tempFile = bufResources.getFile(); - if (tempFile != null) { - // InputStream would not fit within the RAM limit, so spilled over to disk - // Wrap temp file in a PhysicalZipFile - physicalZipFile = canonicalFileToPhysicalZipFile(tempFile, log); - if (physicalZipFile == null) { - // Should not happen - throw ClassGraphException.newClassGraphException("physicalZipFile should not be null"); - } - // Create a new logical slice of the whole physical zipfile - childZipEntrySlice = new ZipFileSlice(physicalZipFile); - - } else { - // Create a new PhysicalZipFile that wraps the ByteBuffer as if the buffer had been - // mmap'd to a file on disk - physicalZipFile = new PhysicalZipFile(bufResources.getByteBuffer(), - /* outermostFile = */ childZipEntry.parentLogicalZipFile.physicalZipFile.getFile(), - childZipEntry.getPath(), NestedJarHandler.this); - // Create a new logical slice of the in-memory extracted inner zipfile - childZipEntrySlice = new ZipFileSlice(physicalZipFile, childZipEntry); - } allocatedPhysicalZipFiles.add(physicalZipFile); + + // Create a new logical slice of the extracted inner zipfile + childZipEntrySlice = new ZipFileSlice(physicalZipFile, childZipEntry); } return childZipEntrySlice; } @@ -406,12 +381,6 @@ public RecyclableInflater newInstance() throws RuntimeException { /** The separator between random temp filename part and leafname. */ public static final String TEMP_FILENAME_LEAF_SEPARATOR = "---"; - /** - * The maximum size of a jar that is downloaded from a {@link URL}'s {@link InputStream} to RAM, before the - * content is spilled over to a temporary file on disk. - */ - private static final int MAX_JAR_RAM_SIZE = 64 * 1024 * 1024; - /** True if {@link #close(LogNode)} has been called. */ private final AtomicBoolean closed = new AtomicBoolean(false); @@ -460,206 +429,14 @@ private PhysicalZipFile canonicalFileToPhysicalZipFile(final File canonicalFile, // ------------------------------------------------------------------------------------------------------------- - /** Resources for a mapped file. */ - public static class MappedByteBufferResources { - private File file; - private boolean fileIsTempFile; - private RandomAccessFile raf; - private FileChannel fileChannel; - private ByteBuffer byteBuffer; - private boolean byteBufferIsMapped; - private final NestedJarHandler nestedJarHandler; - - /** - * 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}. - * @param tempFileBaseName - * the source URL or zip entry that inputStream was opened from (used to name temporary file, if - * needed). - * @param log - * the log. - * @throws IOException - * If the contents could not be read. - */ - public MappedByteBufferResources(final InputStream inputStream, final String tempFileBaseName, - final NestedJarHandler nestedJarHandler, final LogNode log) throws IOException { - this.nestedJarHandler = nestedJarHandler; - final byte[] buf = new byte[MAX_JAR_RAM_SIZE]; - final int bufLength = buf.length; - - int totBytesRead = 0; - int bytesRead = 0; - while ((bytesRead = inputStream.read(buf, totBytesRead, bufLength - totBytesRead)) > 0) { - // Fill buffer until nothing more can be read - totBytesRead += bytesRead; - } - if (bytesRead < 0) { - // Successfully reached end of stream -- wrap array with ByteBuffer and return it - byteBuffer = ByteBuffer.wrap(buf, 0, totBytesRead); - // Don't set file, fileChannel or raf, they are unneeded. - // byteBufferIsMapped will remain false. - } else { - // bytesRead == 0 => ran out of buffer space, spill over to disk - final File tempFile = nestedJarHandler.makeTempFile(tempFileBaseName, /* onlyUseLeafname = */ true); - fileIsTempFile = true; - if (log != null) { - log.log("Could not fit downloaded URL into max RAM buffer size of " + MAX_JAR_RAM_SIZE - + " bytes, downloading to temporary file: " + tempFileBaseName + " -> " + tempFile); - } - Files.write(tempFile.toPath(), buf, StandardOpenOption.WRITE); - try (OutputStream os = new BufferedOutputStream( - new FileOutputStream(tempFile, /* append = */ true))) { - for (int bytesReadCtd; (bytesReadCtd = inputStream.read(buf, 0, buf.length)) > 0;) { - os.write(buf, 0, bytesReadCtd); - } - } - // Map the file to a MappedByteBuffer - mapFile(tempFile); - } - } - - /** - * Map a {@link File} to a {@link MappedByteBuffer}. - * - * @param file - * the file - * @param nestedJarHandler - * the nested jar handler - * @throws IOException - * If the contents could not be read. - */ - public MappedByteBufferResources(final File file, final NestedJarHandler nestedJarHandler) - throws IOException { - this.nestedJarHandler = nestedJarHandler; - mapFile(file); - } - - /** - * Map a {@link File} to a {@link MappedByteBuffer}. - * - * @param file - * the file - * @throws IOException - * Signals that an I/O exception has occurred. - */ - private void mapFile(final File file) throws IOException { - this.file = file; - try { - raf = new RandomAccessFile(file, "r"); - fileChannel = raf.getChannel(); - byteBuffer = nestedJarHandler.mapFileChannelToByteBuffer(fileChannel, 0L, raf.length()); - byteBufferIsMapped = true; - - } catch (final IOException | SecurityException e) { - if (fileChannel != null) { - fileChannel.close(); - fileChannel = null; - } - if (raf != null) { - raf.close(); - raf = null; - } - throw e; - } - } - - /** - * Get the mapped file (or null if an in-memory {@link ByteBuffer} was wrapped instead). - * - * @return the mapped file - */ - public File getFile() { - return file; - } - - /** - * Get the byte buffer. - * - * @return the byte buffer - */ - public ByteBuffer getByteBuffer() { - return byteBuffer; - } - - /** - * Free resources. - * - * @param log - * the log - */ - public void close(final LogNode log) { - if (byteBufferIsMapped && byteBuffer != null) { - nestedJarHandler.unmapByteBuffer(byteBuffer, log); - byteBuffer = null; - } - if (fileChannel != null) { - try { - fileChannel.close(); - fileChannel = null; - } catch (final IOException e) { - // Ignore - } - } - if (raf != null) { - try { - raf.close(); - raf = null; - } catch (final IOException e) { - // Ignore - } - } - if (file != null && fileIsTempFile) { - try { - Files.delete(file.toPath()); - } catch (final IOException e) { - if (log != null) { - log.log("Could not delete temporary file " + file + " : " + e); - } - } - file = null; - } - } - } - - // ------------------------------------------------------------------------------------------------------------- - /** - * Map a {@link FileChannel} to a {@link MappedByteBuffer}. + * Record that a {@link FileChannel} was mapped to a {@link MappedByteBuffer}. * - * @param fileChannel - * the file channel - * @param position - * the start position to map. - * @param size - * the number of bytes to map. - * @return the {@link MappedByteBuffer}. - * @throws IOException - * If an I/O exception occurs. + * @param byteBuffer + * the byte buffer */ - public MappedByteBuffer mapFileChannelToByteBuffer(final FileChannel fileChannel, final long position, - final long size) throws IOException { - MappedByteBuffer byteBuffer; - try { - // Try mapping the FileChannel - byteBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, position, size); - } catch (final FileNotFoundException e) { - throw e; - } catch (IOException | OutOfMemoryError e) { - // If map failed, try calling System.gc() to free some allocated MappedByteBuffers - // (there is a limit to the number of mapped files -- 64k on Linux) - // See: http://www.mapdb.org/blog/mmap_files_alloc_and_jvm_crash/ - System.gc(); - System.runFinalization(); - // Then try calling map again - byteBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, position, size); - } - - // If succeeded, add the ByteBuffer to the set of opened ByteBuffers so it can be cleanly closed + public void addMappedByteBuffer(final ByteBuffer byteBuffer) { mappedByteBuffers.add(new ReferenceEqualityKey(byteBuffer)); - return byteBuffer; } /** @@ -712,7 +489,7 @@ private String sanitizeFilename(final String filename) { * @throws IOException * If the temporary file could not be created. */ - private File makeTempFile(final String filePath, final boolean onlyUseLeafname) throws IOException { + File makeTempFile(final String filePath, final boolean onlyUseLeafname) throws IOException { final File tempFile = File.createTempFile("ClassGraph--", TEMP_FILENAME_LEAF_SEPARATOR + sanitizeFilename(onlyUseLeafname ? leafname(filePath) : filePath)); tempFile.deleteOnExit(); @@ -750,29 +527,8 @@ private PhysicalZipFile downloadJarFromURL(final String jarURL, final LogNode lo } } try (InputStream inputStream = url.openStream()) { - // Fetch the jar contents from the URL's InputStream. - // If it doesn't fit in RAM, spill over to disk. - final MappedByteBufferResources bufResources = new MappedByteBufferResources(inputStream, jarURL, this, - log); - mappedByteBufferResources.add(bufResources); - - PhysicalZipFile physicalZipFile = null; - final File tempFile = bufResources.getFile(); - if (tempFile != null) { - // InputStream would not fit within the RAM limit, so spilled over to disk - // Wrap temp file in a PhysicalZipFile - physicalZipFile = canonicalFileToPhysicalZipFile(tempFile, log); - if (physicalZipFile == null) { - // Should not happen - throw ClassGraphException.newClassGraphException("physicalZipFile should not be null"); - } - } else { - // Wrap ByteBuffer in a PhysicalZipFile. (No need to add to additionalAllocatedPhysicalZipFiles, - // since byteBuffer is not a DirectByteBuffer, it just wraps a standard Java byte array, so the - // DirectByteBuffer cleaner does not need to be called on close.) - physicalZipFile = new PhysicalZipFile(bufResources.getByteBuffer(), /* outermostFile = */ null, - jarURL, NestedJarHandler.this); - } + // 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, jarURL, this, log); allocatedPhysicalZipFiles.add(physicalZipFile); return physicalZipFile; 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 b39985724..2efe43e01 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/PhysicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/PhysicalZipFile.java @@ -31,15 +31,13 @@ import java.io.Closeable; import java.io.File; import java.io.IOException; -import java.io.RandomAccessFile; +import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; -import nonapi.io.github.classgraph.concurrency.SingletonMap; -import nonapi.io.github.classgraph.concurrency.SingletonMap.NullSingletonException; import nonapi.io.github.classgraph.utils.FastPathResolver; import nonapi.io.github.classgraph.utils.FileUtils; import nonapi.io.github.classgraph.utils.LogNode; @@ -52,23 +50,8 @@ class PhysicalZipFile implements Closeable { /** The path to the zipfile. */ private final String path; - /** The {@link RandomAccessFile}. */ - private RandomAccessFile raf; - - /** The {@link FileChannel}. */ - private FileChannel fileChannel; - - /** The file length. */ - final long fileLen; - - /** The number of mapped byte buffers. */ - final int numMappedByteBuffers; - - /** The cached mapped byte buffers for each 2GB chunk. */ - private ByteBuffer[] mappedByteBuffersCached; - - /** A singleton map from chunk index to byte buffer, ensuring that any given chunk is only mapped once. */ - private SingletonMap chunkIdxToByteBuffer; + /** The byte buffer resources. */ + private MappedByteBufferResources byteBufferResources; /** The nested jar handler. */ NestedJarHandler nestedJarHandler; @@ -97,39 +80,8 @@ class PhysicalZipFile implements Closeable { this.nestedJarHandler = nestedJarHandler; this.path = FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, file.getPath()); - // Open the File as a RandomAccessFile, and open a FileChannel on the RandomAccessFile - try { - raf = new RandomAccessFile(file, "r"); - fileLen = raf.length(); - if (fileLen == 0L) { - throw new IOException("Zipfile is empty: " + file); - } - fileChannel = raf.getChannel(); - } catch (final IOException | SecurityException e) { - if (fileChannel != null) { - fileChannel.close(); - fileChannel = null; - } - if (raf != null) { - raf.close(); - raf = null; - } - throw e; - } - - // Implement an array of MappedByteBuffers to support jarfiles >2GB in size: - // https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6347833 - numMappedByteBuffers = (int) ((fileLen + FileUtils.MAX_BUFFER_SIZE) / FileUtils.MAX_BUFFER_SIZE); - mappedByteBuffersCached = new MappedByteBuffer[numMappedByteBuffers]; - chunkIdxToByteBuffer = new SingletonMap() { - @Override - public ByteBuffer newInstance(final Integer chunkIdxI, final LogNode log) throws IOException { - // Map the indexed 2GB chunk of the file to a MappedByteBuffer - final long pos = chunkIdxI.longValue() * FileUtils.MAX_BUFFER_SIZE; - final long chunkSize = Math.min(FileUtils.MAX_BUFFER_SIZE, fileLen - pos); - return nestedJarHandler.mapFileChannelToByteBuffer(fileChannel, pos, chunkSize); - } - }; + // Map the file to a ByteBuffer + this.byteBufferResources = new MappedByteBufferResources(file, nestedJarHandler); } /** @@ -153,16 +105,41 @@ public ByteBuffer newInstance(final Integer chunkIdxI, final LogNode log) throws this.nestedJarHandler = nestedJarHandler; this.isDeflatedToRam = true; - fileLen = byteBuffer.remaining(); - if (fileLen == 0L) { + // Wrap the ByteBuffer + this.byteBufferResources = new MappedByteBufferResources(byteBuffer, nestedJarHandler); + if (this.byteBufferResources.length() == 0L) { throw new IOException("Zipfile is empty: " + path); } + } - // Implement an array of MappedByteBuffers to support jarfiles >2GB in size: - // https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6347833 - numMappedByteBuffers = 1; - mappedByteBuffersCached = new ByteBuffer[numMappedByteBuffers]; - mappedByteBuffersCached[0] = byteBuffer; + /** + * Construct a {@link PhysicalZipFile} from an InputStream, which is downloaded to a {@link ByteBuffer} in RAM, + * or spilled to disk if the content of the InputStream is too large. + * + * @param inputStream + * the input stream + * @param path + * the source URL the InputStream was opened from, or the zip entry path of this entry in the parent + * zipfile + * @param nestedJarHandler + * the nested jar handler + * @param log + * the log + * @throws IOException + * if an I/O exception occurs. + */ + PhysicalZipFile(final InputStream inputStream, final String path, final NestedJarHandler nestedJarHandler, + final LogNode log) throws IOException { + this.nestedJarHandler = nestedJarHandler; + this.path = path; + this.isDeflatedToRam = true; + + // Wrap the ByteBuffer + this.byteBufferResources = new MappedByteBufferResources(inputStream, path, nestedJarHandler, log); + if (this.byteBufferResources.length() == 0L) { + throw new IOException("Zipfile is empty: " + path); + } + this.file = byteBufferResources.getMappedFile(); } /** @@ -178,23 +155,7 @@ public ByteBuffer newInstance(final Integer chunkIdxI, final LogNode log) throws * If the thread was interrupted. */ ByteBuffer getByteBuffer(final int chunkIdx) throws IOException, InterruptedException { - if (closed.get()) { - throw new IOException(getClass().getSimpleName() + " already closed"); - } - if (chunkIdx < 0 || chunkIdx >= mappedByteBuffersCached.length) { - throw new IOException("Chunk index out of range"); - } - // Fast path: only look up singleton map if mappedByteBuffersCached is null - if (mappedByteBuffersCached[chunkIdx] == null) { - // This 2GB chunk has not yet been read -- mmap it (use a singleton map so that the mmap - // doesn't happen more than once, in case of race condition) - try { - mappedByteBuffersCached[chunkIdx] = chunkIdxToByteBuffer.get(chunkIdx, /* log = */ null); - } catch (final NullSingletonException e) { - throw new IOException("Cannot get ByteBuffer chunk " + chunkIdx + " : " + e); - } - } - return mappedByteBuffersCached[chunkIdx]; + return byteBufferResources.getByteBuffer(chunkIdx); } /** @@ -218,6 +179,29 @@ public String getPath() { return path; } + /** + * Get the length of the mapped file, or the initial remaining bytes in the wrapped ByteBuffer if a buffer was + * wrapped. + */ + public long length() { + return byteBufferResources.length(); + } + + /** + * Get the number of 2GB chunks that are available in this PhysicalZipFile. + */ + public int numChunks() { + return byteBufferResources.numChunks(); + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return path; + } + /* (non-Javadoc) * @see java.lang.Object#hashCode() */ @@ -245,44 +229,11 @@ public boolean equals(final Object obj) { @Override public void close() { if (!closed.getAndSet(true)) { - if (chunkIdxToByteBuffer != null) { - chunkIdxToByteBuffer.clear(); - chunkIdxToByteBuffer = null; - } - if (mappedByteBuffersCached != null) { - for (int i = 0; i < mappedByteBuffersCached.length; i++) { - if (mappedByteBuffersCached[i] != null) { - nestedJarHandler.unmapByteBuffer(mappedByteBuffersCached[i], /* log = */ null); - mappedByteBuffersCached[i] = null; - } - } - mappedByteBuffersCached = null; - } - if (fileChannel != null) { - try { - fileChannel.close(); - fileChannel = null; - } catch (final IOException e) { - // Ignore - } - } - if (raf != null) { - try { - raf.close(); - raf = null; - } catch (final IOException e) { - // Ignore - } + if (byteBufferResources != null) { + byteBufferResources.close(/* log = */ null); } + byteBufferResources = null; nestedJarHandler = null; } } - - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return path; - } } \ No newline at end of file diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSlice.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSlice.java index 85aeab74b..c223f0227 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSlice.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSlice.java @@ -68,7 +68,7 @@ public ZipFileSliceReader newInstance() throws RuntimeException { } /** - * Create a ZipFileSlice that wraps an entire {@link PhysicalZipFile}. + * Create a ZipFileSlice that wraps a toplevel {@link PhysicalZipFile}. * * @param physicalZipFile * the physical zipfile @@ -77,7 +77,7 @@ public ZipFileSliceReader newInstance() throws RuntimeException { this.parentZipFileSlice = null; this.physicalZipFile = physicalZipFile; this.startOffsetWithinPhysicalZipFile = 0; - this.len = physicalZipFile.fileLen; + this.len = physicalZipFile.length(); this.pathWithinParentZipFileSlice = physicalZipFile.getPath(); this.zipFileSliceReaderRecycler = newZipFileSliceReaderRecycler(); } @@ -85,16 +85,16 @@ public ZipFileSliceReader newInstance() throws RuntimeException { /** * Create a ZipFileSlice that wraps a {@link PhysicalZipFile} extracted to a ByteBuffer in memory. * - * @param physicalZipFileInRam + * @param physicalZipFile * a physical zipfile that has been extracted to RAM * @param zipEntry * the zip entry */ - ZipFileSlice(final PhysicalZipFile physicalZipFileInRam, final FastZipEntry zipEntry) { + ZipFileSlice(final PhysicalZipFile physicalZipFile, final FastZipEntry zipEntry) { this.parentZipFileSlice = zipEntry.parentLogicalZipFile; - this.physicalZipFile = physicalZipFileInRam; + this.physicalZipFile = physicalZipFile; this.startOffsetWithinPhysicalZipFile = 0; - this.len = physicalZipFile.fileLen; + this.len = physicalZipFile.length(); this.pathWithinParentZipFileSlice = zipEntry.entryName; this.zipFileSliceReaderRecycler = newZipFileSliceReaderRecycler(); } @@ -239,6 +239,6 @@ public String toString() { : physicalZipFile.getFile() == null ? "ByteBuffer downloaded to RAM from " + getPath() : physicalZipFile.getFile()) + " ; byte range: " + startOffsetWithinPhysicalZipFile + ".." - + (startOffsetWithinPhysicalZipFile + len) + " / " + physicalZipFile.fileLen + "]"; + + (startOffsetWithinPhysicalZipFile + len) + " / " + physicalZipFile.length() + "]"; } } \ No newline at end of file diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSliceReader.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSliceReader.java index e3e7fb3e1..34836a590 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSliceReader.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSliceReader.java @@ -45,7 +45,10 @@ class ZipFileSliceReader implements AutoCloseable { /** The zipfile slice. */ private final ZipFileSlice zipFileSlice; - /** The chunk cache. */ + /** + * The chunk cache, for duplicates of the ByteBuffers in the ZipFileSlice, so that different threads can work on + * the same MappedByteBuffers without interfering with each other's buffer position. + */ private final ByteBuffer[] chunkCache; /** A scratch buffer. */ @@ -59,7 +62,7 @@ class ZipFileSliceReader implements AutoCloseable { */ public ZipFileSliceReader(final ZipFileSlice zipFileSlice) { this.zipFileSlice = zipFileSlice; - this.chunkCache = new ByteBuffer[zipFileSlice.physicalZipFile.numMappedByteBuffers]; + this.chunkCache = new ByteBuffer[zipFileSlice.physicalZipFile.numChunks()]; } /** 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 c83ebb780..fbf65265f 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -197,7 +197,7 @@ private static SimpleEntry readAllBytes(final InputStream input bufLength = bufLength << 1; } else { if (bufLength == MAX_BUFFER_SIZE) { - throw new IOException("InputStream too large to read"); + throw new IOException("InputStream too large to read into array"); } bufLength = MAX_BUFFER_SIZE; } From c6bc239b6ea80e7360211b86438ac0461b9461cd Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 29 Nov 2019 03:27:27 -0700 Subject: [PATCH 0548/1778] Update error message --- 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 cea8ee364..eb40904c6 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementDir.java @@ -210,7 +210,7 @@ public synchronized ByteBuffer read() throws IOException { // to read files larger than 2GB is probably limited (it's not even supported for zipfiles), // and the caller can use an InputStream if necessary. throw new IOException( - "File is larger than 2GB, cannot use read() method, use open() instead"); + "File is larger than 2GB -- cannot use read() method, use open() instead"); } // Fetch chunk 0 (the first ~2GB of the file) byteBuffer = mappedFileResources.getByteBuffer(0); From c61edccfcf2b44504620830ffb46fc083fcf4c1e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 29 Nov 2019 07:14:27 -0700 Subject: [PATCH 0549/1778] Numerous robustness fixes --- .../java/io/github/classgraph/ClassGraph.java | 8 +- .../classgraph/ClasspathElementDir.java | 7 +- .../classgraph/ClasspathElementModule.java | 17 ++- .../java/io/github/classgraph/Scanner.java | 107 ++++++++++-------- .../classgraph/concurrency/SingletonMap.java | 38 +++++++ .../MappedByteBufferResources.java | 2 +- .../fastzipfilereader/NestedJarHandler.java | 84 ++++++-------- .../fastzipfilereader/PhysicalZipFile.java | 4 +- 8 files changed, 155 insertions(+), 112 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index 4fe18c03b..9137ef8e9 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -1162,11 +1162,11 @@ public void scanAsync(final ExecutorService executorService, final int numParall @Override public void run() { try { + // Call scanner, but ignore the returned ScanResult new Scanner(scanSpec, executorService, numParallelTasks, scanResultProcessor, failureHandler, - 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) + topLevelLog).call(); + } catch (final InterruptedException | CancellationException | ExecutionException e) { + // Call failure handler failureHandler.onFailure(e); } } diff --git a/src/main/java/io/github/classgraph/ClasspathElementDir.java b/src/main/java/io/github/classgraph/ClasspathElementDir.java index eb40904c6..ac8a08f88 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementDir.java @@ -216,7 +216,12 @@ public synchronized ByteBuffer read() throws IOException { byteBuffer = mappedFileResources.getByteBuffer(0); length = byteBuffer.remaining(); return byteBuffer; - } catch (final IOException | SecurityException | OutOfMemoryError | InterruptedException e) { + } catch (final IOException | SecurityException | OutOfMemoryError e) { + close(); + throw new IOException("Could not open " + this, e); + } catch (final InterruptedException e) { + // Re-set interrupt status + Thread.currentThread().interrupt(); close(); throw new IOException("Could not open " + this, e); } diff --git a/src/main/java/io/github/classgraph/ClasspathElementModule.java b/src/main/java/io/github/classgraph/ClasspathElementModule.java index 18e9331bf..428549ae1 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementModule.java +++ b/src/main/java/io/github/classgraph/ClasspathElementModule.java @@ -39,10 +39,10 @@ import java.util.Set; import io.github.classgraph.Scanner.ClasspathEntryWorkUnit; +import nonapi.io.github.classgraph.concurrency.SingletonMap; import nonapi.io.github.classgraph.concurrency.SingletonMap.NullSingletonException; 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.recycler.RecycleOnClose; import nonapi.io.github.classgraph.recycler.Recycler; import nonapi.io.github.classgraph.scanspec.ScanSpec; @@ -57,8 +57,9 @@ class ClasspathElementModule extends ClasspathElement { /** The module ref. */ final ModuleRef moduleRef; - /** The nested jar handler. */ - private final NestedJarHandler nestedJarHandler; + /** A singleton map from a {@link ModuleRef} to a {@link ModuleReaderProxy} recycler for the module. */ + SingletonMap, IOException> // + moduleRefToModuleReaderProxyRecyclerMap; /** The module reader proxy recycler. */ private Recycler moduleReaderProxyRecycler; @@ -73,16 +74,15 @@ class ClasspathElementModule extends ClasspathElement { * the module ref * @param classLoader * the classloader - * @param nestedJarHandler - * the nested jar handler * @param scanSpec * the scan spec */ ClasspathElementModule(final ModuleRef moduleRef, final ClassLoader classLoader, - final NestedJarHandler nestedJarHandler, final ScanSpec scanSpec) { + final SingletonMap, IOException> // + moduleRefToModuleReaderProxyRecyclerMap, final ScanSpec scanSpec) { super(classLoader, scanSpec); + this.moduleRefToModuleReaderProxyRecyclerMap = moduleRefToModuleReaderProxyRecyclerMap; this.moduleRef = moduleRef; - this.nestedJarHandler = nestedJarHandler; } /* (non-Javadoc) @@ -101,8 +101,7 @@ void open(final WorkQueue workQueueIgnored, final int cl return; } try { - moduleReaderProxyRecycler = nestedJarHandler.moduleRefToModuleReaderProxyRecyclerMap.get(moduleRef, - /* ignored */ null); + moduleReaderProxyRecycler = moduleRefToModuleReaderProxyRecyclerMap.get(moduleRef, log); } catch (final IOException | NullSingletonException e) { if (log != null) { log(classpathElementIdx, "Skipping invalid module " + getModuleName() + " : " + e, log); diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 3708e7599..1f246a181 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -199,60 +199,70 @@ class Scanner implements Callable { scanModules = scanSpec.scanModules; } - this.moduleOrder = new ArrayList<>(); - if (scanModules) { - // Add modules to start of classpath order, before traditional classpath - final List systemModuleRefs = moduleFinder.getSystemModuleRefs(); - final ClassLoader defaultClassLoader = classLoaderOrderRespectingParentDelegation != null - && classLoaderOrderRespectingParentDelegation.length != 0 - ? classLoaderOrderRespectingParentDelegation[0] - : null; - if (systemModuleRefs != null) { - for (final ModuleRef systemModuleRef : systemModuleRefs) { - final String moduleName = systemModuleRef.getName(); - if ( - // If scanning system packages and modules is enabled and white/blacklist is empty, - // then scan all system modules - (scanSpec.enableSystemJarsAndModules - && scanSpec.moduleWhiteBlackList.whitelistAndBlacklistAreEmpty()) - // Otherwise only scan specifically whitelisted system modules - || scanSpec.moduleWhiteBlackList - .isSpecificallyWhitelistedAndNotBlacklisted(moduleName)) { - // Create a new ClasspathElementModule - final ClasspathElementModule classpathElementModule = new ClasspathElementModule( - systemModuleRef, defaultClassLoader, nestedJarHandler, scanSpec); - moduleOrder.add(classpathElementModule); - // Open the ClasspathElementModule - classpathElementModule.open(/* ignored */ null, moduleOrder.size() - 1, classpathFinderLog); - } else { - if (classpathFinderLog != null) { - classpathFinderLog - .log("Skipping non-whitelisted or blacklisted system module: " + moduleName); + try { + this.moduleOrder = new ArrayList<>(); + if (scanModules) { + // Add modules to start of classpath order, before traditional classpath + final List systemModuleRefs = moduleFinder.getSystemModuleRefs(); + final ClassLoader defaultClassLoader = classLoaderOrderRespectingParentDelegation != null + && classLoaderOrderRespectingParentDelegation.length != 0 + ? classLoaderOrderRespectingParentDelegation[0] + : null; + if (systemModuleRefs != null) { + for (final ModuleRef systemModuleRef : systemModuleRefs) { + final String moduleName = systemModuleRef.getName(); + if ( + // If scanning system packages and modules is enabled and white/blacklist is empty, + // then scan all system modules + (scanSpec.enableSystemJarsAndModules + && scanSpec.moduleWhiteBlackList.whitelistAndBlacklistAreEmpty()) + // Otherwise only scan specifically whitelisted system modules + || scanSpec.moduleWhiteBlackList + .isSpecificallyWhitelistedAndNotBlacklisted(moduleName)) { + // Create a new ClasspathElementModule + final ClasspathElementModule classpathElementModule = new ClasspathElementModule( + systemModuleRef, defaultClassLoader, + nestedJarHandler.moduleRefToModuleReaderProxyRecyclerMap, scanSpec); + moduleOrder.add(classpathElementModule); + // Open the ClasspathElementModule + classpathElementModule.open(/* ignored */ null, moduleOrder.size() - 1, + classpathFinderLog); + } else { + if (classpathFinderLog != null) { + classpathFinderLog.log( + "Skipping non-whitelisted or blacklisted system module: " + moduleName); + } } } } - } - final List nonSystemModuleRefs = moduleFinder.getNonSystemModuleRefs(); - if (nonSystemModuleRefs != null) { - for (final ModuleRef nonSystemModuleRef : nonSystemModuleRefs) { - String moduleName = nonSystemModuleRef.getName(); - if (moduleName == null) { - moduleName = ""; - } - if (scanSpec.moduleWhiteBlackList.isWhitelistedAndNotBlacklisted(moduleName)) { - // Create a new ClasspathElementModule - final ClasspathElementModule classpathElementModule = new ClasspathElementModule( - nonSystemModuleRef, defaultClassLoader, nestedJarHandler, scanSpec); - moduleOrder.add(classpathElementModule); - // Open the ClasspathElementModule - classpathElementModule.open(/* ignored */ null, moduleOrder.size() - 1, classpathFinderLog); - } else { - if (classpathFinderLog != null) { - classpathFinderLog.log("Skipping non-whitelisted or blacklisted module: " + moduleName); + final List nonSystemModuleRefs = moduleFinder.getNonSystemModuleRefs(); + if (nonSystemModuleRefs != null) { + for (final ModuleRef nonSystemModuleRef : nonSystemModuleRefs) { + String moduleName = nonSystemModuleRef.getName(); + if (moduleName == null) { + moduleName = ""; + } + if (scanSpec.moduleWhiteBlackList.isWhitelistedAndNotBlacklisted(moduleName)) { + // Create a new ClasspathElementModule + final ClasspathElementModule classpathElementModule = new ClasspathElementModule( + nonSystemModuleRef, defaultClassLoader, + nestedJarHandler.moduleRefToModuleReaderProxyRecyclerMap, scanSpec); + moduleOrder.add(classpathElementModule); + // Open the ClasspathElementModule + classpathElementModule.open(/* ignored */ null, moduleOrder.size() - 1, + classpathFinderLog); + } else { + if (classpathFinderLog != null) { + classpathFinderLog + .log("Skipping non-whitelisted or blacklisted module: " + moduleName); + } } } } } + } catch (final InterruptedException e) { + nestedJarHandler.close(/* log = */ null); + throw e; } } @@ -1145,7 +1155,8 @@ public ScanResult call() throws InterruptedException, CancellationException, Exe } finally { if (removeTemporaryFilesAfterScan) { - // If removeTemporaryFilesAfterScan was set, remove temp files and close resources, zipfiles and modules + // If removeTemporaryFilesAfterScan was set, remove temp files and close resources, + // zipfiles and modules nestedJarHandler.close(topLevelLog); } } 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 ebab598a5..071ab6ff1 100644 --- a/src/main/java/nonapi/io/github/classgraph/concurrency/SingletonMap.java +++ b/src/main/java/nonapi/io/github/classgraph/concurrency/SingletonMap.java @@ -28,6 +28,7 @@ */ package nonapi.io.github.classgraph.concurrency; +import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.List; import java.util.Map.Entry; @@ -222,6 +223,43 @@ public List values() throws InterruptedException { return entries; } + /** + * Returns true if the map is empty. + * + * @return true, if the map is empty + */ + public boolean isEmpty() { + return map.isEmpty(); + } + + /** + * Get the map entries. + * + * @return the map entries. + * @throws InterruptedException + * if interrupted. + */ + public List> entries() throws InterruptedException { + final List> entries = new ArrayList<>(map.size()); + for (final Entry> ent : map.entrySet()) { + entries.add(new SimpleEntry<>(ent.getKey(), ent.getValue().get())); + } + return entries; + } + + /** + * Remove the singleton for a given key. + * + * @return the old singleton from the map, if one was present, otherwise null. + * @throws InterruptedException + * if interrupted. + */ + @SuppressWarnings("null") + public V remove(final K key) throws InterruptedException { + final SingletonHolder val = map.remove(key); + return val == null ? null : val.get(); + } + /** Clear the map. */ public void clear() { map.clear(); diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java index 238c6fd6e..e9796a5ee 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java @@ -76,7 +76,7 @@ public class MappedByteBufferResources { /** The nested jar handler. */ private final NestedJarHandler nestedJarHandler; - /** Set to true once this {@link PhysicalZipFile} is closed. */ + /** Set to true once {@link #close()} has been called. */ private final AtomicBoolean closed = new AtomicBoolean(false); /** 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 44db0585e..69b7f5995 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java @@ -58,7 +58,6 @@ import io.github.classgraph.ScanResult; import nonapi.io.github.classgraph.concurrency.InterruptionChecker; import nonapi.io.github.classgraph.concurrency.SingletonMap; -import nonapi.io.github.classgraph.concurrency.SingletonMap.NullSingletonException; import nonapi.io.github.classgraph.json.ReferenceEqualityKey; import nonapi.io.github.classgraph.recycler.Recycler; import nonapi.io.github.classgraph.scanspec.ScanSpec; @@ -85,6 +84,8 @@ public PhysicalZipFile newInstance(final File canonicalFile, final LogNode log) .newClassGraphException(NestedJarHandler.class.getSimpleName() + " already closed"); } final PhysicalZipFile physicalZipFile = new PhysicalZipFile(canonicalFile, NestedJarHandler.this); + allocatedPhysicalZipFiles.add(physicalZipFile); + return physicalZipFile; } }; @@ -194,9 +195,16 @@ public Entry newInstance(final String nestedJarPathRaw, } else { // Jarfile should be a local file -- wrap in a PhysicalZipFile instance try { + // Get canonical file final File canonicalFile = new File(nestedJarPath).getCanonicalFile(); - physicalZipFile = canonicalFileToPhysicalZipFile(canonicalFile, log); + // Get or create a PhysicalZipFile instance for the canonical file + physicalZipFile = canonicalFileToPhysicalZipFileMap.get(canonicalFile, log); + } catch (final NullSingletonException 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( "Path component " + nestedJarPath + " could not be canonicalized: " + e); } @@ -368,11 +376,11 @@ public RecyclableInflater newInstance() throws RuntimeException { * {@link MappedByteBuffer} instances that are currently mapped. (Use {@link ReferenceEqualityKey} so that the * entire contents of the buffers are not compared by {@link ByteBuffer#equals(Object)}). */ - private final Set> mappedByteBuffers = Collections + private Set> mappedByteBuffers = Collections .newSetFromMap(new ConcurrentHashMap, Boolean>()); /** {@link MappedByteBufferResources} instances that were allocated for downloading jars from URLs. */ - private final Set mappedByteBufferResources = Collections + private Set mappedByteBufferResources = Collections .newSetFromMap(new ConcurrentHashMap()); /** Any temporary files created while scanning. */ @@ -404,31 +412,6 @@ public NestedJarHandler(final ScanSpec scanSpec, final InterruptionChecker inter // ------------------------------------------------------------------------------------------------------------- - /** - * Get the {@link PhysicalZipFile} for a cononical {@link File}. - * - * @param canonicalFile - * the canonical file - * @param log - * the log - * @return the physical zip file - * @throws IOException - * If the {@link File} could not be read, or was not a file. - * @throws InterruptedException - * If the thread was interrupted. - */ - private PhysicalZipFile canonicalFileToPhysicalZipFile(final File canonicalFile, final LogNode log) - throws IOException, InterruptedException { - try { - // Get or create a PhysicalZipFile instance for the canonical file - return canonicalFileToPhysicalZipFileMap.get(canonicalFile, log); - } catch (final NullSingletonException e) { - throw new IOException("Could not get physical zipfile " + canonicalFile + " : " + e); - } - } - - // ------------------------------------------------------------------------------------------------------------- - /** * Record that a {@link FileChannel} was mapped to a {@link MappedByteBuffer}. * @@ -582,14 +565,17 @@ public void close(final LogNode log) { logicalZipFile.close(); } if (canonicalFileToPhysicalZipFileMap != null) { - try { - for (final PhysicalZipFile physicalZipFile : canonicalFileToPhysicalZipFileMap.values()) { - physicalZipFile.close(); + while (!canonicalFileToPhysicalZipFileMap.isEmpty()) { + try { + for (final Entry ent : canonicalFileToPhysicalZipFileMap.entries()) { + final PhysicalZipFile physicalZipFile = ent.getValue(); + physicalZipFile.close(); + canonicalFileToPhysicalZipFileMap.remove(ent.getKey()); + } + } catch (final InterruptedException e) { + interruptionChecker.interrupt(); } - } catch (final InterruptedException e) { - interruptionChecker.interrupt(); } - canonicalFileToPhysicalZipFileMap.clear(); canonicalFileToPhysicalZipFileMap = null; } if (allocatedPhysicalZipFiles != null) { @@ -604,7 +590,22 @@ public void close(final LogNode log) { fastZipEntryToZipFileSliceMap.clear(); fastZipEntryToZipFileSliceMap = null; } - // Temp files have to be deleted last, after all PhysicalZipFiles are closed + if (mappedByteBufferResources != null) { + for (final MappedByteBufferResources bufRes : mappedByteBufferResources) { + bufRes.close(log); + } + mappedByteBufferResources = null; + } + if (mappedByteBuffers != null) { + while (!mappedByteBuffers.isEmpty()) { + for (final ReferenceEqualityKey byteBufferRef : new ArrayList<>( + mappedByteBuffers)) { + unmapByteBuffer(byteBufferRef.get(), log); + } + } + mappedByteBuffers = null; + } + // 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"); @@ -621,17 +622,6 @@ public void close(final LogNode log) { tempFiles.clear(); tempFiles = null; } - if (mappedByteBufferResources != null) { - for (final MappedByteBufferResources bufRes : new ArrayList<>(mappedByteBufferResources)) { - bufRes.close(log); - } - } - if (mappedByteBuffers != null) { - for (final ReferenceEqualityKey byteBufferRef : new ArrayList<>( - mappedByteBuffers)) { - unmapByteBuffer(byteBufferRef.get(), log); - } - } } } } 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 2efe43e01..dbfcb6386 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/PhysicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/PhysicalZipFile.java @@ -44,7 +44,7 @@ /** A physical zipfile, which is mmap'd using a {@link FileChannel}. */ class PhysicalZipFile implements Closeable { - /** The {@link File} backing this {@link PhysicalZipFile}. */ + /** The {@link File} backing this {@link PhysicalZipFile}, if any. */ private final File file; /** The path to the zipfile. */ @@ -59,7 +59,7 @@ class PhysicalZipFile implements Closeable { /** True if the zipfile was deflated to RAM, rather than mapped from disk. */ boolean isDeflatedToRam; - /** Set to true once this {@link PhysicalZipFile} is closed. */ + /** Set to true once {@link #close()} has been called. */ private final AtomicBoolean closed = new AtomicBoolean(false); /** From 7e66fba31c0be6d41027725f0fcc251b2ac380bf Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 29 Nov 2019 07:16:31 -0700 Subject: [PATCH 0550/1778] [maven-release-plugin] prepare release classgraph-4.8.58 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index d5f1fa05a..f7f2b6149 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.58-SNAPSHOT + 4.8.58 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.56 + classgraph-4.8.58 From e4ae3bdb82d43151247b0e62ec9a9c4386beeb72 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 29 Nov 2019 07:16:38 -0700 Subject: [PATCH 0551/1778] [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 f7f2b6149..ae9716e47 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.58 + 4.8.59-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.58 + classgraph-4.8.56 From 56c2e320db8e602721ae8c63d7006fe44ae991bf Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 30 Nov 2019 11:30:14 -0700 Subject: [PATCH 0552/1778] Robustness fixes --- .../MappedByteBufferResources.java | 63 ++++++++++++------- .../fastzipfilereader/NestedJarHandler.java | 47 ++++++++++---- 2 files changed, 75 insertions(+), 35 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java index e9796a5ee..4edbdbc49 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java @@ -119,20 +119,32 @@ public MappedByteBufferResources(final InputStream inputStream, final String tem } else { // bytesRead == 0 => ran out of buffer space, spill over to disk - final File tempFile = nestedJarHandler.makeTempFile(tempFileBaseName, /* onlyUseLeafname = */ true); - mappedFileIsTempFile = true; if (log != null) { log.log("Could not fit downloaded URL into max RAM buffer size of " + MAX_JAR_RAM_SIZE - + " bytes, downloading to temporary file: " + tempFileBaseName + " -> " + tempFile); + + " bytes, downloading to temporary file: " + tempFileBaseName + " -> " + this.mappedFile); } - Files.write(tempFile.toPath(), buf, StandardOpenOption.WRITE); - try (OutputStream os = new BufferedOutputStream(new FileOutputStream(tempFile, /* append = */ true))) { + try { + this.mappedFile = nestedJarHandler.makeTempFile(tempFileBaseName, /* onlyUseLeafname = */ true); + } catch (final IOException e) { + if (log != null) { + log.log("Could not create temporary file: " + e); + } + throw e; + } + this.mappedFileIsTempFile = true; + + // Write the full buffer to the temporary file + Files.write(this.mappedFile.toPath(), buf, StandardOpenOption.WRITE); + + // Copy the rest of the InputStream to the end of the temporary file + try (OutputStream os = new BufferedOutputStream( + new FileOutputStream(this.mappedFile, /* append = */ true))) { for (int bytesReadCtd; (bytesReadCtd = inputStream.read(buf, 0, buf.length)) > 0;) { os.write(buf, 0, bytesReadCtd); } } // Map the file to a MappedByteBuffer - mapFile(tempFile); + mapFile(); } } @@ -164,7 +176,9 @@ public MappedByteBufferResources(final ByteBuffer byteBuffer, final NestedJarHan */ public MappedByteBufferResources(final File file, final NestedJarHandler nestedJarHandler) throws IOException { this.nestedJarHandler = nestedJarHandler; - mapFile(file); + this.mappedFile = file; + // Map the file to a MappedByteBuffer + mapFile(); } /** @@ -180,32 +194,36 @@ private void wrapByteBuffer(final ByteBuffer byteBuffer) { byteBufferChunksCached = new AtomicReferenceArray(1); byteBufferChunksCached.set(0, byteBuffer); - // Don't set file, fileChannel or raf, they are unneeded. - // byteBufferIsMapped will remain false. + // Don't set mappedFile, fileChannel or raf, they are unneeded. } /** * Map a {@link File} to a {@link MappedByteBuffer}. * - * @param file - * the file * @throws IOException * Signals that an I/O exception has occurred. */ - private void mapFile(final File file) throws IOException { - this.mappedFile = file; + private void mapFile() throws IOException { try { - raf = new RandomAccessFile(file, "r"); + raf = new RandomAccessFile(mappedFile, "r"); length = raf.length(); fileChannel = raf.getChannel(); } catch (final IOException | SecurityException e) { if (fileChannel != null) { - fileChannel.close(); + try { + fileChannel.close(); + } catch (final IOException e2) { + // Ignore + } fileChannel = null; } if (raf != null) { - raf.close(); + try { + raf.close(); + } catch (final IOException e2) { + // Ignore + } raf = null; } throw e; @@ -249,7 +267,6 @@ public ByteBuffer newInstance(final Integer chunkIdxI, final LogNode log) throws return byteBuffer; } }; - } /** @@ -327,6 +344,7 @@ public void close(final LogNode log) { chunkIdxToByteBufferSingletonMap = null; } if (byteBufferChunksCached != null) { + // Only unmap bytebuffers if they came from a mapped file if (mappedFile != null) { for (int i = 0; i < byteBufferChunksCached.length(); i++) { final ByteBuffer mappedByteBuffer = byteBufferChunksCached.get(i); @@ -341,26 +359,27 @@ public void close(final LogNode log) { if (fileChannel != null) { try { fileChannel.close(); - fileChannel = null; } catch (final IOException e) { // Ignore } + fileChannel = null; } if (raf != null) { try { raf.close(); - raf = null; } catch (final IOException e) { // Ignore } + raf = null; } if (mappedFile != null) { + // If mapped file was a temp file, remove it if (mappedFileIsTempFile) { try { - Files.delete(mappedFile.toPath()); - } catch (final IOException e) { + nestedJarHandler.removeTempFile(mappedFile); + } catch (IOException | SecurityException e) { if (log != null) { - log.log("Could not delete temporary file " + mappedFile + " : " + e); + log.log("Removing temporary file failed: " + mappedFile); } } } 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 69b7f5995..7a5a81ae4 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java @@ -47,7 +47,6 @@ import java.util.Queue; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicBoolean; import java.util.zip.Inflater; @@ -384,7 +383,7 @@ public RecyclableInflater newInstance() throws RuntimeException { .newSetFromMap(new ConcurrentHashMap()); /** Any temporary files created while scanning. */ - private ConcurrentLinkedDeque tempFiles = new ConcurrentLinkedDeque<>(); + private Set tempFiles = Collections.newSetFromMap(new ConcurrentHashMap()); /** The separator between random temp filename part and leafname. */ public static final String TEMP_FILENAME_LEAF_SEPARATOR = "---"; @@ -464,7 +463,7 @@ private String sanitizeFilename(final String filename) { /** * Create a temporary file, and mark it for deletion on exit. * - * @param filePath + * @param filePathBase * The path to derive the temporary filename from. * @param onlyUseLeafname * If true, only use the leafname of filePath to derive the temporary filename. @@ -472,14 +471,36 @@ private String sanitizeFilename(final String filename) { * @throws IOException * If the temporary file could not be created. */ - File makeTempFile(final String filePath, final boolean onlyUseLeafname) throws IOException { - final File tempFile = File.createTempFile("ClassGraph--", - TEMP_FILENAME_LEAF_SEPARATOR + sanitizeFilename(onlyUseLeafname ? leafname(filePath) : filePath)); + File makeTempFile(final String filePathBase, final boolean onlyUseLeafname) throws IOException { + final File tempFile = File.createTempFile("ClassGraph--", TEMP_FILENAME_LEAF_SEPARATOR + + sanitizeFilename(onlyUseLeafname ? leafname(filePathBase) : filePathBase)); tempFile.deleteOnExit(); tempFiles.add(tempFile); return tempFile; } + /** + * Attempt to remove a temporary file. + * + * @param tempFile + * the temp file + * @throws IOException + * If the temporary file could not be removed. + * @throws SecurityException + * 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); + } + } else { + throw new IOException("Not a temp file: " + tempFile); + } + } + /** * 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. @@ -610,16 +631,16 @@ public void close(final LogNode log) { final LogNode rmLog = tempFiles.isEmpty() || log == null ? null : log.log("Removing temporary files"); while (!tempFiles.isEmpty()) { - final File tempFile = tempFiles.removeLast(); - try { - Files.delete(tempFile.toPath()); - } catch (final IOException | SecurityException e) { - if (rmLog != null) { - rmLog.log("Removing temporary file failed: " + e); + for (final File tempFile : new ArrayList<>(tempFiles)) { + try { + removeTempFile(tempFile); + } catch (IOException | SecurityException e) { + if (rmLog != null) { + rmLog.log("Removing temporary file failed: " + tempFile); + } } } } - tempFiles.clear(); tempFiles = null; } } From e213ce7203f365142ff9b74d75727d27ea930ce3 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 1 Dec 2019 07:25:46 -0700 Subject: [PATCH 0553/1778] Increase robustness of error handling --- .../MappedByteBufferResources.java | 59 ++++++++++--------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java index 4edbdbc49..6db65e3dd 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java @@ -30,7 +30,6 @@ import java.io.BufferedOutputStream; import java.io.File; -import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; @@ -40,6 +39,7 @@ import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; +import java.nio.channels.NonReadableChannelException; import java.nio.file.Files; import java.nio.file.StandardOpenOption; import java.util.concurrent.atomic.AtomicBoolean; @@ -143,6 +143,7 @@ public MappedByteBufferResources(final InputStream inputStream, final String tem os.write(buf, 0, bytesReadCtd); } } + // Map the file to a MappedByteBuffer mapFile(); } @@ -161,6 +162,7 @@ public MappedByteBufferResources(final InputStream inputStream, final String tem public MappedByteBufferResources(final ByteBuffer byteBuffer, final NestedJarHandler nestedJarHandler) throws IOException { this.nestedJarHandler = nestedJarHandler; + // Wrap the existing byte buffer wrapByteBuffer(byteBuffer); } @@ -189,11 +191,9 @@ public MappedByteBufferResources(final File file, final NestedJarHandler nestedJ */ private void wrapByteBuffer(final ByteBuffer byteBuffer) { length = byteBuffer.remaining(); - // Put the ByteBuffer into the cache, so that the singleton map code for file mapping is never called byteBufferChunksCached = new AtomicReferenceArray(1); byteBufferChunksCached.set(0, byteBuffer); - // Don't set mappedFile, fileChannel or raf, they are unneeded. } @@ -209,24 +209,12 @@ private void mapFile() throws IOException { length = raf.length(); fileChannel = raf.getChannel(); - } catch (final IOException | SecurityException e) { - if (fileChannel != null) { - try { - fileChannel.close(); - } catch (final IOException e2) { - // Ignore - } - fileChannel = null; - } - if (raf != null) { - try { - raf.close(); - } catch (final IOException e2) { - // Ignore - } - raf = null; - } + } catch (final IOException e) { + close(/* log = */ null); throw e; + } catch (final IllegalArgumentException | SecurityException e) { + close(/* log = */ null); + throw new IOException(e); } // Implement an array of MappedByteBuffers to support jarfiles >2GB in size: @@ -249,16 +237,29 @@ public ByteBuffer newInstance(final Integer chunkIdxI, final LogNode log) throws try { // Try mapping the FileChannel byteBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, pos, chunkSize); - } catch (final FileNotFoundException e) { + } catch (final IOException e) { + MappedByteBufferResources.this.close(log); throw e; - } catch (IOException | OutOfMemoryError e) { - // If map failed, try calling System.gc() to free some allocated MappedByteBuffers - // (there is a limit to the number of mapped files -- 64k on Linux) - // See: http://www.mapdb.org/blog/mmap_files_alloc_and_jvm_crash/ - System.gc(); - System.runFinalization(); - // Then try calling map again - byteBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, pos, chunkSize); + } catch (final NonReadableChannelException | IllegalArgumentException e) { + MappedByteBufferResources.this.close(log); + throw new IOException(e); + } catch (final OutOfMemoryError e) { + try { + // If map failed, try calling System.gc() to free some allocated MappedByteBuffers + // (there is a limit to the number of mapped files -- 64k on Linux) + // See: http://www.mapdb.org/blog/mmap_files_alloc_and_jvm_crash/ + System.gc(); + System.runFinalization(); + // Then try calling map again + byteBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, pos, chunkSize); + } catch (final OutOfMemoryError e2) { + // Out of mappable virtual memory + MappedByteBufferResources.this.close(log); + throw new IOException(e2); + } catch (IOException | IllegalArgumentException e2) { + MappedByteBufferResources.this.close(log); + throw e2; + } } // Record that the byte buffer has been mapped From 28b32ec4b63f2527270414eadfb935a536f8d725 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 9 Dec 2019 16:39:06 -0700 Subject: [PATCH 0554/1778] Error handling consistency fix --- .../fastzipfilereader/MappedByteBufferResources.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java index 6db65e3dd..a9929bf51 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java @@ -252,13 +252,13 @@ public ByteBuffer newInstance(final Integer chunkIdxI, final LogNode log) throws System.runFinalization(); // Then try calling map again byteBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, pos, chunkSize); - } catch (final OutOfMemoryError e2) { - // Out of mappable virtual memory - MappedByteBufferResources.this.close(log); - throw new IOException(e2); - } catch (IOException | IllegalArgumentException e2) { + } catch (IOException e2) { MappedByteBufferResources.this.close(log); throw e2; + } catch (final OutOfMemoryError | IllegalArgumentException e2) { + // Out of mappable virtual memory, or other issue + MappedByteBufferResources.this.close(log); + throw new IOException(e2); } } From 9c7239266940ce85312ad562cc1cdd983e73796a Mon Sep 17 00:00:00 2001 From: Phokham Nonava Date: Tue, 17 Dec 2019 09:14:31 +0100 Subject: [PATCH 0555/1778] Respect Tomcat delegate property for delegation order --- .../TomcatWebappClassLoaderBaseHandler.java | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 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 9151ae684..776189235 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java @@ -68,8 +68,24 @@ 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.getParent(), /* isParent = */ true, log); + final boolean isParentFirst = isParentFirst(classLoader); + if (isParentFirst) { + classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true, log); + classLoaderOrder.add(classLoader, log); + return; + } + // Use parent-last delegation order classLoaderOrder.add(classLoader, log); + classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true, log); + } + + 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; } /** @@ -97,7 +113,7 @@ public static void findClasspathOrder(final ClassLoader classLoader, final Class final List> allResources = (List>) ReflectionUtils.getFieldVal(resources, "allResources", false); if (allResources != null) { - // type List + // type List for (final List webResourceSetList : allResources) { // type WebResourceSet // {DirResourceSet, FileResourceSet, JarResourceSet, JarWarResourceSet, EmptyResourceSet} From c81f6d9d37f3d11e1cff2ee370fbf5e4d85d5ca4 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 17 Dec 2019 03:10:57 -0700 Subject: [PATCH 0556/1778] Source > Cleanup --- .../classgraph/fastzipfilereader/MappedByteBufferResources.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java index a9929bf51..8d2085854 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java @@ -252,7 +252,7 @@ public ByteBuffer newInstance(final Integer chunkIdxI, final LogNode log) throws System.runFinalization(); // Then try calling map again byteBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, pos, chunkSize); - } catch (IOException e2) { + } catch (final IOException e2) { MappedByteBufferResources.this.close(log); throw e2; } catch (final OutOfMemoryError | IllegalArgumentException e2) { From 0477972f98cf3c06585cfeb60f563bbe6db7d8a1 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 17 Dec 2019 03:12:58 -0700 Subject: [PATCH 0557/1778] Code cleanups --- .../TomcatWebappClassLoaderBaseHandler.java | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 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 776189235..22ff1682e 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/TomcatWebappClassLoaderBaseHandler.java @@ -56,6 +56,22 @@ public static boolean canHandle(final Class classLoaderClass, final LogNode l 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; + } + /** * Find the {@link ClassLoader} delegation order for a {@link ClassLoader}. * @@ -70,22 +86,14 @@ public static void findClassLoaderOrder(final ClassLoader classLoader, final Cla 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); - return; } - // Use parent-last delegation order classLoaderOrder.add(classLoader, log); - classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true, log); - } - - private static boolean isParentFirst(final ClassLoader classLoader) { - final Object delegateObject = ReflectionUtils.getFieldVal(classLoader, "delegate", false); - if (delegateObject != null) { - return (boolean) delegateObject; + if (!isParentFirst) { + // Use parent-last delegation order + classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true, log); } - // Assume parent-first delegation order - return true; } /** From 16f2344b67fd2bab0522e6f0a39bea221880ba1c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 17 Dec 2019 03:14:33 -0700 Subject: [PATCH 0558/1778] [maven-release-plugin] prepare release classgraph-4.8.59 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ae9716e47..25f49cb80 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.59-SNAPSHOT + 4.8.59 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.56 + classgraph-4.8.59 From 487400a5b84a2d70690cc2abc393051b5afb8974 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 17 Dec 2019 03:14:40 -0700 Subject: [PATCH 0559/1778] [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 25f49cb80..f71c4f57e 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.59 + 4.8.60-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.59 + classgraph-4.8.56 From 971a92ea0bf0aca0d8147d0b1d418643df4c9e24 Mon Sep 17 00:00:00 2001 From: "Sean C. Sullivan" Date: Sun, 22 Dec 2019 11:00:55 -0800 Subject: [PATCH 0560/1778] GitHub Actions checkout v2 --- .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 401b76210..bea7d9498 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: os: [ ubuntu-latest, windows-latest, macos-latest ] java: [ '1.8.0', '11.0.x', '12.0.x', '13.0.x' ] steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 - name: Set up JDK uses: actions/setup-java@v1.2.0 with: From fc855667d931c42c2d8e1b8065bc340c00468fc1 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 7 Jan 2020 03:44:03 -0700 Subject: [PATCH 0561/1778] Respect parent-last classloading (#396) --- .../SpringBootRestartClassLoaderHandler.java | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) 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 0fabc1dc8..a00a364be 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/SpringBootRestartClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/SpringBootRestartClassLoaderHandler.java @@ -74,13 +74,20 @@ public static void findClassLoaderOrder(final ClassLoader classLoader, final Cla final LogNode log) { // The Restart classloader is a parent-last classloader, so need to delegate to the context // classloader(s) before the parent. - classLoaderOrder.delegateTo(Thread.currentThread().getContextClassLoader(), /* isParent = */ false, log); - classLoaderOrder.delegateTo(ClassGraph.class.getClassLoader(), /* isParent = */ false, log); + final ClassLoader parent = classLoader.getParent(); + if (Thread.currentThread().getContextClassLoader() != parent) { + classLoaderOrder.delegateTo(Thread.currentThread().getContextClassLoader(), /* isParent = */ false, + log); + } + if (ClassGraph.class.getClassLoader() != parent) { + classLoaderOrder.delegateTo(ClassGraph.class.getClassLoader(), /* isParent = */ false, log); + } // Also delegate to system classloader, in case the above two are actually the same as `classLoader` - classLoaderOrder.delegateTo(ClassLoader.getSystemClassLoader(), /* isParent = */ true, log); - // Finally delegate to the parent of the RestartClassLoader, in case that is different from - // SystemClassLoader - classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true, log); + if (ClassLoader.getSystemClassLoader() != parent) { + classLoaderOrder.delegateTo(ClassLoader.getSystemClassLoader(), /* isParent = */ true, log); + } + // Finally delegate to the parent of the RestartClassLoader + classLoaderOrder.delegateTo(parent, /* isParent = */ true, log); } /** From 7094a05940619206d581e6eed70c5c048fa14bee Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 7 Jan 2020 18:17:56 -0700 Subject: [PATCH 0562/1778] Fix Spring Boot classloading issue (Fixes #396) --- .../SpringBootRestartClassLoaderHandler.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) 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 a00a364be..4e71c6759 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/SpringBootRestartClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/SpringBootRestartClassLoaderHandler.java @@ -72,8 +72,12 @@ public static boolean canHandle(final Class classLoaderClass, final LogNode l */ public static void findClassLoaderOrder(final ClassLoader classLoader, final ClassLoaderOrder classLoaderOrder, final LogNode log) { - // The Restart classloader is a parent-last classloader, so need to delegate to the context - // classloader(s) before the parent. + // The Restart classloader is a parent-last classloader, so add the Restart classloader itself to the + // classloader order first + classLoaderOrder.add(classLoader, log); + + // Need to delegate to any other non-parent context classloader(s) before the parent, so go through all + // other context classloaders next, and delegate to them final ClassLoader parent = classLoader.getParent(); if (Thread.currentThread().getContextClassLoader() != parent) { classLoaderOrder.delegateTo(Thread.currentThread().getContextClassLoader(), /* isParent = */ false, @@ -82,10 +86,10 @@ public static void findClassLoaderOrder(final ClassLoader classLoader, final Cla if (ClassGraph.class.getClassLoader() != parent) { classLoaderOrder.delegateTo(ClassGraph.class.getClassLoader(), /* isParent = */ false, log); } - // Also delegate to system classloader, in case the above two are actually the same as `classLoader` if (ClassLoader.getSystemClassLoader() != parent) { classLoaderOrder.delegateTo(ClassLoader.getSystemClassLoader(), /* isParent = */ true, log); } + // Finally delegate to the parent of the RestartClassLoader classLoaderOrder.delegateTo(parent, /* isParent = */ true, log); } From 48f53b95d571b62efa685cc949f0db744a12c21a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 7 Jan 2020 18:20:45 -0700 Subject: [PATCH 0563/1778] [maven-release-plugin] prepare release classgraph-4.8.60 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index f71c4f57e..72e9665e2 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.60-SNAPSHOT + 4.8.60 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.56 + classgraph-4.8.60 From bc98b5aaf09ff68807c7c4288663cf8f78332661 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 7 Jan 2020 18:20:55 -0700 Subject: [PATCH 0564/1778] [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 72e9665e2..07d94e5d3 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.60 + 4.8.61-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.60 + classgraph-4.8.56 From 3176503686f54ca4e5d47554af2a0d22e9e27614 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 7 Jan 2020 18:28:19 -0700 Subject: [PATCH 0565/1778] Make some further simplifications for the Restart classloader (#396) --- .../SpringBootRestartClassLoaderHandler.java | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) 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 4e71c6759..2beea04f0 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/SpringBootRestartClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/SpringBootRestartClassLoaderHandler.java @@ -76,22 +76,8 @@ public static void findClassLoaderOrder(final ClassLoader classLoader, final Cla // classloader order first classLoaderOrder.add(classLoader, log); - // Need to delegate to any other non-parent context classloader(s) before the parent, so go through all - // other context classloaders next, and delegate to them - final ClassLoader parent = classLoader.getParent(); - if (Thread.currentThread().getContextClassLoader() != parent) { - classLoaderOrder.delegateTo(Thread.currentThread().getContextClassLoader(), /* isParent = */ false, - log); - } - if (ClassGraph.class.getClassLoader() != parent) { - classLoaderOrder.delegateTo(ClassGraph.class.getClassLoader(), /* isParent = */ false, log); - } - if (ClassLoader.getSystemClassLoader() != parent) { - classLoaderOrder.delegateTo(ClassLoader.getSystemClassLoader(), /* isParent = */ true, log); - } - // Finally delegate to the parent of the RestartClassLoader - classLoaderOrder.delegateTo(parent, /* isParent = */ true, log); + classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true, log); } /** From edf7baa73213ecdc5cc5a4b403fa1649d624decb Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 19 Jan 2020 22:24:31 -0700 Subject: [PATCH 0566/1778] Don't suggest to use `LATEST` as default version (#397) --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index cf1595314..9d71287ac 100644 --- a/README.md +++ b/README.md @@ -89,11 +89,13 @@ ClassGraph provides a number of important capabilities to the JVM ecosystem: ### Maven dependency +Replace `X.Y.Z` below with the latest [release number](https://github.com/classgraph/classgraph/releases). (Alternatively, you could use `LATEST` in place of `X.Y.Z` instead if you just want to grab the latest version -- although be aware that that may lead to non-reproducible builds, since the ClassGraph version number could increase at any time. You could use [dependency locking](https://docs.gradle.org/current/userguide/dependency_locking.html) to address this.) + ```xml io.github.classgraph classgraph - LATEST + X.Y.Z ``` From c16b94dd7cb2ac6def5906ab87984bc006d5f62a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 4 Feb 2020 08:41:08 -0700 Subject: [PATCH 0567/1778] Organize imports --- .../classloaderhandler/SpringBootRestartClassLoaderHandler.java | 1 - 1 file changed, 1 deletion(-) 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 2beea04f0..806c3a99d 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/SpringBootRestartClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/SpringBootRestartClassLoaderHandler.java @@ -28,7 +28,6 @@ */ package nonapi.io.github.classgraph.classloaderhandler; -import io.github.classgraph.ClassGraph; import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; import nonapi.io.github.classgraph.classpath.ClasspathOrder; import nonapi.io.github.classgraph.scanspec.ScanSpec; From ca63b8c3e2ebdf029a5cf3af5a54dc6a58937ce0 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 4 Feb 2020 08:47:35 -0700 Subject: [PATCH 0568/1778] Allow .getClasspath...() methods to be called before .scan() (#398) --- src/main/java/io/github/classgraph/ClassGraph.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index 9137ef8e9..6dd6365da 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -1163,6 +1163,7 @@ public void scanAsync(final ExecutorService executorService, final int numParall public void run() { try { // Call scanner, but ignore the returned ScanResult + scanSpec.performScan = true; new Scanner(scanSpec, executorService, numParallelTasks, scanResultProcessor, failureHandler, topLevelLog).call(); } catch (final InterruptedException | CancellationException | ExecutionException e) { @@ -1188,6 +1189,7 @@ public void run() { */ public Future scanAsync(final ExecutorService executorService, final int numParallelTasks) { try { + scanSpec.performScan = true; return executorService.submit(new Scanner(scanSpec, executorService, numParallelTasks, /* scanResultProcessor = */ null, /* failureHandler = */ null, topLevelLog)); } catch (final InterruptedException e) { From 3ed5003e5e14af33f94d655ecc757556eaba24b9 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 4 Feb 2020 09:06:32 -0700 Subject: [PATCH 0569/1778] Better fix for #398 (should be able to scan after getting classpath) --- .../java/io/github/classgraph/ClassGraph.java | 79 +++++++++++++++---- .../github/classgraph/ClasspathElement.java | 4 - .../java/io/github/classgraph/ScanResult.java | 9 ++- .../java/io/github/classgraph/Scanner.java | 14 +++- .../SpringBootRestartClassLoaderHandler.java | 2 +- .../github/classgraph/scanspec/ScanSpec.java | 3 - 6 files changed, 80 insertions(+), 31 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index 6dd6365da..655010c41 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -66,7 +66,7 @@ public class ClassGraph { * The default number of worker threads to use while scanning. This number gave the best results on a relatively * modern laptop with SSD, while scanning a large classpath. */ - private static final int DEFAULT_NUM_WORKER_THREADS = Math.max( + static final int DEFAULT_NUM_WORKER_THREADS = Math.max( // Always scan with at least 2 threads 2, // (int) Math.ceil( @@ -1163,9 +1163,8 @@ public void scanAsync(final ExecutorService executorService, final int numParall public void run() { try { // Call scanner, but ignore the returned ScanResult - scanSpec.performScan = true; - new Scanner(scanSpec, executorService, numParallelTasks, scanResultProcessor, failureHandler, - topLevelLog).call(); + new Scanner(scanSpec, /* performScan = */ true, executorService, numParallelTasks, + scanResultProcessor, failureHandler, topLevelLog).call(); } catch (final InterruptedException | CancellationException | ExecutionException e) { // Call failure handler failureHandler.onFailure(e); @@ -1178,7 +1177,9 @@ public void run() { * Asynchronously scans the classpath for matching files, returning a {@code Future}. You should * assign the wrapped {@link ScanResult} in a try-with-resources statement, or manually close it when you are * finished with it. - * + * + * @param performScan + * If true, performing a scan. If false, only fetching the classpath. * @param executorService * A custom {@link ExecutorService} to use for scheduling worker tasks. * @param numParallelTasks @@ -1187,10 +1188,10 @@ public void run() { * @return a {@code Future}, that when resolved using get() yields a new {@link ScanResult} object * representing the result of the scan. */ - public Future scanAsync(final ExecutorService executorService, final int numParallelTasks) { + private Future scanAsync(final boolean performScan, final ExecutorService executorService, + final int numParallelTasks) { try { - scanSpec.performScan = true; - return executorService.submit(new Scanner(scanSpec, executorService, numParallelTasks, + return executorService.submit(new Scanner(scanSpec, performScan, executorService, numParallelTasks, /* scanResultProcessor = */ null, /* failureHandler = */ null, topLevelLog)); } catch (final InterruptedException e) { // Interrupted during the Scanner constructor's execution (specifically, by getModuleOrder(), @@ -1204,6 +1205,23 @@ public ScanResult call() throws Exception { } } + /** + * Asynchronously scans the classpath for matching files, returning a {@code Future}. You should + * assign the wrapped {@link ScanResult} in a try-with-resources statement, or manually close it when you are + * finished with it. + * + * @param executorService + * A custom {@link ExecutorService} to use for scheduling worker tasks. + * @param numParallelTasks + * The number of parallel tasks to break the work into during the most CPU-intensive stage of + * classpath scanning. Ideally the ExecutorService will have at least this many threads available. + * @return a {@code Future}, that when resolved using get() yields a new {@link ScanResult} object + * representing the result of the scan. + */ + public Future scanAsync(final ExecutorService executorService, final int numParallelTasks) { + return scanAsync(/* performScan = */ true, executorService, numParallelTasks); + } + /** * Scans the classpath using the requested {@link ExecutorService} and the requested degree of parallelism, * blocking until the scan is complete. You should assign the returned {@link ScanResult} in a @@ -1284,6 +1302,35 @@ public ScanResult scan() { // ------------------------------------------------------------------------------------------------------------- + /** + * Get a {@link ScanResult} that can be used for determining the classpath. + * + * @param executorService + * The executor service. + * @return a {@link ScanResult} object representing the result of the scan (can only be used for determining + * classpath). + * @throws ClassGraphException + * if any of the worker threads throws an uncaught exception, or the scan was interrupted. + */ + ScanResult getClasspathScanResult(final AutoCloseableExecutorService executorService) { + try { + final ScanResult scanResult = scanAsync(/* performScan = */ false, executorService, + DEFAULT_NUM_WORKER_THREADS).get(); + + // The resulting scanResult cannot be null, but check for null to keep SpotBugs happy + if (scanResult == null) { + throw new NullPointerException(); + } + return scanResult; + + } catch (final InterruptedException | CancellationException e) { + throw ClassGraphException.newClassGraphException("Scan interrupted", e); + } catch (final ExecutionException e) { + throw ClassGraphException.newClassGraphException("Uncaught exception during scan", + InterruptionChecker.getCause(e)); + } + } + /** * Returns the list of all unique File objects representing directories or zip/jarfiles on the classpath, in * classloader resolution order. Classpath elements that do not exist as a file or directory are not included in @@ -1295,8 +1342,8 @@ public ScanResult scan() { * if any of the worker threads throws an uncaught exception, or the scan was interrupted. */ public List getClasspathFiles() { - scanSpec.performScan = false; - try (ScanResult scanResult = scan()) { + try (AutoCloseableExecutorService executorService = new AutoCloseableExecutorService( + DEFAULT_NUM_WORKER_THREADS); ScanResult scanResult = getClasspathScanResult(executorService)) { return scanResult.getClasspathFiles(); } } @@ -1328,8 +1375,8 @@ public String getClasspath() { * if any of the worker threads throws an uncaught exception, or the scan was interrupted. */ public List getClasspathURIs() { - scanSpec.performScan = false; - try (ScanResult scanResult = scan()) { + try (AutoCloseableExecutorService executorService = new AutoCloseableExecutorService( + DEFAULT_NUM_WORKER_THREADS); ScanResult scanResult = getClasspathScanResult(executorService)) { return scanResult.getClasspathURIs(); } } @@ -1344,8 +1391,8 @@ public List getClasspathURIs() { * if any of the worker threads throws an uncaught exception, or the scan was interrupted. */ public List getClasspathURLs() { - scanSpec.performScan = false; - try (ScanResult scanResult = scan()) { + try (AutoCloseableExecutorService executorService = new AutoCloseableExecutorService( + DEFAULT_NUM_WORKER_THREADS); ScanResult scanResult = getClasspathScanResult(executorService)) { return scanResult.getClasspathURLs(); } } @@ -1358,8 +1405,8 @@ public List getClasspathURLs() { * if any of the worker threads throws an uncaught exception, or the scan was interrupted. */ public List getModules() { - scanSpec.performScan = false; - try (ScanResult scanResult = scan()) { + try (AutoCloseableExecutorService executorService = new AutoCloseableExecutorService( + DEFAULT_NUM_WORKER_THREADS); ScanResult scanResult = getClasspathScanResult(executorService)) { return scanResult.getModules(); } } diff --git a/src/main/java/io/github/classgraph/ClasspathElement.java b/src/main/java/io/github/classgraph/ClasspathElement.java index fc8f2942d..3cef3bbc8 100644 --- a/src/main/java/io/github/classgraph/ClasspathElement.java +++ b/src/main/java/io/github/classgraph/ClasspathElement.java @@ -189,10 +189,6 @@ protected void checkResourcePathWhiteBlackList(final String relativePath, final * the log */ void maskClassfiles(final int classpathIdx, final Set classpathRelativePathsFound, final LogNode log) { - if (!scanSpec.performScan) { - // Should not happen - throw new IllegalArgumentException("performScan is false"); - } // Find relative paths that occur more than once in the classpath / module path. // Usually duplicate relative paths occur only between classpath / module path elements, not within, // but actually there is no restriction for paths within a zipfile to be unique, and in fact diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index 80a115b7f..790086742 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -50,6 +50,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import nonapi.io.github.classgraph.concurrency.AutoCloseableExecutorService; import nonapi.io.github.classgraph.fastzipfilereader.NestedJarHandler; import nonapi.io.github.classgraph.json.JSONDeserializer; import nonapi.io.github.classgraph.json.JSONSerializer; @@ -1276,14 +1277,16 @@ public static ScanResult fromJSON(final String json) { // and scans classpath element paths (needed for classloading), but does not scan the actual classfiles final ClassGraph classGraph = new ClassGraph(); classGraph.scanSpec = deserialized.scanSpec; - classGraph.scanSpec.performScan = false; if (classGraph.scanSpec.overrideClasspath == null) { // Use the same classpath as before, if classpath was not overridden classGraph.overrideClasspath(deserialized.classpath); } - final ScanResult scanResult = classGraph.scan(); + final ScanResult scanResult; + try (AutoCloseableExecutorService executorService = new AutoCloseableExecutorService( + ClassGraph.DEFAULT_NUM_WORKER_THREADS)) { + scanResult = classGraph.getClasspathScanResult(executorService); + } scanResult.rawClasspathEltOrderStrs = deserialized.classpath; - scanResult.scanSpec.performScan = true; // Set the fields related to ClassInfo in the new ScanResult, based on the deserialized JSON scanResult.scanSpec = deserialized.scanSpec; diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 1f246a181..77d0c5da4 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -80,6 +80,9 @@ class Scanner implements Callable { /** The scan spec. */ private final ScanSpec scanSpec; + /** If true, performing a scan. If false, only fetching the classpath. */ + public boolean performScan; + /** The nested jar handler. */ private final NestedJarHandler nestedJarHandler; @@ -117,6 +120,8 @@ class Scanner implements Callable { * * @param scanSpec * the scan spec + * @param performScan + * If true, performing a scan. If false, only fetching the classpath. * @param executorService * the executor service * @param numParallelTasks @@ -130,10 +135,11 @@ class Scanner implements Callable { * @throws InterruptedException * if interrupted */ - Scanner(final ScanSpec scanSpec, final ExecutorService executorService, final int numParallelTasks, - final ScanResultProcessor scanResultProcessor, final FailureHandler failureHandler, - final LogNode topLevelLog) throws InterruptedException { + Scanner(final ScanSpec scanSpec, final boolean performScan, final ExecutorService executorService, + final int numParallelTasks, final ScanResultProcessor scanResultProcessor, + final FailureHandler failureHandler, final LogNode topLevelLog) throws InterruptedException { this.scanSpec = scanSpec; + this.performScan = performScan; scanSpec.sortPrefixes(); scanSpec.log(topLevelLog); if (topLevelLog != null) { @@ -1054,7 +1060,7 @@ public void processWorkUnit(final ClasspathElement classpathElement, } } - if (scanSpec.performScan) { + if (performScan) { // Scan classpath / modules, producing a ScanResult. return performScan(finalClasspathEltOrderFiltered, finalClasspathEltOrderStrs, classLoaderOrderRespectingParentDelegation); 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 806c3a99d..ad5f92ebd 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/SpringBootRestartClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/SpringBootRestartClassLoaderHandler.java @@ -74,7 +74,7 @@ public static void findClassLoaderOrder(final ClassLoader classLoader, final Cla // The Restart classloader is a parent-last classloader, so add the Restart classloader itself to the // classloader order first classLoaderOrder.add(classLoader, log); - + // Finally delegate to the parent of the RestartClassLoader classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true, log); } 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 483434c00..037a29d01 100644 --- a/src/main/java/nonapi/io/github/classgraph/scanspec/ScanSpec.java +++ b/src/main/java/nonapi/io/github/classgraph/scanspec/ScanSpec.java @@ -90,9 +90,6 @@ public class ScanSpec { // ------------------------------------------------------------------------------------------------------------- - /** If true, performing a scan. If false, only fetching the classpath. */ - public boolean performScan = true; - /** If true, scan jarfiles. */ public boolean scanJars = true; From 284128dc75db3ac084f7aba3f76d53d9cce592fd Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 4 Feb 2020 09:08:20 -0700 Subject: [PATCH 0570/1778] Change method parameter order --- src/main/java/io/github/classgraph/ClassGraph.java | 4 ++-- src/main/java/io/github/classgraph/Scanner.java | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index 655010c41..aff61f103 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -1163,7 +1163,7 @@ public void scanAsync(final ExecutorService executorService, final int numParall public void run() { try { // Call scanner, but ignore the returned ScanResult - new Scanner(scanSpec, /* performScan = */ true, executorService, numParallelTasks, + new Scanner(/* performScan = */ true, scanSpec, executorService, numParallelTasks, scanResultProcessor, failureHandler, topLevelLog).call(); } catch (final InterruptedException | CancellationException | ExecutionException e) { // Call failure handler @@ -1191,7 +1191,7 @@ public void run() { private Future scanAsync(final boolean performScan, final ExecutorService executorService, final int numParallelTasks) { try { - return executorService.submit(new Scanner(scanSpec, performScan, executorService, numParallelTasks, + return executorService.submit(new Scanner(performScan, scanSpec, executorService, numParallelTasks, /* scanResultProcessor = */ null, /* failureHandler = */ null, topLevelLog)); } catch (final InterruptedException e) { // Interrupted during the Scanner constructor's execution (specifically, by getModuleOrder(), diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 77d0c5da4..03c8166d9 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -117,11 +117,11 @@ class Scanner implements Callable { /** * The classpath scanner. Scanning is started by calling {@link #call()} on this object. - * - * @param scanSpec - * the scan spec + * * @param performScan * If true, performing a scan. If false, only fetching the classpath. + * @param scanSpec + * the scan spec * @param executorService * the executor service * @param numParallelTasks @@ -132,10 +132,11 @@ class Scanner implements Callable { * the failure handler * @param topLevelLog * the log + * * @throws InterruptedException * if interrupted */ - Scanner(final ScanSpec scanSpec, final boolean performScan, final ExecutorService executorService, + Scanner(final boolean performScan, final ScanSpec scanSpec, final ExecutorService executorService, final int numParallelTasks, final ScanResultProcessor scanResultProcessor, final FailureHandler failureHandler, final LogNode topLevelLog) throws InterruptedException { this.scanSpec = scanSpec; From 8085a860ea4cbbc336f3c148fe37a31d52a30202 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 4 Feb 2020 19:53:58 -0700 Subject: [PATCH 0571/1778] [maven-release-plugin] prepare release classgraph-4.8.61 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 07d94e5d3..cd4ee36a9 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.61-SNAPSHOT + 4.8.61 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.56 + classgraph-4.8.61 From ef32debbd2568b1a441155bcfb261096e2ad02c0 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 4 Feb 2020 19:54:05 -0700 Subject: [PATCH 0572/1778] [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 cd4ee36a9..d2acb9ca6 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.61 + 4.8.62-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.61 + classgraph-4.8.56 From 1ae2bfa3fbde1860942757ee32e2eeeaaf6fff98 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 5 Feb 2020 11:01:10 -0700 Subject: [PATCH 0573/1778] Try ScanResult-based classloading only as an absolute last resort (#399) --- .../classgraph/ClassGraphClassLoader.java | 83 +++++++++++-------- .../java/io/github/classgraph/ScanResult.java | 6 +- 2 files changed, 52 insertions(+), 37 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java index 963ace2d0..9eaf2fb9c 100644 --- a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java +++ b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java @@ -32,7 +32,9 @@ import java.io.InputStream; import java.net.URL; import java.nio.ByteBuffer; +import java.util.ArrayList; import java.util.Enumeration; +import java.util.List; import nonapi.io.github.classgraph.utils.JarUtils; @@ -42,6 +44,9 @@ class ClassGraphClassLoader extends ClassLoader { /** The scan result. */ private final ScanResult scanResult; + /** The environment classloader order. */ + private final ClassLoader[] envClassLoaderOrder; + /** * Constructor. * @@ -51,6 +56,7 @@ class ClassGraphClassLoader extends ClassLoader { ClassGraphClassLoader(final ScanResult scanResult) { super(null); this.scanResult = scanResult; + this.envClassLoaderOrder = scanResult.getClassLoaderOrderRespectingParentDelegation(); registerAsParallelCapable(); } @@ -66,55 +72,62 @@ protected Class findClass(final String className) return loadedClass; } - // Get ClassInfo for named class - final ClassInfo classInfo = scanResult.getClassInfo(className); + // Try null classloader (the classloader that loaded this class, as the caller of Class.forName()) + try { + return Class.forName(className, scanResult.scanSpec.initializeLoadedClasses, null); + } catch (ClassNotFoundException | NoClassDefFoundError e) { + // Ignore + } - // Try environment classloaders first - boolean triedClassInfoLoader = false; - final ClassLoader[] classLoaderOrder = scanResult.getClassLoaderOrderRespectingParentDelegation(); - if (classLoaderOrder != null) { + // Try environment classloaders + final List triedClassLoaders = new ArrayList<>(); + if (envClassLoaderOrder != null) { // Try environment classloaders - for (final ClassLoader envClassLoader : classLoaderOrder) { + for (final ClassLoader envClassLoader : envClassLoaderOrder) { + triedClassLoaders.add(envClassLoader); try { return Class.forName(className, scanResult.scanSpec.initializeLoadedClasses, envClassLoader); } catch (ClassNotFoundException | NoClassDefFoundError e) { // Ignore } - if (classInfo != null && envClassLoader == classInfo.classLoader) { - triedClassInfoLoader = true; - } } } - // Try null classloader (the classloader that loaded this class, as the caller of Class.forName()) - try { - return Class.forName(className, scanResult.scanSpec.initializeLoadedClasses, null); - } catch (ClassNotFoundException | NoClassDefFoundError e) { - // Ignore - } - - // Try specific classloader for the classpath element that the classfile was obtained from - if (!triedClassInfoLoader && classInfo != null && classInfo.classLoader != null) { - try { - return Class.forName(className, scanResult.scanSpec.initializeLoadedClasses, classInfo.classLoader); - } catch (ClassNotFoundException | NoClassDefFoundError e) { - // Ignore + // Try getting the ClassInfo for the named class + final ClassInfo classInfo = scanResult.classNameToClassInfo == null ? null + : scanResult.classNameToClassInfo.get(className); + if (classInfo != null) { + // Try specific classloader for the classpath element that the classfile was obtained from + if (classInfo.classLoader != null && !triedClassLoaders.contains(classInfo.classLoader)) { + try { + return Class.forName(className, scanResult.scanSpec.initializeLoadedClasses, + classInfo.classLoader); + } catch (ClassNotFoundException | NoClassDefFoundError e) { + // Ignore + } } - } - // If class came from a module, and it was not able to be loaded by the environment classloader, - // then it is possible it was a non-public class, and ClassGraph found it by ignoring class visibility - // when reading the resources in exported packages directly. Force ClassGraph to respect JPMS - // encapsulation rules by refusing to load modular classes that the context/system classloaders - // could not load. (A SecurityException should be thrown above, but this is here for completeness.) - if (classInfo != null && classInfo.classpathElement instanceof ClasspathElementModule - && !classInfo.isPublic()) { - throw new ClassNotFoundException("Classfile for class " + className + " was found in a module, " - + "but the context and system classloaders could not load the class, probably because " - + "the class is not public."); + // If class came from a module, and it was not able to be loaded by the environment classloader, + // then it is probable it was a non-public class, and ClassGraph found it by ignoring class visibility + // when reading the resources in exported packages directly. Force ClassGraph to respect JPMS + // encapsulation rules by refusing to load modular classes that the context/system classloaders + // could not load. (A SecurityException should be thrown above, but this is here for completeness.) + if (classInfo.classpathElement instanceof ClasspathElementModule && !classInfo.isPublic()) { + throw new ClassNotFoundException("Classfile for class " + className + " was found in a module, " + + "but the context and system classloaders could not load the class, probably because " + + "the class is not public."); + } } - // Try obtaining the classfile as a resource, and defining the class from the resource content + // Try obtaining the classfile as a resource, and defining the class from the resource content. + // This is a last-ditch attempt if the environment classloader(s) failed. This should be performed + // after envirnoment classloading is attempted, so that classes are not loaded by a mix of environment + // classloaders and direct manual classloading, otherwise class compatibility issues can arise. + // Also, the scanResult should only be accessed as a last resort, so that wherever possible, linked + // classes can be loaded after the ScanResult is closed. Otherwise if you load classes before a + // ScanResult is closed, then you close the ScanResult, then you access fields of the ScanResult + // with a type that has not yet been loaded, this can trigger an exception that the ScanResult + // was accessed after the ScanResult is closed (#399). final ResourceList classfileResources = scanResult .getResourcesWithPath(JarUtils.classNameToClassfilePath(className)); if (classfileResources != null) { diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index 790086742..5f295472f 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -1389,8 +1389,10 @@ public void close() { } classGraphClassLoader = null; if (classNameToClassInfo != null) { - 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(); From 3b8dfe27d455f61d1de7abea9d9522d282951f43 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 5 Feb 2020 16:53:41 -0700 Subject: [PATCH 0574/1778] Use override classloaders if set, then try classpath URLs (#399) --- .../classgraph/ClassGraphClassLoader.java | 74 +++++++++++++++---- 1 file changed, 58 insertions(+), 16 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java index 9eaf2fb9c..6bdf208fc 100644 --- a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java +++ b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java @@ -31,11 +31,13 @@ import java.io.IOException; import java.io.InputStream; import java.net.URL; +import java.net.URLClassLoader; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; +import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.JarUtils; /** {@link ClassLoader} for classes found by ClassGraph during scanning. */ @@ -47,6 +49,9 @@ class ClassGraphClassLoader extends ClassLoader { /** The environment classloader order. */ private final ClassLoader[] envClassLoaderOrder; + /** The classpath URLs. */ + private final URLClassLoader classpathLoader; + /** * Constructor. * @@ -57,6 +62,7 @@ class ClassGraphClassLoader extends ClassLoader { super(null); this.scanResult = scanResult; this.envClassLoaderOrder = scanResult.getClassLoaderOrderRespectingParentDelegation(); + this.classpathLoader = new URLClassLoader(scanResult.getClasspathURLs().toArray(new URL[0])); registerAsParallelCapable(); } @@ -72,27 +78,64 @@ protected Class findClass(final String className) return loadedClass; } - // Try null classloader (the classloader that loaded this class, as the caller of Class.forName()) - try { - return Class.forName(className, scanResult.scanSpec.initializeLoadedClasses, null); - } catch (ClassNotFoundException | NoClassDefFoundError e) { - // Ignore - } - - // Try environment classloaders + // Only try environment classloaders if classpath and/or classloaders are not overridden + final ScanSpec scanSpec = scanResult.scanSpec; final List triedClassLoaders = new ArrayList<>(); - if (envClassLoaderOrder != null) { + if ((scanSpec.overrideClasspath == null || scanSpec.overrideClasspath.isEmpty()) + && (scanSpec.overrideClassLoaders == null || scanSpec.overrideClassLoaders.isEmpty())) { + // Try null classloader (the classloader that loaded this class, as the caller of Class.forName()) + try { + return Class.forName(className, scanSpec.initializeLoadedClasses, null); + } catch (ClassNotFoundException | LinkageError e) { + // Ignore + } + // Try environment classloaders - for (final ClassLoader envClassLoader : envClassLoaderOrder) { - triedClassLoaders.add(envClassLoader); + if (envClassLoaderOrder != null) { + // Try environment classloaders + for (final ClassLoader envClassLoader : envClassLoaderOrder) { + triedClassLoaders.add(envClassLoader); + try { + return Class.forName(className, scanSpec.initializeLoadedClasses, envClassLoader); + } catch (ClassNotFoundException | LinkageError e) { + // Ignore + } + } + } + } + + // If classloaders are overridden or added, try loading through those classloaders + if (scanSpec.overrideClassLoaders != null && !scanSpec.overrideClassLoaders.isEmpty()) { + for (final ClassLoader overrideClassLoader : scanSpec.overrideClassLoaders) { + triedClassLoaders.add(overrideClassLoader); + try { + return overrideClassLoader.loadClass(className); + } catch (ClassNotFoundException | LinkageError e) { + // Ignore + } + } + } + if (scanSpec.addedClassLoaders != null && !scanSpec.addedClassLoaders.isEmpty()) { + for (final ClassLoader addedClassLoader : scanSpec.addedClassLoaders) { + triedClassLoaders.add(addedClassLoader); try { - return Class.forName(className, scanResult.scanSpec.initializeLoadedClasses, envClassLoader); - } catch (ClassNotFoundException | NoClassDefFoundError e) { + return addedClassLoader.loadClass(className); + } catch (ClassNotFoundException | LinkageError e) { // Ignore } } } + // Try loading from classpath URLs. This should handle classpath override situations, and this will also + // enable classloading after the ScanResult has been closed in most situations (#399). Some of these URLs + // might be invalid though if the ScanResult has been closed (e.g. in the rare case that an inner jar + // had to be extracted to a temporary file on disk). + try { + return classpathLoader.loadClass(className); + } catch (ClassNotFoundException | LinkageError e) { + // Ignore + } + // Try getting the ClassInfo for the named class final ClassInfo classInfo = scanResult.classNameToClassInfo == null ? null : scanResult.classNameToClassInfo.get(className); @@ -100,9 +143,8 @@ protected Class findClass(final String className) // Try specific classloader for the classpath element that the classfile was obtained from if (classInfo.classLoader != null && !triedClassLoaders.contains(classInfo.classLoader)) { try { - return Class.forName(className, scanResult.scanSpec.initializeLoadedClasses, - classInfo.classLoader); - } catch (ClassNotFoundException | NoClassDefFoundError e) { + return Class.forName(className, scanSpec.initializeLoadedClasses, classInfo.classLoader); + } catch (ClassNotFoundException | LinkageError e) { // Ignore } } From 4bc97d33c346f09b6b871a802cd1849a46956bd1 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 5 Feb 2020 20:42:50 -0700 Subject: [PATCH 0575/1778] Tweaks to classloading order (#399) --- .../classgraph/ClassGraphClassLoader.java | 120 ++++++++++-------- .../classgraph/classpath/ClasspathFinder.java | 7 +- 2 files changed, 75 insertions(+), 52 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java index 6bdf208fc..43f1ee7fc 100644 --- a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java +++ b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java @@ -33,9 +33,9 @@ import java.net.URL; import java.net.URLClassLoader; import java.nio.ByteBuffer; -import java.util.ArrayList; import java.util.Enumeration; -import java.util.List; +import java.util.HashSet; +import java.util.Set; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.JarUtils; @@ -80,7 +80,7 @@ protected Class findClass(final String className) // Only try environment classloaders if classpath and/or classloaders are not overridden final ScanSpec scanSpec = scanResult.scanSpec; - final List triedClassLoaders = new ArrayList<>(); + final Set triedClassLoaders = new HashSet<>(); if ((scanSpec.overrideClasspath == null || scanSpec.overrideClasspath.isEmpty()) && (scanSpec.overrideClassLoaders == null || scanSpec.overrideClassLoaders.isEmpty())) { // Try null classloader (the classloader that loaded this class, as the caller of Class.forName()) @@ -94,58 +94,44 @@ protected Class findClass(final String className) if (envClassLoaderOrder != null) { // Try environment classloaders for (final ClassLoader envClassLoader : envClassLoaderOrder) { - triedClassLoaders.add(envClassLoader); - try { - return Class.forName(className, scanSpec.initializeLoadedClasses, envClassLoader); - } catch (ClassNotFoundException | LinkageError e) { - // Ignore + if (triedClassLoaders.add(envClassLoader)) { + try { + return Class.forName(className, scanSpec.initializeLoadedClasses, envClassLoader); + } catch (ClassNotFoundException | LinkageError e) { + // Ignore + } } } } } - // If classloaders are overridden or added, try loading through those classloaders - if (scanSpec.overrideClassLoaders != null && !scanSpec.overrideClassLoaders.isEmpty()) { - for (final ClassLoader overrideClassLoader : scanSpec.overrideClassLoaders) { - triedClassLoaders.add(overrideClassLoader); - try { - return overrideClassLoader.loadClass(className); - } catch (ClassNotFoundException | LinkageError e) { - // Ignore - } - } - } - if (scanSpec.addedClassLoaders != null && !scanSpec.addedClassLoaders.isEmpty()) { - for (final ClassLoader addedClassLoader : scanSpec.addedClassLoaders) { - triedClassLoaders.add(addedClassLoader); - try { - return addedClassLoader.loadClass(className); - } catch (ClassNotFoundException | LinkageError e) { - // Ignore - } + // If the classpath is overridden, try loading class from the classpath URLs (this is done before + // checking classloader overrides, since classpath override takes precedence over classloader + // overrides in the ClasspathFinder class). Some of these URLs might be invalid if the ScanResult + // has been closed (e.g. in the rare case that an inner jar had to be extracted to a temporary file + // on disk). + if (scanSpec.overrideClasspath != null && !scanSpec.overrideClasspath.isEmpty()) { + try { + return classpathLoader.loadClass(className); + } catch (ClassNotFoundException | LinkageError e) { + // Ignore } } - // Try loading from classpath URLs. This should handle classpath override situations, and this will also - // enable classloading after the ScanResult has been closed in most situations (#399). Some of these URLs - // might be invalid though if the ScanResult has been closed (e.g. in the rare case that an inner jar - // had to be extracted to a temporary file on disk). - try { - return classpathLoader.loadClass(className); - } catch (ClassNotFoundException | LinkageError e) { - // Ignore - } - - // Try getting the ClassInfo for the named class + // Try getting the ClassInfo for the named class, then the ClassLoader from the ClassInfo. + // This requires the ScanResult not to have been already closed, so this is only attempted if all the + // above efforts failed (#399). final ClassInfo classInfo = scanResult.classNameToClassInfo == null ? null : scanResult.classNameToClassInfo.get(className); if (classInfo != null) { // Try specific classloader for the classpath element that the classfile was obtained from - if (classInfo.classLoader != null && !triedClassLoaders.contains(classInfo.classLoader)) { - try { - return Class.forName(className, scanSpec.initializeLoadedClasses, classInfo.classLoader); - } catch (ClassNotFoundException | LinkageError e) { - // Ignore + if (classInfo.classLoader != null) { + if (triedClassLoaders.add(classInfo.classLoader)) { + try { + return Class.forName(className, scanSpec.initializeLoadedClasses, classInfo.classLoader); + } catch (ClassNotFoundException | LinkageError e) { + // Ignore + } } } @@ -161,15 +147,49 @@ protected Class findClass(final String className) } } + // If classloaders are overridden or added, try loading through those classloaders + if (scanSpec.overrideClassLoaders != null && !scanSpec.overrideClassLoaders.isEmpty()) { + for (final ClassLoader overrideClassLoader : scanSpec.overrideClassLoaders) { + if (triedClassLoaders.add(overrideClassLoader)) { + try { + return overrideClassLoader.loadClass(className); + } catch (ClassNotFoundException | LinkageError e) { + // Ignore + } + } + } + } + if (scanSpec.addedClassLoaders != null && !scanSpec.addedClassLoaders.isEmpty()) { + for (final ClassLoader addedClassLoader : scanSpec.addedClassLoaders) { + if (triedClassLoaders.add(addedClassLoader)) { + try { + return addedClassLoader.loadClass(className); + } catch (ClassNotFoundException | LinkageError e) { + // Ignore + } + } + } + } + + // If the classpath was not overridden, now that override classloaders have been attempted and failed, + // still try to load the class from the classpath URLs before attempting direct classloading from resources + if (scanSpec.overrideClasspath == null || scanSpec.overrideClasspath.isEmpty()) { + try { + return classpathLoader.loadClass(className); + } catch (ClassNotFoundException | LinkageError e) { + // Ignore + } + } + // Try obtaining the classfile as a resource, and defining the class from the resource content. - // This is a last-ditch attempt if the environment classloader(s) failed. This should be performed - // after envirnoment classloading is attempted, so that classes are not loaded by a mix of environment + // This is a last-ditch attempt if the above efforts all failed. This should be performed after + // envirnoment classloading is attempted, so that classes are not loaded by a mix of environment // classloaders and direct manual classloading, otherwise class compatibility issues can arise. - // Also, the scanResult should only be accessed as a last resort, so that wherever possible, linked - // classes can be loaded after the ScanResult is closed. Otherwise if you load classes before a - // ScanResult is closed, then you close the ScanResult, then you access fields of the ScanResult - // with a type that has not yet been loaded, this can trigger an exception that the ScanResult - // was accessed after the ScanResult is closed (#399). + // The ScanResult should only be accessed (to fetch resources) as a last resort, so that wherever + // possible, linked classes can be loaded after the ScanResult is closed. Otherwise if you load + // classes before a ScanResult is closed, then you close the ScanResult, then you try to access + // fields of the ScanResult that have a type that has not yet been loaded, this can trigger an + // exception that the ScanResult was accessed after it was closed (#399). final ResourceList classfileResources = scanResult .getResourcesWithPath(JarUtils.classNameToClassfilePath(className)); if (classfileResources != null) { 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 f8c2f1cf0..17ce9b991 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java @@ -118,8 +118,11 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { } final LogNode overrideLog = classpathFinderLog == null ? null : classpathFinderLog.log("Overriding classpath with: " + scanSpec.overrideClasspath); - classpathOrder.addClasspathEntries(scanSpec.overrideClasspath, defaultClassLoader, scanSpec, - overrideLog); + classpathOrder.addClasspathEntries(scanSpec.overrideClasspath, + // If the classpath is overridden, the classloader used to load classes is overridden in + // ClassGraphClassLoader by a custom URLClassLoader that loads from the override classpath. + // Just use defaultClassLoader as a placeholder here. + defaultClassLoader, scanSpec, overrideLog); if (overrideLog != null) { overrideLog.log("WARNING: when the classpath is overridden, there is no guarantee that the classes " + "found by classpath scanning will be the same as the classes loaded by the " From 435a77c3ee78b547e953e1ec6b15347fe7af9677 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 5 Feb 2020 20:45:33 -0700 Subject: [PATCH 0576/1778] Update comment --- .../java/io/github/classgraph/ClassGraphClassLoader.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java index 43f1ee7fc..b718a0449 100644 --- a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java +++ b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java @@ -119,8 +119,9 @@ protected Class findClass(final String className) } // Try getting the ClassInfo for the named class, then the ClassLoader from the ClassInfo. - // This requires the ScanResult not to have been already closed, so this is only attempted if all the - // above efforts failed (#399). + // This should still be valid if the ScanResult was closed, since ScanResult#close() leaves + // the classNameToClassInfo map intact, but still, this is only attempted if all the above + // efforts failed, to avoid accessing ClassInfo objects after the ScanResult is closed (#399). final ClassInfo classInfo = scanResult.classNameToClassInfo == null ? null : scanResult.classNameToClassInfo.get(className); if (classInfo != null) { From 8ef5f2fefaff495aabd6be4787149651698eac98 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 7 Feb 2020 15:22:34 -0700 Subject: [PATCH 0577/1778] Make getResource() have the same semantics as findClass() (#399) --- .../classgraph/ClassGraphClassLoader.java | 241 ++++++++++++------ 1 file changed, 158 insertions(+), 83 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java index b718a0449..2ac63fa0c 100644 --- a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java +++ b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java @@ -34,7 +34,7 @@ import java.net.URLClassLoader; import java.nio.ByteBuffer; import java.util.Enumeration; -import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.Set; import nonapi.io.github.classgraph.scanspec.ScanSpec; @@ -46,11 +46,14 @@ class ClassGraphClassLoader extends ClassLoader { /** The scan result. */ private final ScanResult scanResult; - /** The environment classloader order. */ - private final ClassLoader[] envClassLoaderOrder; + /** Whether or not to initialize loaded classes. */ + private final boolean initializeLoadedClasses; - /** The classpath URLs. */ - private final URLClassLoader classpathLoader; + /** The ordered set of environment classloaders to try delegating to. */ + private final Set environmentClassLoaderDelegationOrder; + + /** The ordered set of overridden or added classloaders to try delegating to. */ + private final Set overriddenOrAddedClassLoaderDelegationOrder; /** * Constructor. @@ -60,47 +63,34 @@ class ClassGraphClassLoader extends ClassLoader { */ ClassGraphClassLoader(final ScanResult scanResult) { super(null); - this.scanResult = scanResult; - this.envClassLoaderOrder = scanResult.getClassLoaderOrderRespectingParentDelegation(); - this.classpathLoader = new URLClassLoader(scanResult.getClasspathURLs().toArray(new URL[0])); registerAsParallelCapable(); - } - /* (non-Javadoc) - * @see java.lang.ClassLoader#findClass(java.lang.String) - */ - @Override - protected Class findClass(final String className) - throws ClassNotFoundException, LinkageError, SecurityException { - // Use cached class, if it is already loaded - final Class loadedClass = findLoadedClass(className); - if (loadedClass != null) { - return loadedClass; - } + this.scanResult = scanResult; + final ScanSpec scanSpec = scanResult.scanSpec; + initializeLoadedClasses = scanSpec.initializeLoadedClasses; + + final boolean classpathOverridden = scanSpec.overrideClasspath != null + && !scanSpec.overrideClasspath.isEmpty(); + final boolean classloadersOverridden = scanSpec.overrideClassLoaders != null + && !scanSpec.overrideClassLoaders.isEmpty(); + final boolean clasloadersAdded = scanSpec.addedClassLoaders != null + && !scanSpec.addedClassLoaders.isEmpty(); + + // Uniquified order of classloaders to delegate to + environmentClassLoaderDelegationOrder = new LinkedHashSet<>(); // Only try environment classloaders if classpath and/or classloaders are not overridden - final ScanSpec scanSpec = scanResult.scanSpec; - final Set triedClassLoaders = new HashSet<>(); - if ((scanSpec.overrideClasspath == null || scanSpec.overrideClasspath.isEmpty()) - && (scanSpec.overrideClassLoaders == null || scanSpec.overrideClassLoaders.isEmpty())) { - // Try null classloader (the classloader that loaded this class, as the caller of Class.forName()) - try { - return Class.forName(className, scanSpec.initializeLoadedClasses, null); - } catch (ClassNotFoundException | LinkageError e) { - // Ignore - } + if (!classpathOverridden && !classloadersOverridden) { + // Try the null classloader first (this will default to the context classloader of the class + // that called ClassGraph) + environmentClassLoaderDelegationOrder.add(null); // Try environment classloaders + final ClassLoader[] envClassLoaderOrder = scanResult.getClassLoaderOrderRespectingParentDelegation(); if (envClassLoaderOrder != null) { // Try environment classloaders for (final ClassLoader envClassLoader : envClassLoaderOrder) { - if (triedClassLoaders.add(envClassLoader)) { - try { - return Class.forName(className, scanSpec.initializeLoadedClasses, envClassLoader); - } catch (ClassNotFoundException | LinkageError e) { - // Ignore - } - } + environmentClassLoaderDelegationOrder.add(envClassLoader); } } } @@ -110,11 +100,47 @@ protected Class findClass(final String className) // overrides in the ClasspathFinder class). Some of these URLs might be invalid if the ScanResult // has been closed (e.g. in the rare case that an inner jar had to be extracted to a temporary file // on disk). - if (scanSpec.overrideClasspath != null && !scanSpec.overrideClasspath.isEmpty()) { - try { - return classpathLoader.loadClass(className); - } catch (ClassNotFoundException | LinkageError e) { - // Ignore + final URLClassLoader classpathClassLoader = new URLClassLoader( + scanResult.getClasspathURLs().toArray(new URL[0])); + if (classpathOverridden) { + environmentClassLoaderDelegationOrder.add(classpathClassLoader); + } + + // If classloaders are overridden or added, try loading through those classloaders + overriddenOrAddedClassLoaderDelegationOrder = new LinkedHashSet<>(); + if (classloadersOverridden) { + overriddenOrAddedClassLoaderDelegationOrder.addAll(scanSpec.overrideClassLoaders); + } + if (clasloadersAdded) { + overriddenOrAddedClassLoaderDelegationOrder.addAll(scanSpec.addedClassLoaders); + } + if (!classpathOverridden) { + // If the classpath was not overridden, now that override classloaders have been attempted and failed, + // try to load the class from the classpath URLs before attempting direct classloading from resources + overriddenOrAddedClassLoaderDelegationOrder.add(classpathClassLoader); + } + } + + /* (non-Javadoc) + * @see java.lang.ClassLoader#findClass(java.lang.String) + */ + @Override + protected Class findClass(final String className) + throws ClassNotFoundException, LinkageError, SecurityException { + // Use cached class, if it is already loaded + final Class loadedClass = findLoadedClass(className); + if (loadedClass != null) { + return loadedClass; + } + + // Try environment classloader(s) + if (!environmentClassLoaderDelegationOrder.isEmpty()) { + for (final ClassLoader envClassLoader : environmentClassLoaderDelegationOrder) { + try { + return Class.forName(className, initializeLoadedClasses, envClassLoader); + } catch (ClassNotFoundException | LinkageError e) { + // Ignore + } } } @@ -125,14 +151,14 @@ protected Class findClass(final String className) final ClassInfo classInfo = scanResult.classNameToClassInfo == null ? null : scanResult.classNameToClassInfo.get(className); if (classInfo != null) { - // Try specific classloader for the classpath element that the classfile was obtained from - if (classInfo.classLoader != null) { - if (triedClassLoaders.add(classInfo.classLoader)) { - try { - return Class.forName(className, scanSpec.initializeLoadedClasses, classInfo.classLoader); - } catch (ClassNotFoundException | LinkageError e) { - // Ignore - } + // Try specific classloader for the classpath element that the classfile was obtained from, + // as long as it wasn't already tried + if (classInfo.classLoader != null + && !environmentClassLoaderDelegationOrder.contains(classInfo.classLoader)) { + try { + return Class.forName(className, initializeLoadedClasses, classInfo.classLoader); + } catch (ClassNotFoundException | LinkageError e) { + // Ignore } } @@ -148,43 +174,20 @@ protected Class findClass(final String className) } } - // If classloaders are overridden or added, try loading through those classloaders - if (scanSpec.overrideClassLoaders != null && !scanSpec.overrideClassLoaders.isEmpty()) { - for (final ClassLoader overrideClassLoader : scanSpec.overrideClassLoaders) { - if (triedClassLoaders.add(overrideClassLoader)) { - try { - return overrideClassLoader.loadClass(className); - } catch (ClassNotFoundException | LinkageError e) { - // Ignore - } - } - } - } - if (scanSpec.addedClassLoaders != null && !scanSpec.addedClassLoaders.isEmpty()) { - for (final ClassLoader addedClassLoader : scanSpec.addedClassLoaders) { - if (triedClassLoaders.add(addedClassLoader)) { - try { - return addedClassLoader.loadClass(className); - } catch (ClassNotFoundException | LinkageError e) { - // Ignore - } + // Try overridden or added classloader(s) + if (!overriddenOrAddedClassLoaderDelegationOrder.isEmpty()) { + for (final ClassLoader additionalClassLoader : overriddenOrAddedClassLoaderDelegationOrder) { + try { + return Class.forName(className, initializeLoadedClasses, additionalClassLoader); + } catch (ClassNotFoundException | LinkageError e) { + // Ignore } } } - // If the classpath was not overridden, now that override classloaders have been attempted and failed, - // still try to load the class from the classpath URLs before attempting direct classloading from resources - if (scanSpec.overrideClasspath == null || scanSpec.overrideClasspath.isEmpty()) { - try { - return classpathLoader.loadClass(className); - } catch (ClassNotFoundException | LinkageError e) { - // Ignore - } - } - - // Try obtaining the classfile as a resource, and defining the class from the resource content. - // This is a last-ditch attempt if the above efforts all failed. This should be performed after - // envirnoment classloading is attempted, so that classes are not loaded by a mix of environment + // As a last-ditch attempt, if the above efforts all failed, try obtaining the classfile as a + // resource, and define the class from the resource content. This should be performed after + // environment classloading is attempted, so that classes are not loaded by a mix of environment // classloaders and direct manual classloading, otherwise class compatibility issues can arise. // The ScanResult should only be accessed (to fetch resources) as a last resort, so that wherever // possible, linked classes can be loaded after the ScanResult is closed. Otherwise if you load @@ -215,6 +218,30 @@ protected Class findClass(final String className) */ @Override public URL getResource(final String path) { + // This order should match the order in findClass(String) + + // Try loading resource from environment classloader(s) + if (!environmentClassLoaderDelegationOrder.isEmpty()) { + for (final ClassLoader envClassLoader : environmentClassLoaderDelegationOrder) { + final URL resource = envClassLoader.getResource(path); + if (resource != null) { + return resource; + } + } + } + + // Try loading resource from overridden or added classloader(s) + if (!overriddenOrAddedClassLoaderDelegationOrder.isEmpty()) { + for (final ClassLoader additionalClassLoader : overriddenOrAddedClassLoaderDelegationOrder) { + final URL resource = additionalClassLoader.getResource(path); + if (resource != null) { + return resource; + } + } + } + + // Finally 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()) { return super.getResource(path); @@ -228,6 +255,30 @@ public URL getResource(final String path) { */ @Override public Enumeration getResources(final String path) throws IOException { + // This order should match the order in findClass(String) + + // Try loading resources from environment classloader(s) + if (!environmentClassLoaderDelegationOrder.isEmpty()) { + for (final ClassLoader envClassLoader : environmentClassLoaderDelegationOrder) { + final Enumeration resources = envClassLoader.getResources(path); + if (resources != null) { + return resources; + } + } + } + + // Try loading resources from overridden or added classloader(s) + if (!overriddenOrAddedClassLoaderDelegationOrder.isEmpty()) { + for (final ClassLoader additionalClassLoader : overriddenOrAddedClassLoaderDelegationOrder) { + final Enumeration resources = additionalClassLoader.getResources(path); + if (resources != null) { + return resources; + } + } + } + + // Finally 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()) { return super.getResources(path); @@ -254,6 +305,30 @@ public URL nextElement() { */ @Override public InputStream getResourceAsStream(final String path) { + // This order should match the order in findClass(String) + + // Try opening resource from environment classloader(s) + if (!environmentClassLoaderDelegationOrder.isEmpty()) { + for (final ClassLoader envClassLoader : environmentClassLoaderDelegationOrder) { + final InputStream inputStream = envClassLoader.getResourceAsStream(path); + if (inputStream != null) { + return inputStream; + } + } + } + + // Try opening resource from overridden or added classloader(s) + if (!overriddenOrAddedClassLoaderDelegationOrder.isEmpty()) { + for (final ClassLoader additionalClassLoader : overriddenOrAddedClassLoaderDelegationOrder) { + final InputStream inputStream = additionalClassLoader.getResourceAsStream(path); + if (inputStream != null) { + return inputStream; + } + } + } + + // Finally 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()) { return super.getResourceAsStream(path); From 6b557978c2f33c45d5b4244102e0bd9e7bb2a208 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 7 Feb 2020 15:25:05 -0700 Subject: [PATCH 0578/1778] [maven-release-plugin] prepare release classgraph-4.8.62 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index d2acb9ca6..f775a0065 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.62-SNAPSHOT + 4.8.62 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.56 + classgraph-4.8.62 From 1cf94eb81b86d08450d487161af863f5b5f36d17 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 7 Feb 2020 15:25:12 -0700 Subject: [PATCH 0579/1778] [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 f775a0065..120ddfd16 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.62 + 4.8.63-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.62 + classgraph-4.8.56 From 32844668e7b153dabf06de1b79609420adb81736 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 7 Feb 2020 15:28:01 -0700 Subject: [PATCH 0580/1778] Bump version number back down --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 120ddfd16..d2acb9ca6 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.63-SNAPSHOT + 4.8.62-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 5bb8051cfe6f862f1a72935b728863394c12911a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 7 Feb 2020 15:31:46 -0700 Subject: [PATCH 0581/1778] Fix getResources() semantics --- .../java/io/github/classgraph/ClassGraphClassLoader.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java index 2ac63fa0c..05ec7e89d 100644 --- a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java +++ b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java @@ -33,6 +33,7 @@ import java.net.URL; import java.net.URLClassLoader; import java.nio.ByteBuffer; +import java.util.Collections; import java.util.Enumeration; import java.util.LinkedHashSet; import java.util.Set; @@ -261,7 +262,7 @@ public Enumeration getResources(final String path) throws IOException { if (!environmentClassLoaderDelegationOrder.isEmpty()) { for (final ClassLoader envClassLoader : environmentClassLoaderDelegationOrder) { final Enumeration resources = envClassLoader.getResources(path); - if (resources != null) { + if (resources != null && resources.hasMoreElements()) { return resources; } } @@ -271,7 +272,7 @@ public Enumeration getResources(final String path) throws IOException { if (!overriddenOrAddedClassLoaderDelegationOrder.isEmpty()) { for (final ClassLoader additionalClassLoader : overriddenOrAddedClassLoaderDelegationOrder) { final Enumeration resources = additionalClassLoader.getResources(path); - if (resources != null) { + if (resources != null && resources.hasMoreElements()) { return resources; } } @@ -281,7 +282,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 super.getResources(path); + return Collections. emptyEnumeration(); } else { return new Enumeration() { /** The idx. */ From 2df6e0134b2bcdeee4025914764db3d58c5f41a2 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 7 Feb 2020 15:32:19 -0700 Subject: [PATCH 0582/1778] [maven-release-plugin] prepare release classgraph-4.8.62 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index d2acb9ca6..f775a0065 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.62-SNAPSHOT + 4.8.62 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.56 + classgraph-4.8.62 From 17fb0f9405fce1eb98be03689d5d45b9b1a6038e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 7 Feb 2020 15:32:26 -0700 Subject: [PATCH 0583/1778] [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 f775a0065..120ddfd16 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.62 + 4.8.63-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.62 + classgraph-4.8.56 From 62b322b3ffef325719c14b695081f59a4454c4fe Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 7 Feb 2020 15:48:18 -0700 Subject: [PATCH 0584/1778] Small optimizations to avoid duplicating work --- .../github/classgraph/ClassGraphClassLoader.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java index 05ec7e89d..9c99c4f31 100644 --- a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java +++ b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java @@ -120,6 +120,8 @@ class ClassGraphClassLoader extends ClassLoader { // try to load the class from the classpath URLs before attempting direct classloading from resources overriddenOrAddedClassLoaderDelegationOrder.add(classpathClassLoader); } + // Remove duplicates + overriddenOrAddedClassLoaderDelegationOrder.removeAll(environmentClassLoaderDelegationOrder); } /* (non-Javadoc) @@ -149,9 +151,11 @@ protected Class findClass(final String className) // This should still be valid if the ScanResult was closed, since ScanResult#close() leaves // the classNameToClassInfo map intact, but still, this is only attempted if all the above // efforts failed, to avoid accessing ClassInfo objects after the ScanResult is closed (#399). + ClassLoader classInfoClassLoader = null; final ClassInfo classInfo = scanResult.classNameToClassInfo == null ? null : scanResult.classNameToClassInfo.get(className); if (classInfo != null) { + classInfoClassLoader = classInfo.classLoader; // Try specific classloader for the classpath element that the classfile was obtained from, // as long as it wasn't already tried if (classInfo.classLoader != null @@ -178,10 +182,12 @@ protected Class findClass(final String className) // Try overridden or added classloader(s) if (!overriddenOrAddedClassLoaderDelegationOrder.isEmpty()) { for (final ClassLoader additionalClassLoader : overriddenOrAddedClassLoaderDelegationOrder) { - try { - return Class.forName(className, initializeLoadedClasses, additionalClassLoader); - } catch (ClassNotFoundException | LinkageError e) { - // Ignore + if (additionalClassLoader != classInfoClassLoader) { + try { + return Class.forName(className, initializeLoadedClasses, additionalClassLoader); + } catch (ClassNotFoundException | LinkageError e) { + // Ignore + } } } } From 8b385563b30152604614e5f6660fa3cfc5461dbc Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 7 Feb 2020 18:48:13 -0700 Subject: [PATCH 0585/1778] Small code cleanup --- .../java/io/github/classgraph/ClassGraphClassLoader.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java index 9c99c4f31..50b153bce 100644 --- a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java +++ b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java @@ -158,10 +158,10 @@ protected Class findClass(final String className) classInfoClassLoader = classInfo.classLoader; // Try specific classloader for the classpath element that the classfile was obtained from, // as long as it wasn't already tried - if (classInfo.classLoader != null - && !environmentClassLoaderDelegationOrder.contains(classInfo.classLoader)) { + if (classInfoClassLoader != null + && !environmentClassLoaderDelegationOrder.contains(classInfoClassLoader)) { try { - return Class.forName(className, initializeLoadedClasses, classInfo.classLoader); + return Class.forName(className, initializeLoadedClasses, classInfoClassLoader); } catch (ClassNotFoundException | LinkageError e) { // Ignore } From 037983ee0a876a77b0b26050a00558d35d965d90 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 8 Feb 2020 10:05:50 -0700 Subject: [PATCH 0586/1778] Trim 64MB buffer if jar is smaller; use InputStream size hint (#400) --- .../MappedByteBufferResources.java | 68 +++++++++++++------ .../fastzipfilereader/NestedJarHandler.java | 7 +- .../fastzipfilereader/PhysicalZipFile.java | 9 ++- 3 files changed, 61 insertions(+), 23 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java index 8d2085854..ae193fa78 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java @@ -42,6 +42,7 @@ import java.nio.channels.NonReadableChannelException; import java.nio.file.Files; import java.nio.file.StandardOpenOption; +import java.util.Arrays; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReferenceArray; @@ -91,6 +92,8 @@ public class MappedByteBufferResources { * * @param inputStream * The {@link InputStream}. + * @param inputStreamLengthHint + * The number of bytes to read in inputStream, or -1 if unknown. * @param tempFileBaseName * the source URL or zip entry that inputStream was opened from (used to name temporary file, if * needed). @@ -101,27 +104,49 @@ public class MappedByteBufferResources { * @throws IOException * If the contents could not be read. */ - public MappedByteBufferResources(final InputStream inputStream, final String tempFileBaseName, - final NestedJarHandler nestedJarHandler, final LogNode log) throws IOException { + public MappedByteBufferResources(final InputStream inputStream, final int inputStreamLengthHint, + final String tempFileBaseName, final NestedJarHandler nestedJarHandler, final LogNode log) + throws IOException { this.nestedJarHandler = nestedJarHandler; - final byte[] buf = new byte[MAX_JAR_RAM_SIZE]; - final int bufLength = buf.length; - - int totBytesRead = 0; - int bytesRead = 0; - while ((bytesRead = inputStream.read(buf, totBytesRead, bufLength - totBytesRead)) > 0) { - // Fill buffer until nothing more can be read - totBytesRead += bytesRead; - } - if (bytesRead < 0) { - // Successfully reached end of stream -- wrap array buffer with ByteBuffer - wrapByteBuffer(ByteBuffer.wrap(buf, 0, totBytesRead)); - + byte[] buf; + boolean spillToDisk = false; + if (inputStreamLengthHint != -1 && inputStreamLengthHint <= MAX_JAR_RAM_SIZE) { + // inputStreamLengthHint indicates that inputStream is longer than MAX_JAR_RAM_SIZE, + // so try downloading to RAM + buf = new byte[inputStreamLengthHint == -1 ? MAX_JAR_RAM_SIZE + : Math.min(MAX_JAR_RAM_SIZE, inputStreamLengthHint)]; + final int bufLength = buf.length; + + int totBytesRead = 0; + int bytesRead = 0; + while ((bytesRead = inputStream.read(buf, totBytesRead, bufLength - totBytesRead)) > 0) { + // Fill buffer until nothing more can be read + totBytesRead += bytesRead; + } + if (bytesRead < 0) { + // Successfully reached end of stream -- wrap array buffer with ByteBuffer + if (totBytesRead < buf.length) { + // Trim array + buf = Arrays.copyOf(buf, totBytesRead); + } + // Wrap array in a RAM-backed ByteBuffer + wrapByteBuffer(ByteBuffer.wrap(buf, 0, totBytesRead)); + } else { + // Didn't reach end of inputStream after buf was filled, so inputStreamLengthHint underestimated + // the length of the stream -- spill to disk, since we don't know how long the stream is now + spillToDisk = true; + } } else { + // inputStreamLengthHint indicates that inputStream is longer than MAX_JAR_RAM_SIZE, + // so immediately spill to disk + buf = null; + spillToDisk = true; + } + if (spillToDisk) { // bytesRead == 0 => ran out of buffer space, spill over to disk if (log != null) { - log.log("Could not fit downloaded URL into max RAM buffer size of " + MAX_JAR_RAM_SIZE - + " bytes, downloading to temporary file: " + tempFileBaseName + " -> " + this.mappedFile); + log.log("Could not fit InputStream content into max RAM buffer size of " + MAX_JAR_RAM_SIZE + + " bytes, saving to temporary file: " + tempFileBaseName + " -> " + this.mappedFile); } try { this.mappedFile = nestedJarHandler.makeTempFile(tempFileBaseName, /* onlyUseLeafname = */ true); @@ -133,8 +158,13 @@ public MappedByteBufferResources(final InputStream inputStream, final String tem } this.mappedFileIsTempFile = true; - // Write the full buffer to the temporary file - Files.write(this.mappedFile.toPath(), buf, StandardOpenOption.WRITE); + if (buf != null) { + // If any content was already read from inputStream, flush it out to the temporary file + Files.write(this.mappedFile.toPath(), buf, StandardOpenOption.WRITE); + } else { + // Buffer was never allocated -- allocate one for the copy operation below + buf = new byte[8192]; + } // Copy the rest of the InputStream to the end of the temporary file try (OutputStream os = new BufferedOutputStream( 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 7a5a81ae4..96ffcb09f 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java @@ -123,6 +123,10 @@ public ZipFileSlice newInstance(final FastZipEntry childZipEntry, final LogNode // 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.open(), + childZipEntry.uncompressedSize > 0L + && childZipEntry.uncompressedSize < FileUtils.MAX_BUFFER_SIZE + ? (int) childZipEntry.uncompressedSize + : -1, childZipEntry.entryName, NestedJarHandler.this, log); allocatedPhysicalZipFiles.add(physicalZipFile); @@ -532,7 +536,8 @@ private PhysicalZipFile downloadJarFromURL(final String jarURL, final LogNode lo } try (InputStream inputStream = url.openStream()) { // 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, jarURL, this, log); + final PhysicalZipFile physicalZipFile = new PhysicalZipFile(inputStream, /* length unknown */ -1, + jarURL, this, log); allocatedPhysicalZipFiles.add(physicalZipFile); return physicalZipFile; 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 dbfcb6386..809a442e2 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/PhysicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/PhysicalZipFile.java @@ -118,6 +118,8 @@ class PhysicalZipFile implements Closeable { * * @param inputStream * the input stream + * @param inputStreamLengthHint + * The number of bytes to read in inputStream, or -1 if unknown. * @param path * the source URL the InputStream was opened from, or the zip entry path of this entry in the parent * zipfile @@ -128,14 +130,15 @@ class PhysicalZipFile implements Closeable { * @throws IOException * if an I/O exception occurs. */ - PhysicalZipFile(final InputStream inputStream, final String path, final NestedJarHandler nestedJarHandler, - final LogNode log) throws IOException { + PhysicalZipFile(final InputStream inputStream, final int inputStreamLengthHint, final String path, + final NestedJarHandler nestedJarHandler, final LogNode log) throws IOException { this.nestedJarHandler = nestedJarHandler; this.path = path; this.isDeflatedToRam = true; // Wrap the ByteBuffer - this.byteBufferResources = new MappedByteBufferResources(inputStream, path, nestedJarHandler, log); + this.byteBufferResources = new MappedByteBufferResources(inputStream, inputStreamLengthHint, path, + nestedJarHandler, log); if (this.byteBufferResources.length() == 0L) { throw new IOException("Zipfile is empty: " + path); } From 2161e910a82cf28be98f2c5db6d51128df117d83 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 8 Feb 2020 10:06:37 -0700 Subject: [PATCH 0587/1778] Small code cleanup --- .../fastzipfilereader/MappedByteBufferResources.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java index ae193fa78..87f5a70c0 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java @@ -109,7 +109,7 @@ public MappedByteBufferResources(final InputStream inputStream, final int inputS throws IOException { this.nestedJarHandler = nestedJarHandler; byte[] buf; - boolean spillToDisk = false; + boolean spillToDisk; if (inputStreamLengthHint != -1 && inputStreamLengthHint <= MAX_JAR_RAM_SIZE) { // inputStreamLengthHint indicates that inputStream is longer than MAX_JAR_RAM_SIZE, // so try downloading to RAM @@ -131,6 +131,7 @@ public MappedByteBufferResources(final InputStream inputStream, final int inputS } // Wrap array in a RAM-backed ByteBuffer wrapByteBuffer(ByteBuffer.wrap(buf, 0, totBytesRead)); + spillToDisk = false; } else { // Didn't reach end of inputStream after buf was filled, so inputStreamLengthHint underestimated // the length of the stream -- spill to disk, since we don't know how long the stream is now From 0e944e0c291e00348cbd83a360bfd54d50276d7b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 8 Feb 2020 10:27:48 -0700 Subject: [PATCH 0588/1778] Add `ClassGraph#setMaxBufferedJarRAMSize(int)` (#400) --- .../java/io/github/classgraph/ClassGraph.java | 37 +++++++++++++++++++ .../MappedByteBufferResources.java | 29 +++++++-------- .../fastzipfilereader/NestedJarHandler.java | 4 +- .../fastzipfilereader/PhysicalZipFile.java | 8 +++- .../github/classgraph/scanspec/ScanSpec.java | 27 ++++++++++++++ 5 files changed, 85 insertions(+), 20 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index aff61f103..7432a2bc7 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -29,8 +29,10 @@ package io.github.classgraph; import java.io.File; +import java.io.InputStream; import java.net.URI; import java.net.URL; +import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; @@ -1090,6 +1092,41 @@ public ClassGraph enableSystemJarsAndModules() { return this; } + // ------------------------------------------------------------------------------------------------------------- + + /** + * The maximum size of an inner (nested) jar that has been deflated (i.e. compressed, not stored) within an + * outer jar, before it has to be spilled to disk rather than stored in a RAM-backed {@link ByteBuffer} when it + * is deflated, in order for the inner jar's entries to be read. (Note that this situation of having to deflate + * a nested jar to RAM or disk in order to read it is rare, because normally adding a jarfile to another jarfile + * will store the inner jar, rather than deflate it, because deflating a jarfile does not usually produce any + * further compression gains. If an inner jar is stored, not deflated, then its zip entries can be read directly + * using ClassGraph's own zipfile central directory parser, which can use file slicing to extract entries + * directly from stored nested jars.) + * + *

+ * This is also the maximum size of a jar downloaded from an {@code http://} or {@code https://} classpath + * {@link URL} to RAM. Once this many bytes have been read from the {@link URL}'s {@link InputStream}, then the + * RAM contents are spilled over to a temporary file on disk, and the rest of the content is downloaded to the + * temporary file. (This is also rare, because normally there are no {@code http://} or {@code https://} + * classpath entries.) + * + *

+ * Default: 64MB (i.e. writing to disk is avoided wherever possible). Setting a lower max RAM size value will + * decrease ClassGraph's memory usage if either of the above rare situations occurs. + * + * @param maxBufferedJarRAMSize + * The max RAM size to use for deflated inner jars or downloaded jars. This is the limit per jar, not + * for the whole classpath. + * @return this (for method chaining). + */ + public ClassGraph setMaxBufferedJarRAMSize(final int maxBufferedJarRAMSize) { + scanSpec.maxBufferedJarRAMSize = maxBufferedJarRAMSize; + return this; + } + + // ------------------------------------------------------------------------------------------------------------- + /** * Enables logging by calling {@link #verbose()}, and then sets the logger to "realtime logging mode", where log * entries are written out immediately to stderr, rather than only after the scan has completed. Can help to diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java index 87f5a70c0..93dd9a272 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java @@ -35,7 +35,6 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.RandomAccessFile; -import java.net.URL; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; @@ -48,6 +47,7 @@ import nonapi.io.github.classgraph.concurrency.SingletonMap; import nonapi.io.github.classgraph.concurrency.SingletonMap.NullSingletonException; +import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.FileUtils; import nonapi.io.github.classgraph.utils.LogNode; @@ -80,12 +80,6 @@ public class MappedByteBufferResources { /** Set to true once {@link #close()} has been called. */ private final AtomicBoolean closed = new AtomicBoolean(false); - /** - * The maximum size of a jar that is downloaded from a {@link URL}'s {@link InputStream} to RAM, before the - * content is spilled over to a temporary file on disk. - */ - private static final int MAX_JAR_RAM_SIZE = 64 * 1024 * 1024; - /** * Read all the bytes in an {@link InputStream}, with spillover to a temporary file on disk if a maximum buffer * size is exceeded. @@ -99,22 +93,24 @@ public class MappedByteBufferResources { * needed). * @param nestedJarHandler * the nested jar handler + * @param scanSpec + * the scan spec. * @param log * the log. * @throws IOException * If the contents could not be read. */ public MappedByteBufferResources(final InputStream inputStream, final int inputStreamLengthHint, - final String tempFileBaseName, final NestedJarHandler nestedJarHandler, final LogNode log) - throws IOException { + final String tempFileBaseName, final NestedJarHandler nestedJarHandler, final ScanSpec scanSpec, + final LogNode log) throws IOException { this.nestedJarHandler = nestedJarHandler; byte[] buf; boolean spillToDisk; - if (inputStreamLengthHint != -1 && inputStreamLengthHint <= MAX_JAR_RAM_SIZE) { - // inputStreamLengthHint indicates that inputStream is longer than MAX_JAR_RAM_SIZE, + if (inputStreamLengthHint != -1 && inputStreamLengthHint <= scanSpec.maxBufferedJarRAMSize) { + // inputStreamLengthHint indicates that inputStream is longer than scanSpec.maxJarRamSize, // so try downloading to RAM - buf = new byte[inputStreamLengthHint == -1 ? MAX_JAR_RAM_SIZE - : Math.min(MAX_JAR_RAM_SIZE, inputStreamLengthHint)]; + buf = new byte[inputStreamLengthHint == -1 ? scanSpec.maxBufferedJarRAMSize + : Math.min(scanSpec.maxBufferedJarRAMSize, inputStreamLengthHint)]; final int bufLength = buf.length; int totBytesRead = 0; @@ -138,7 +134,7 @@ public MappedByteBufferResources(final InputStream inputStream, final int inputS spillToDisk = true; } } else { - // inputStreamLengthHint indicates that inputStream is longer than MAX_JAR_RAM_SIZE, + // inputStreamLengthHint indicates that inputStream is longer than scanSpec.maxJarRamSize, // so immediately spill to disk buf = null; spillToDisk = true; @@ -146,8 +142,9 @@ public MappedByteBufferResources(final InputStream inputStream, final int inputS if (spillToDisk) { // bytesRead == 0 => ran out of buffer space, spill over to disk if (log != null) { - log.log("Could not fit InputStream content into max RAM buffer size of " + MAX_JAR_RAM_SIZE - + " bytes, saving to temporary file: " + tempFileBaseName + " -> " + this.mappedFile); + log.log("Could not fit InputStream content into max RAM buffer size of " + + scanSpec.maxBufferedJarRAMSize + " bytes, saving to temporary file: " + tempFileBaseName + + " -> " + this.mappedFile); } try { this.mappedFile = nestedJarHandler.makeTempFile(tempFileBaseName, /* onlyUseLeafname = */ true); 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 96ffcb09f..86f0c6af5 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java @@ -127,7 +127,7 @@ public ZipFileSlice newInstance(final FastZipEntry childZipEntry, final LogNode && childZipEntry.uncompressedSize < FileUtils.MAX_BUFFER_SIZE ? (int) childZipEntry.uncompressedSize : -1, - childZipEntry.entryName, NestedJarHandler.this, log); + childZipEntry.entryName, NestedJarHandler.this, scanSpec, log); allocatedPhysicalZipFiles.add(physicalZipFile); // Create a new logical slice of the extracted inner zipfile @@ -537,7 +537,7 @@ private PhysicalZipFile downloadJarFromURL(final String jarURL, final LogNode lo try (InputStream inputStream = url.openStream()) { // 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, /* length unknown */ -1, - jarURL, this, log); + jarURL, this, scanSpec, log); allocatedPhysicalZipFiles.add(physicalZipFile); return physicalZipFile; 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 809a442e2..f705dd298 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/PhysicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/PhysicalZipFile.java @@ -38,6 +38,7 @@ import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; +import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.FastPathResolver; import nonapi.io.github.classgraph.utils.FileUtils; import nonapi.io.github.classgraph.utils.LogNode; @@ -125,20 +126,23 @@ class PhysicalZipFile implements Closeable { * zipfile * @param nestedJarHandler * the nested jar handler + * @param scanSpec + * the scan spec. * @param log * the log * @throws IOException * if an I/O exception occurs. */ PhysicalZipFile(final InputStream inputStream, final int inputStreamLengthHint, final String path, - final NestedJarHandler nestedJarHandler, final LogNode log) throws IOException { + final NestedJarHandler nestedJarHandler, final ScanSpec scanSpec, final LogNode log) + throws IOException { this.nestedJarHandler = nestedJarHandler; this.path = path; this.isDeflatedToRam = true; // Wrap the ByteBuffer this.byteBufferResources = new MappedByteBufferResources(inputStream, inputStreamLengthHint, path, - nestedJarHandler, log); + nestedJarHandler, scanSpec, log); if (this.byteBufferResources.length() == 0L) { throw new IOException("Zipfile is empty: " + path); } 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 037a29d01..779ed9551 100644 --- a/src/main/java/nonapi/io/github/classgraph/scanspec/ScanSpec.java +++ b/src/main/java/nonapi/io/github/classgraph/scanspec/ScanSpec.java @@ -28,9 +28,11 @@ */ package nonapi.io.github.classgraph.scanspec; +import java.io.InputStream; import java.lang.reflect.Field; import java.net.URI; import java.net.URL; +import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; @@ -228,6 +230,31 @@ public class ScanSpec { // ------------------------------------------------------------------------------------------------------------- + /** + * The maximum size of an inner (nested) jar that has been deflated (i.e. compressed, not stored) within an + * outer jar, before it has to be spilled to disk rather than stored in a RAM-backed {@link ByteBuffer} when it + * is deflated, in order for the inner jar's entries to be read. (Note that this situation of having to deflate + * a nested jar to RAM or disk in order to read it is rare, because normally adding a jarfile to another jarfile + * will store the inner jar, rather than deflate it, because deflating a jarfile does not usually produce any + * further compression gains. If an inner jar is stored, not deflated, then its zip entries can be read directly + * using ClassGraph's own zipfile central directory parser, which can use file slicing to extract entries + * directly from stored nested jars.) + * + *

+ * This is also the maximum size of a jar downloaded from an {@code http://} or {@code https://} classpath + * {@link URL} to RAM. Once this many bytes have been read from the {@link URL}'s {@link InputStream}, then the + * RAM contents are spilled over to a temporary file on disk, and the rest of the content is downloaded to the + * temporary file. (This is also rare, because normally there are no {@code http://} or {@code https://} + * classpath entries.) + * + *

+ * Default: 64MB (i.e. writing to disk is avoided wherever possible). Setting a lower max RAM size value will + * decrease ClassGraph's memory usage if either of the above rare situations occurs. + */ + public int maxBufferedJarRAMSize = 64 * 1024 * 1024; + + // ------------------------------------------------------------------------------------------------------------- + /** Constructor for deserialization. */ public ScanSpec() { // Intentionally empty From 7fa4aca40f80693f371669344f97cf8e699ea24c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 8 Feb 2020 11:08:24 -0700 Subject: [PATCH 0589/1778] Update comment --- .../classgraph/utils/InputStreamOrByteBufferAdapter.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/utils/InputStreamOrByteBufferAdapter.java b/src/main/java/nonapi/io/github/classgraph/utils/InputStreamOrByteBufferAdapter.java index 013951a13..f20d9920e 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/InputStreamOrByteBufferAdapter.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/InputStreamOrByteBufferAdapter.java @@ -57,8 +57,9 @@ public class InputStreamOrByteBufferAdapter implements AutoCloseable { private ByteBuffer byteBuffer; /** - * Bytes read from the beginning of the classfile, for a memory-backed ByteBuffer. This array is reused across - * calls. + * Bytes read from the beginning of the classfile. Only access an index in this array directly if at least that + * many bytes have already been read from the classfile (i.e. can be used to read backwards but not forwards in + * the classfile). */ public byte[] buf; From d49b9b1739cec51f0ff39c699c90571d74dca737 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 8 Feb 2020 16:00:09 -0700 Subject: [PATCH 0590/1778] Remove plugin nature --- .project | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/.project b/.project index b8e1f6a83..0e6b5e6db 100644 --- a/.project +++ b/.project @@ -1,24 +1,23 @@ - ClassGraph - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.m2e.core.maven2Builder - - - - - - org.eclipse.pde.PluginNature - 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 + From 0d0d65c437ec30012fe7ee62dfa2718ea04a7861 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 8 Feb 2020 19:24:29 -0700 Subject: [PATCH 0591/1778] Add disableMemoryMapping() to use RandomAccessFile instead of mmap: #400 --- .../java/io/github/classgraph/ClassGraph.java | 14 + .../classgraph/ClasspathElementDir.java | 59 ++- .../classgraph/ClasspathElementZip.java | 41 +- .../fastzipfilereader/ByteBufferWrapper.java | 388 ++++++++++++++++++ .../fastzipfilereader/FastZipEntry.java | 65 +-- .../MappedByteBufferResources.java | 171 ++++---- .../fastzipfilereader/NestedJarHandler.java | 24 +- .../fastzipfilereader/PhysicalZipFile.java | 17 +- .../fastzipfilereader/ZipFileSliceReader.java | 41 +- .../github/classgraph/scanspec/ScanSpec.java | 9 + .../utils/InputStreamOrByteBufferAdapter.java | 58 +-- 11 files changed, 671 insertions(+), 216 deletions(-) create mode 100644 src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ByteBufferWrapper.java diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index 7432a2bc7..9358f39b6 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -30,9 +30,11 @@ import java.io.File; import java.io.InputStream; +import java.io.RandomAccessFile; import java.net.URI; import java.net.URL; import java.nio.ByteBuffer; +import java.nio.MappedByteBuffer; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; @@ -1125,6 +1127,18 @@ public ClassGraph setMaxBufferedJarRAMSize(final int maxBufferedJarRAMSize) { return this; } + /** + * If true, use a {@link RandomAccessFile} rather than a {@link MappedByteBuffer} to open jarfiles, which is + * slower, but does not use up virtual memory space. You can call this method to disable memory mapping if you + * run into an {@link OutOfMemoryError} when scanning. + * + * @return this (for method chaining). + */ + public ClassGraph disableMemoryMapping() { + scanSpec.disableMemoryMapping = true; + return this; + } + // ------------------------------------------------------------------------------------------------------------- /** diff --git a/src/main/java/io/github/classgraph/ClasspathElementDir.java b/src/main/java/io/github/classgraph/ClasspathElementDir.java index ac8a08f88..a9e74763f 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementDir.java @@ -44,6 +44,7 @@ import nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandlerRegistry; import nonapi.io.github.classgraph.classpath.ClasspathOrder.ClasspathElementAndClassLoader; import nonapi.io.github.classgraph.concurrency.WorkQueue; +import nonapi.io.github.classgraph.fastzipfilereader.ByteBufferWrapper; import nonapi.io.github.classgraph.fastzipfilereader.LogicalZipFile; import nonapi.io.github.classgraph.fastzipfilereader.MappedByteBufferResources; import nonapi.io.github.classgraph.fastzipfilereader.NestedJarHandler; @@ -159,11 +160,16 @@ void open(final WorkQueue workQueue, final int classpath * the classpath resource file * @param nestedJarHandler * the nested jar handler + * @param log + * the log * @return the resource */ private Resource newResource(final String relativePath, final File classpathResourceFile, - final NestedJarHandler nestedJarHandler) { + final NestedJarHandler nestedJarHandler, final LogNode log) { return new Resource(this, classpathResourceFile.length()) { + /** The {@link ByteBufferWrapper}, or null. */ + protected ByteBufferWrapper byteBufferWrapper; + /** The mapped file. */ private MappedByteBufferResources mappedFileResources; @@ -196,15 +202,15 @@ public Set getPosixFilePermissions() { return posixFilePermissions; } - @Override - public synchronized ByteBuffer read() throws IOException { + synchronized ByteBufferWrapper readWrapped() throws IOException { if (skipClasspathElement) { // Shouldn't happen throw new IOException("Parent directory could not be opened"); } markAsOpen(); try { - mappedFileResources = new MappedByteBufferResources(classpathResourceFile, nestedJarHandler); + mappedFileResources = new MappedByteBufferResources(classpathResourceFile, nestedJarHandler, + log); if (mappedFileResources.numChunks() > 1) { // We could provide another method that fetches a chunk other than chunk 0, but the need // to read files larger than 2GB is probably limited (it's not even supported for zipfiles), @@ -213,9 +219,8 @@ public synchronized ByteBuffer read() throws IOException { "File is larger than 2GB -- cannot use read() method, use open() instead"); } // Fetch chunk 0 (the first ~2GB of the file) - byteBuffer = mappedFileResources.getByteBuffer(0); - length = byteBuffer.remaining(); - return byteBuffer; + byteBufferWrapper = mappedFileResources.getByteBuffer(0); + return byteBufferWrapper; } catch (final IOException | SecurityException | OutOfMemoryError e) { close(); throw new IOException("Could not open " + this, e); @@ -227,10 +232,35 @@ public synchronized ByteBuffer read() throws IOException { } } + @Override + public synchronized ByteBuffer read() throws IOException { + if (skipClasspathElement) { + // Shouldn't happen + throw new IOException("Parent directory could not be opened"); + } + markAsOpen(); + try { + readWrapped(); + final ByteBuffer buf = byteBufferWrapper.getByteBuffer(); + if (buf == null) { + throw new IOException("Could not read resource as a ByteBuffer, because memory mapping " + + "of files was disabled, or an OutOfMemoryError occurred while attempting to " + + "map files"); + } + byteBuffer = buf.duplicate(); + byteBuffer.position(0); + length = byteBuffer.remaining(); + return byteBuffer; + } catch (final IOException | SecurityException | OutOfMemoryError e) { + close(); + throw new IOException("Could not open " + this, e); + } + } + @Override synchronized InputStreamOrByteBufferAdapter openOrRead() throws IOException { if (length >= FileUtils.FILECHANNEL_FILE_SIZE_THRESHOLD) { - return new InputStreamOrByteBufferAdapter(read()); + return new InputStreamOrByteBufferAdapter(readWrapped()); } else { return new InputStreamOrByteBufferAdapter(inputStream = new InputStreamResourceCloser(this, Files.newInputStream(classpathResourceFile.toPath()))); @@ -277,6 +307,11 @@ public synchronized byte[] load() throws IOException { @Override public synchronized void close() { super.close(); // Close inputStream + if (byteBufferWrapper != null) { + byteBufferWrapper.close(/* log = */ null); + byteBufferWrapper = null; + byteBuffer = null; + } if (mappedFileResources != null) { mappedFileResources.close(/* log = */ null); mappedFileResources = null; @@ -297,7 +332,8 @@ public synchronized void close() { @Override Resource getResource(final String relativePath) { final File resourceFile = new File(classpathEltDir, relativePath); - return FileUtils.canReadAndIsFile(resourceFile) ? newResource(relativePath, resourceFile, nestedJarHandler) + return FileUtils.canReadAndIsFile(resourceFile) + ? newResource(relativePath, resourceFile, nestedJarHandler, /* log = */ null) : null; } @@ -408,7 +444,8 @@ private void scanDirRecursively(final File dir, final LogNode log) { || (parentMatchStatus == ScanSpecPathMatch.AT_WHITELISTED_CLASS_PACKAGE && scanSpec.classfileIsSpecificallyWhitelisted(fileInDirRelativePath))) { // Resource is whitelisted - final Resource resource = newResource(fileInDirRelativePath, fileInDir, nestedJarHandler); + final Resource resource = newResource(fileInDirRelativePath, fileInDir, nestedJarHandler, + subLog); addWhitelistedResource(resource, parentMatchStatus, /* isClassfileOnly = */ false, subLog); // Save last modified time @@ -424,7 +461,7 @@ private void scanDirRecursively(final File dir, final LogNode log) { // Always check for module descriptor in package root, even if package root isn't in whitelist for (final File fileInDir : filesInDir) { if (fileInDir.getName().equals("module-info.class") && fileInDir.isFile()) { - final Resource resource = newResource("module-info.class", fileInDir, nestedJarHandler); + final Resource resource = newResource("module-info.class", fileInDir, nestedJarHandler, subLog); addWhitelistedResource(resource, parentMatchStatus, /* isClassfileOnly = */ true, subLog); fileToLastModified.put(fileInDir, fileInDir.lastModified()); } diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index f531726e8..c77a3070a 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -46,6 +46,7 @@ import nonapi.io.github.classgraph.classpath.ClasspathOrder.ClasspathElementAndClassLoader; import nonapi.io.github.classgraph.concurrency.SingletonMap.NullSingletonException; import nonapi.io.github.classgraph.concurrency.WorkQueue; +import nonapi.io.github.classgraph.fastzipfilereader.ByteBufferWrapper; import nonapi.io.github.classgraph.fastzipfilereader.FastZipEntry; import nonapi.io.github.classgraph.fastzipfilereader.LogicalZipFile; import nonapi.io.github.classgraph.fastzipfilereader.NestedJarHandler; @@ -290,6 +291,9 @@ void open(final WorkQueue workQueue, final int classpath */ private Resource newResource(final FastZipEntry zipEntry, final String pathRelativeToPackageRoot) { return new Resource(this, zipEntry.uncompressedSize) { + /** The {@link ByteBufferWrapper}, or null. */ + protected ByteBufferWrapper byteBufferWrapper; + /** * Path with package root prefix and/or any Spring Boot prefix ("BOOT-INF/classes/" or * "WEB-INF/classes/") removed. @@ -379,13 +383,31 @@ synchronized InputStreamOrByteBufferAdapter openOrRead() throws IOException { public synchronized ByteBuffer read() throws IOException { try { if (zipEntry.canGetAsSlice()) { - // For STORED entries that do not span multiple 2GB chunks, can create a - // ByteBuffer slice directly from the entry - markAsOpen(); - // compressedSize should have the same value as uncompressedSize for STORED - // entries, but compressedSize is more reliable (uncompressedSize may be -1) - length = zipEntry.compressedSize; - return zipEntry.getAsSlice(); + try { + // For STORED entries that do not span multiple 2GB chunks, can create a + // ByteBuffer slice directly from the entry + markAsOpen(); + // compressedSize should have the same value as uncompressedSize for STORED + // entries, but compressedSize is more reliable (uncompressedSize may be -1) + length = zipEntry.compressedSize; + byteBufferWrapper = zipEntry.getAsSlice(); + byteBuffer = byteBufferWrapper.getByteBuffer(); + if (byteBuffer == null) { + throw new IOException( + "Could not read resource as a ByteBuffer, because memory mapping " + + "of files was disabled, or an OutOfMemoryError occurred while attempting to " + + "map files"); + } + return byteBuffer; + + } catch (final IOException e) { + close(); + throw e; + } catch (final InterruptedException e) { + close(); + nestedJarHandler.interruptionChecker.interrupt(); + throw new IOException(e); + } } else { // Otherwise, decompress or extract the entry into a byte[] array, @@ -418,6 +440,11 @@ public synchronized byte[] load() throws IOException { @Override public synchronized void close() { super.close(); // Close inputStream + if (byteBufferWrapper != null) { + byteBufferWrapper.close(/* log = */ null); + byteBufferWrapper = null; + byteBuffer = null; + } if (byteBuffer != null) { byteBuffer = null; } diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ByteBufferWrapper.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ByteBufferWrapper.java new file mode 100644 index 000000000..85050c9df --- /dev/null +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ByteBufferWrapper.java @@ -0,0 +1,388 @@ +/* + * This file is part of ClassGraph. + * + * Author: Luke Hutchison + * + * Hosted at: https://github.com/classgraph/classgraph + * + * -- + * + * The MIT License (MIT) + * + * Copyright (c) 2020 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.fastzipfilereader; + +import java.io.EOFException; +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.Buffer; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.channels.NonReadableChannelException; + +import nonapi.io.github.classgraph.utils.FileUtils; +import nonapi.io.github.classgraph.utils.LogNode; + +/** + * A wrapper for either {@link ByteBuffer} (backed by an array in RAM or mapped to a file on disk as a + * {@link MappedByteBuffer}), or a {@link RandomAccessFile} (which is slower, but doesn't consume either resident + * RAM or virtual memory address space). + */ +/** + * @author luke + * + */ +/** + * @author luke + * + */ +public final class ByteBufferWrapper { + + /** The {@link ByteBuffer} to use, or if null, use a {@link RandomAccessFile} instead. */ + private ByteBuffer byteBuffer; + + /** True if byteBuffer is a {@link MemoryMappedByteBuffer}. */ + private boolean isMemoryMappedByteBuffer; + + /** The {@link RandomAccessFile} to use, if byteBuffer is null. */ + private RandomAccessFile raf; + + /** The {@link File} to use, if memory mapping is disabled. */ + private File file; + + /** The start position of the {@link RandomAccessFile} slice. */ + private long rafSliceStart; + + /** The limit of the {@link RandomAccessFile} slice. */ + private int rafSliceLength; + + /** The length of the {@link RandomAccessFile}. */ + private long rafLength; + + /** + * Wrap a range of a {@link RandomAccessFile}. + * + * @param file + * the {@link File} to map. + * @param sliceStart + * The starting position of the slice within the file. + * @param sliceLength + * The length of the slice (must be smaller than (2GB - 8B)). + */ + public ByteBufferWrapper(final File file, final long sliceStart, final long sliceLength) throws IOException { + final long fileLen = file.length(); + if (sliceStart < 0 || sliceStart >= fileLen) { + throw new IllegalArgumentException("start out of range: " + sliceLength); + } + if (sliceLength > FileUtils.MAX_BUFFER_SIZE) { + throw new IllegalArgumentException("len > max (" + FileUtils.MAX_BUFFER_SIZE + ")"); + } + if (sliceLength > fileLen - sliceStart) { + throw new IllegalArgumentException("len > " + (fileLen - sliceStart)); + } + this.file = file; + // RandomAccessFile#length() is not threadsafe, so use File.length(): + // https://bugs.java.com/bugdatabase/view_bug.do?bug_id=4823133 + rafLength = file.length(); + raf = new RandomAccessFile(file, "r"); + raf.seek(sliceStart); + rafSliceStart = sliceStart; + rafSliceLength = (int) sliceLength; + } + + /** + * Memory-map a chunk of a file. + * + * @param fileChannel + * a {@link FileChannel} to map. + * @param start + * The starting position to map. + * @param len + * The number of bytes to map (must be smaller than (2GB - 8B)). + * @throws IOException + * if file cannot be mapped. + * @throws OutOfMemoryError + * if virtual memory space runs out. + */ + public ByteBufferWrapper(final FileChannel fileChannel, final long start, final long len) throws IOException { + if (len > FileUtils.MAX_BUFFER_SIZE) { + throw new IllegalArgumentException("len > " + FileUtils.MAX_BUFFER_SIZE); + } + try { + // Try mapping the FileChannel + byteBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, start, len); + isMemoryMappedByteBuffer = true; + } catch (final OutOfMemoryError e) { + // If map failed, try calling System.gc() to free some allocated MappedByteBuffers + // (there is a limit to the number of mapped files -- 64k on Linux) + // See: http://www.mapdb.org/blog/mmap_files_alloc_and_jvm_crash/ + System.gc(); + System.runFinalization(); + // Then try calling map again + byteBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, start, len); + // May throw OutOfMemoryError again; caller should catch this + isMemoryMappedByteBuffer = true; + } catch (NonReadableChannelException | IllegalArgumentException | UnsupportedOperationException e) { + // None of these exceptions should be thrown, but wrap in IOException for simplicity + throw new IOException(e); + } + } + + /** + * Wrap an array in a {@link ByteBuffer}. + * + * @param arr + * The array to wrap. + * @throws OutOfMemoryError + * if virtual memory space runs out. + */ + public ByteBufferWrapper(final byte[] arr) { + this.byteBuffer = ByteBuffer.wrap(arr, 0, arr.length); + } + + /** + * Wrap a {@link ByteBuffer}. + * + * @param byteBuffer + * The byte buffer to wrap. + * @throws OutOfMemoryError + * if virtual memory space runs out. + */ + public ByteBufferWrapper(final ByteBuffer byteBuffer) { + this.byteBuffer = byteBuffer; + } + + /** + * Duplicate this {@link ByteBufferWrapper} by duplicating the underlying {@link ByteBuffer}, or opening another + * {@link RandomAccessFile} on the same file. + * + * @throws IOException + * if file can no longer be opened. + */ + public ByteBufferWrapper duplicate() throws IOException { + return byteBuffer != null ? new ByteBufferWrapper(byteBuffer.duplicate()) + : new ByteBufferWrapper(file, rafSliceStart, rafLength); + } + + /** + * Slice this {@link ByteBufferWrapper} by duplicating the underlying {@link ByteBuffer}, or opening another + * {@link RandomAccessFile} on the same file, then slicing it. + * + * @throws IOException + * if file can no longer be opened. + */ + public ByteBufferWrapper slice(final int childSliceStart, final int childSliceLength) throws IOException { + if (byteBuffer != null) { + // Slice a ByteBuffer + + // Duplicate the ByteBuffer so that the position and limit are not changed on the original + // (In JDK 13, there is a slice(start, len), which eliminates this need.) + final ByteBuffer dup = byteBuffer.duplicate(); + + // Create and return a slice on the chunk ByteBuffer that contains only this zip entry + // N.B. the cast to Buffer is necessary, see: + // https://github.com/plasma-umass/doppio/issues/497#issuecomment-334740243 + // https://github.com/classgraph/classgraph/issues/284#issuecomment-443612800 + ((Buffer) byteBuffer).mark(); + try { + ((Buffer) dup).position(childSliceStart); + ((Buffer) dup).limit(childSliceStart + childSliceLength); + final ByteBufferWrapper slice = new ByteBufferWrapper(dup.slice()); + return slice; + } finally { + ((Buffer) byteBuffer).reset(); + ((Buffer) dup).limit(dup.capacity()); + } + + } else { + // Memory mapping is disabled -- open another RandomAccessFile on a slice of the current one + if (childSliceStart + childSliceLength > rafLength) { + throw new IOException("Child slice extends past end of file"); + } + if (childSliceLength > rafSliceLength) { + throw new IOException("Child slice larger than parent slice"); + } + return new ByteBufferWrapper(file, rafSliceStart + childSliceStart, childSliceLength); + } + } + + /** Return true if remaining() returns a number greater than zero. */ + public boolean hasRemaining() { + return remaining() > 0; + } + + /** Return the number of bytes remaining in the wrapped file slice. */ + public int remaining() { + try { + if (byteBuffer != null) { + return byteBuffer.remaining(); + } else { + final long rafSliceEnd = rafSliceStart + rafSliceLength; + return Math.max(0, (int) Math.max(0, rafSliceEnd - raf.getFilePointer())); + } + } catch (final IOException e) { + return 0; + } + } + + /** + * Read bytes from the current position in the underlying {@link ByteBuffer} or {@link RandomAccessFile} into a + * byte array. + * + * @param arr + * The byte array to read into. + * @param arrStart + * The start position in the array. + * @param numBytesToRead + * The number of bytes to read. + * @throws IOException + * If the file is closed or the requested number of bytes could not be read. + */ + public void get(final byte[] arr, final int arrStart, final int numBytesToRead) throws IOException { + if (byteBuffer != null) { + // Read from the wrapped ByteBuffer + byteBuffer.get(arr, arrStart, numBytesToRead); + + } else { + // Read from the wrapped RandomAccessFile + if (raf.read(arr, arrStart, numBytesToRead) < numBytesToRead) { + throw new IOException("Unexpected EOF"); + } + } + } + + /** + * Read bytes from the specified position in the underlying {@link ByteBuffer} or {@link RandomAccessFile} into + * a byte array. + * + * @param off + * The offset within the slice to start reading from. + * @param arr + * The byte array to read into. + * @param arrStart + * The start position in the array. + * @param numBytesToRead + * The number of bytes to read. + * @throws IOException + * If the file is closed or the requested number of bytes could not be read. + */ + public void get(final int off, final byte[] arr, final int arrStart, final int numBytesToRead) + throws IOException { + if (byteBuffer != null) { + // Read from the wrapped ByteBuffer + if (numBytesToRead > byteBuffer.remaining()) { + throw new IOException("Unexpected EOF"); + } + // N.B. the cast to Buffer is necessary, see: + // https://github.com/plasma-umass/doppio/issues/497#issuecomment-334740243 + // https://github.com/classgraph/classgraph/issues/284#issuecomment-443612800 + // Otherwise compiling in JDK<9 compatibility mode using JDK9+ causes runtime breakage. + ((Buffer) byteBuffer).mark(); + try { + ((Buffer) byteBuffer).position(off); + byteBuffer.get(arr, arrStart, numBytesToRead); + } catch (final BufferUnderflowException e) { + // Should not happen + throw new EOFException("Unexpected EOF"); + } finally { + ((Buffer) byteBuffer).reset(); + } + + } else { + // Read from the wrapped RandomAccessFile + final long currPos = raf.getFilePointer(); + raf.seek(rafSliceStart + off); + if (raf.read(arr, arrStart, numBytesToRead) < numBytesToRead) { + throw new IOException("Unexpected EOF"); + } + raf.seek(currPos); + } + } + + /** + * Skip the given number of bytes from the current position. + * + * @param numBytesToSkip + * the number of bytes to skip. + */ + public void skip(final int numBytesToSkip) throws IOException { + if (numBytesToSkip == 0) { + return; + } else if (numBytesToSkip < 0) { + throw new IllegalArgumentException("Tried to skip a negative number of bytes"); + } + if (byteBuffer != null) { + try { + // N.B. the cast to Buffer is necessary, see: + // https://github.com/plasma-umass/doppio/issues/497#issuecomment-334740243 + // https://github.com/classgraph/classgraph/issues/284#issuecomment-443612800 + ((Buffer) byteBuffer).position(byteBuffer.position() + numBytesToSkip); + } catch (final IllegalArgumentException e) { + throw new IOException("Unexpected EOF"); + } + } else { + if (raf.skipBytes(numBytesToSkip) < numBytesToSkip) { + throw new IOException("Unexpected EOF"); + } + } + } + + /** + * @return the current position in the file slice. + */ + public int position() throws IOException { + if (byteBuffer != null) { + return byteBuffer.position(); + } else { + return (int) (raf.getFilePointer() - rafSliceStart); + } + } + + /** + * Get the wrapped {@link ByteBuffer}, or returns null if this file is backed by a {@link RandomAccessFile} + * instead. + */ + public ByteBuffer getByteBuffer() { + return byteBuffer; + } + + /** + * Unmap the wrapped {@link ByteBuffer} (if this object wraps a {@link MappedByteBuffer} and not an array-backed + * {@link ByteBuffer} or {@link RandomAccessFile}). + * + * @param log + * the log + */ + public void close(final LogNode log) { + if (isMemoryMappedByteBuffer) { + FileUtils.closeDirectByteBuffer(byteBuffer, log); + } + if (raf != null) { + try { + raf.close(); + } catch (final IOException e) { + // Ignore + } + raf = null; + } + } +} 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 8730de4f6..9ddaa1c95 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/FastZipEntry.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/FastZipEntry.java @@ -31,9 +31,7 @@ import java.io.EOFException; import java.io.IOException; import java.io.InputStream; -import java.nio.Buffer; import java.nio.BufferUnderflowException; -import java.nio.ByteBuffer; import java.util.Calendar; import java.util.TimeZone; import java.util.concurrent.atomic.AtomicBoolean; @@ -256,23 +254,21 @@ public boolean canGetAsSlice() throws IOException, InterruptedException { * @throws InterruptedException * If the thread was interrupted. */ - public ByteBuffer getAsSlice() throws IOException, InterruptedException { + public ByteBufferWrapper getAsSlice() throws IOException, InterruptedException { // Check the file is STORED and resides in only one chunk if (!canGetAsSlice()) { throw new IllegalArgumentException("Cannot open zip entry as a slice"); } + final int sliceLength = (int) uncompressedSize; + // Fetch the ByteBuffer for the applicable chunk final long dataStartOffsetWithinPhysicalZipFile = getEntryDataStartOffsetWithinPhysicalZipFile(); final int chunkIdx = (int) (dataStartOffsetWithinPhysicalZipFile / FileUtils.MAX_BUFFER_SIZE); final long chunkStart = chunkIdx * (long) FileUtils.MAX_BUFFER_SIZE; - final ByteBuffer dupdBuf = parentLogicalZipFile.physicalZipFile.getByteBuffer(chunkIdx).duplicate(); - // Create and return a slice on the chunk ByteBuffer that contains only this zip entry - // N.B. the cast to Buffer is necessary, see: - // https://github.com/plasma-umass/doppio/issues/497#issuecomment-334740243 - // https://github.com/classgraph/classgraph/issues/284#issuecomment-443612800 - ((Buffer) dupdBuf).position((int) (dataStartOffsetWithinPhysicalZipFile - chunkStart)); - ((Buffer) dupdBuf).limit((int) (dataStartOffsetWithinPhysicalZipFile + uncompressedSize - chunkStart)); - return dupdBuf.slice(); + final int sliceStart = (int) (dataStartOffsetWithinPhysicalZipFile - chunkStart); + + // Duplicate and slice the ByteBuffer + return parentLogicalZipFile.physicalZipFile.getByteBuffer(chunkIdx).slice(sliceStart, sliceLength); } // ------------------------------------------------------------------------------------------------------------- @@ -301,7 +297,7 @@ public InputStream open() throws IOException, InterruptedException { private final byte[] scratch = new byte[8 * 1024]; /** The current 2GB chunk of the zip entry. */ - private ByteBuffer currChunkByteBuf; + private ByteBufferWrapper currChunkByteBuf; /** True if the current 2GB chunk is the last chunk in the zip entry. */ private boolean isLastChunk; @@ -326,51 +322,34 @@ public InputStream open() throws IOException, InterruptedException { // Calculate the chunk index for the first chunk currChunkIdx = (int) (dataStartOffsetWithinPhysicalZipFile / FileUtils.MAX_BUFFER_SIZE); - // Get the MappedByteBuffer for the 2GB chunk, and duplicate it - currChunkByteBuf = parentLogicalZipFile.physicalZipFile.getByteBuffer(currChunkIdx).duplicate(); - // Calculate the start position within the first chunk, and set the position of the slice. // N.B. the cast to Buffer is necessary, see: // https://github.com/plasma-umass/doppio/issues/497#issuecomment-334740243 // https://github.com/classgraph/classgraph/issues/284#issuecomment-443612800 final int chunkPos = (int) (dataStartOffsetWithinPhysicalZipFile - (((long) currChunkIdx) * (long) FileUtils.MAX_BUFFER_SIZE)); - ((Buffer) currChunkByteBuf).position(chunkPos); // Calculate end pos for the first chunk, and truncate it if it overflows 2GB - final long endPos = chunkPos + compressedSize; - ((Buffer) currChunkByteBuf).limit((int) Math.min(FileUtils.MAX_BUFFER_SIZE, endPos)); - isLastChunk = endPos <= FileUtils.MAX_BUFFER_SIZE; + final int chunkLength = (int) Math.min(FileUtils.MAX_BUFFER_SIZE, compressedSize); + // True if there's only one chunk (first chunk is also last chunk) + isLastChunk = chunkLength == compressedSize; + + // Get the MappedByteBuffer for the 2GB chunk, duplicate it and slice it + currChunkByteBuf = parentLogicalZipFile.physicalZipFile.getByteBuffer(currChunkIdx).slice(chunkPos, + chunkLength); } /** Advance to the next 2GB chunk. */ private boolean readNextChunk() throws IOException, InterruptedException { currChunkIdx++; + isLastChunk = currChunkIdx >= parentLogicalZipFile.physicalZipFile.numChunks() - 1; if (currChunkIdx >= parentLogicalZipFile.physicalZipFile.numChunks()) { // Ran out of chunks return false; } - // Calculate how many bytes were consumed in previous chunks - final long chunkStartOff = ((long) currChunkIdx) * (long) FileUtils.MAX_BUFFER_SIZE; - final long priorBytes = chunkStartOff - dataStartOffsetWithinPhysicalZipFile; - final long remainingBytes = compressedSize - priorBytes; - if (remainingBytes <= 0) { - return false; - } - // Get the MappedByteBuffer for the next 2GB chunk, and duplicate it currChunkByteBuf = parentLogicalZipFile.physicalZipFile.getByteBuffer(currChunkIdx).duplicate(); - - // The start position for 2nd and subsequent chunks is 0. - // N.B. the cast to Buffer is necessary, see: - // https://github.com/plasma-umass/doppio/issues/497#issuecomment-334740243 - // https://github.com/classgraph/classgraph/issues/284#issuecomment-443612800 - ((Buffer) currChunkByteBuf).position(0); - - // Calculate end pos for the next chunk, and truncate it if it overflows 2GB - ((Buffer) currChunkByteBuf).limit((int) Math.min(FileUtils.MAX_BUFFER_SIZE, remainingBytes)); - isLastChunk = remainingBytes <= FileUtils.MAX_BUFFER_SIZE; return true; } @@ -479,11 +458,7 @@ private int readStored(final byte[] buf, final int off, final int len) final int remainingToRead = len - read; final int remainingInBuf = currChunkByteBuf.remaining(); final int numBytesRead = Math.min(remainingToRead, remainingInBuf); - try { - currChunkByteBuf.get(buf, off + read, numBytesRead); - } catch (final BufferUnderflowException e) { - throw new EOFException("Unexpected EOF in stored (non-deflated) zip entry data"); - } + currChunkByteBuf.get(buf, off + read, numBytesRead); read += numBytesRead; } return read; @@ -508,11 +483,7 @@ private void skipStored(final long n) throws IOException { final int remainingInBuf = currChunkByteBuf.remaining(); final int numBytesToSkip = (int) Math.min(FileUtils.MAX_BUFFER_SIZE, Math.min(remainingToSkip, remainingInBuf)); - try { - ((Buffer) currChunkByteBuf).position(currChunkByteBuf.position() + numBytesToSkip); - } catch (final BufferUnderflowException e) { - throw new EOFException("Unexpected EOF in stored (non-deflated) zip entry data"); - } + currChunkByteBuf.skip(numBytesToSkip); skipped += numBytesToSkip; } } catch (final InterruptedException e) { diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java index 93dd9a272..5a6363720 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java @@ -38,11 +38,11 @@ import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; -import java.nio.channels.NonReadableChannelException; import java.nio.file.Files; import java.nio.file.StandardOpenOption; import java.util.Arrays; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReferenceArray; import nonapi.io.github.classgraph.concurrency.SingletonMap; @@ -66,13 +66,13 @@ public class MappedByteBufferResources { private FileChannel fileChannel; /** The total length. */ - private long length; + private final AtomicLong length = new AtomicLong(); /** The cached mapped byte buffers for each 2GB chunk. */ - private AtomicReferenceArray byteBufferChunksCached; + private AtomicReferenceArray byteBufferChunksCached; /** A singleton map from chunk index to byte buffer, ensuring that any given chunk is only mapped once. */ - private SingletonMap chunkIdxToByteBufferSingletonMap; + private SingletonMap chunkIdxToByteBufferSingletonMap; /** The nested jar handler. */ private final NestedJarHandler nestedJarHandler; @@ -93,17 +93,16 @@ public class MappedByteBufferResources { * needed). * @param nestedJarHandler * the nested jar handler - * @param scanSpec - * the scan spec. * @param log * the log. * @throws IOException * If the contents could not be read. */ public MappedByteBufferResources(final InputStream inputStream, final int inputStreamLengthHint, - final String tempFileBaseName, final NestedJarHandler nestedJarHandler, final ScanSpec scanSpec, - final LogNode log) throws IOException { + final String tempFileBaseName, final NestedJarHandler nestedJarHandler, final LogNode log) + throws IOException { this.nestedJarHandler = nestedJarHandler; + final ScanSpec scanSpec = nestedJarHandler.scanSpec; byte[] buf; boolean spillToDisk; if (inputStreamLengthHint != -1 && inputStreamLengthHint <= scanSpec.maxBufferedJarRAMSize) { @@ -126,7 +125,7 @@ public MappedByteBufferResources(final InputStream inputStream, final int inputS buf = Arrays.copyOf(buf, totBytesRead); } // Wrap array in a RAM-backed ByteBuffer - wrapByteBuffer(ByteBuffer.wrap(buf, 0, totBytesRead)); + wrapByteBuffer(new ByteBufferWrapper(buf)); spillToDisk = false; } else { // Didn't reach end of inputStream after buf was filled, so inputStreamLengthHint underestimated @@ -173,42 +172,45 @@ public MappedByteBufferResources(final InputStream inputStream, final int inputS } // Map the file to a MappedByteBuffer - mapFile(); + mapFile(scanSpec.disableMemoryMapping, log); } } /** - * Wrap an existing ByteBuffer. + * Map a {@link File} to a {@link MappedByteBuffer}. * - * @param byteBuffer - * the byte buffer + * @param file + * the file * @param nestedJarHandler * the nested jar handler + * @param log + * the log * @throws IOException * If the contents could not be read. */ - public MappedByteBufferResources(final ByteBuffer byteBuffer, final NestedJarHandler nestedJarHandler) + public MappedByteBufferResources(final File file, final NestedJarHandler nestedJarHandler, final LogNode log) throws IOException { this.nestedJarHandler = nestedJarHandler; - // Wrap the existing byte buffer - wrapByteBuffer(byteBuffer); + this.mappedFile = file; + // Map the file to a MappedByteBuffer + mapFile(nestedJarHandler.scanSpec.disableMemoryMapping, log); } /** - * Map a {@link File} to a {@link MappedByteBuffer}. + * Wrap an existing ByteBuffer. * - * @param file - * the file + * @param byteBuffer + * the byte buffer * @param nestedJarHandler * the nested jar handler * @throws IOException * If the contents could not be read. */ - public MappedByteBufferResources(final File file, final NestedJarHandler nestedJarHandler) throws IOException { + public MappedByteBufferResources(final ByteBuffer byteBuffer, final NestedJarHandler nestedJarHandler) + throws IOException { this.nestedJarHandler = nestedJarHandler; - this.mappedFile = file; - // Map the file to a MappedByteBuffer - mapFile(); + // Wrap the existing byte buffer + wrapByteBuffer(new ByteBufferWrapper(byteBuffer)); } /** @@ -217,79 +219,94 @@ public MappedByteBufferResources(final File file, final NestedJarHandler nestedJ * @param byteBuffer * the {@link ByteBuffer}. */ - private void wrapByteBuffer(final ByteBuffer byteBuffer) { - length = byteBuffer.remaining(); + private void wrapByteBuffer(final ByteBufferWrapper byteBuffer) { + length.set(byteBuffer.remaining()); // Put the ByteBuffer into the cache, so that the singleton map code for file mapping is never called - byteBufferChunksCached = new AtomicReferenceArray(1); + byteBufferChunksCached = new AtomicReferenceArray(1); byteBufferChunksCached.set(0, byteBuffer); // Don't set mappedFile, fileChannel or raf, they are unneeded. } /** - * Map a {@link File} to a {@link MappedByteBuffer}. + * Map a {@link File} to a {@link MappedByteBuffer} or {@link RandomAccessFile}. * + * @param disableMemoryMapping + * If true, use a {@link RandomAccessFile} rather than a {@link MappedByteBuffer} so that virtual + * memory space is not used when reading jarfiles. + * @param log + * the log. * @throws IOException * Signals that an I/O exception has occurred. */ - private void mapFile() throws IOException { - try { - raf = new RandomAccessFile(mappedFile, "r"); - length = raf.length(); - fileChannel = raf.getChannel(); - - } catch (final IOException e) { - close(/* log = */ null); - throw e; - } catch (final IllegalArgumentException | SecurityException e) { - close(/* log = */ null); - throw new IOException(e); + private void mapFile(final boolean disableMemoryMapping, final LogNode log) throws IOException { + // If memory mapping is enabled, share one instance of RandomAccessFile and FileChannel across + // all memory-mapped chunks, to avoid opening a new file or channel for every new chunk mapping + if (!disableMemoryMapping) { + try { + raf = new RandomAccessFile(mappedFile, "r"); + fileChannel = raf.getChannel(); + } catch (final IOException e) { + close(log); + throw e; + } catch (final IllegalArgumentException | SecurityException e) { + close(log); + throw new IOException(e); + } } + length.set(mappedFile.length()); // Implement an array of MappedByteBuffers to support jarfiles >2GB in size: // https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6347833 - final int numByteBufferChunks = (int) ((length + FileUtils.MAX_BUFFER_SIZE) / FileUtils.MAX_BUFFER_SIZE); - byteBufferChunksCached = new AtomicReferenceArray(numByteBufferChunks); - chunkIdxToByteBufferSingletonMap = new SingletonMap() { + final int numByteBufferChunks = (int) ((length.get() + FileUtils.MAX_BUFFER_SIZE) + / FileUtils.MAX_BUFFER_SIZE); + byteBufferChunksCached = new AtomicReferenceArray(numByteBufferChunks); + chunkIdxToByteBufferSingletonMap = new SingletonMap() { @Override - public ByteBuffer newInstance(final Integer chunkIdxI, final LogNode log) throws IOException { + public ByteBufferWrapper newInstance(final Integer chunkIdxI, final LogNode log) throws IOException { // Map the indexed 2GB chunk of the file to a MappedByteBuffer final long pos = chunkIdxI.longValue() * FileUtils.MAX_BUFFER_SIZE; - final long chunkSize = Math.min(FileUtils.MAX_BUFFER_SIZE, length - pos); + final long chunkSize = Math.min(FileUtils.MAX_BUFFER_SIZE, length.get() - pos); - if (fileChannel == null) { - // Should not happen - throw new IOException("Cannot map a null FileChannel"); - } + ByteBufferWrapper byteBuffer = null; + if (!disableMemoryMapping) { + if (fileChannel == null) { + // Should not happen + throw new IOException("Cannot map a null FileChannel"); + } - MappedByteBuffer byteBuffer; - try { - // Try mapping the FileChannel - byteBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, pos, chunkSize); - } catch (final IOException e) { - MappedByteBufferResources.this.close(log); - throw e; - } catch (final NonReadableChannelException | IllegalArgumentException e) { - MappedByteBufferResources.this.close(log); - throw new IOException(e); - } catch (final OutOfMemoryError e) { try { - // If map failed, try calling System.gc() to free some allocated MappedByteBuffers - // (there is a limit to the number of mapped files -- 64k on Linux) - // See: http://www.mapdb.org/blog/mmap_files_alloc_and_jvm_crash/ - System.gc(); - System.runFinalization(); - // Then try calling map again - byteBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, pos, chunkSize); - } catch (final IOException e2) { - MappedByteBufferResources.this.close(log); - throw e2; - } catch (final OutOfMemoryError | IllegalArgumentException e2) { - // Out of mappable virtual memory, or other issue + // Try memory-mapping the file channel + byteBuffer = new ByteBufferWrapper(fileChannel, pos, chunkSize); + + } catch (final IOException e) { + // Should not happen, since if raf was opened, the file must exist. + // (TODO: I saw mention somewhere that on some operating systems, the JRE + // may throw IOException and not OutOfMemoryError if memory mapping runs + // out of virtual address space -- need to investigate this.) MappedByteBufferResources.this.close(log); - throw new IOException(e2); + throw e; + + } catch (final OutOfMemoryError e) { + if (log != null) { + log.log("Out of memory when trying to memory map file " + mappedFile); + } + // Failover to non-memory-mapped mode + // (fileChannel and raf will be closed when this MappedByteBufferResources object is closed) } } + if (byteBuffer == null) { + // Memory mapping was disabled or failed -- use a RandomAccessFile to access the file instead + if (log != null) { + log.log("Memory mapping is disabled for file " + mappedFile + + " -- using slower RandomAccessFile method to open file instead"); + } + + // If memory mapping was disabled or failed, every duplicate of this ByteBufferWrapper + // needs to open its own RAF, for thread safety + byteBuffer = new ByteBufferWrapper(mappedFile, pos, chunkSize); + } + // Record that the byte buffer has been mapped nestedJarHandler.addMappedByteBuffer(byteBuffer); @@ -310,7 +327,7 @@ public ByteBuffer newInstance(final Integer chunkIdxI, final LogNode log) throws * @throws InterruptedException * If the thread was interrupted. */ - public ByteBuffer getByteBuffer(final int chunkIdx) throws IOException, InterruptedException { + public ByteBufferWrapper getByteBuffer(final int chunkIdx) throws IOException, InterruptedException { if (closed.get()) { throw new IOException(getClass().getSimpleName() + " already closed"); } @@ -318,7 +335,7 @@ public ByteBuffer getByteBuffer(final int chunkIdx) throws IOException, Interrup throw new IOException("Chunk index out of range"); } // Fast path: only look up singleton map if mappedByteBuffersCached is null - ByteBuffer cachedBuf = byteBufferChunksCached.get(chunkIdx); + ByteBufferWrapper cachedBuf = byteBufferChunksCached.get(chunkIdx); if (cachedBuf == null) { // This 2GB chunk has not yet been read -- mmap it and cache it. // (Use a singleton map so that the mmap doesn't happen more than once) @@ -350,7 +367,7 @@ public File getMappedFile() { * wrapped. */ public long length() { - return length; + return length.get(); } /** @@ -376,9 +393,9 @@ public void close(final LogNode log) { // Only unmap bytebuffers if they came from a mapped file if (mappedFile != null) { for (int i = 0; i < byteBufferChunksCached.length(); i++) { - final ByteBuffer mappedByteBuffer = byteBufferChunksCached.get(i); + final ByteBufferWrapper mappedByteBuffer = byteBufferChunksCached.get(i); if (mappedByteBuffer != null) { - nestedJarHandler.unmapByteBuffer(mappedByteBuffer, /* log = */ null); + nestedJarHandler.unmapByteBuffer(mappedByteBuffer, log); byteBufferChunksCached.set(i, 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 86f0c6af5..2a207b8b6 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java @@ -68,7 +68,7 @@ /** Open and read jarfiles, which may be nested within other jarfiles. */ public class NestedJarHandler { /** The {@link ScanSpec}. */ - private final ScanSpec scanSpec; + final ScanSpec scanSpec; /** * A singleton map from a zipfile's {@link File} to the {@link PhysicalZipFile} for that file, used to ensure @@ -82,7 +82,7 @@ public PhysicalZipFile newInstance(final File canonicalFile, final LogNode log) throw ClassGraphException .newClassGraphException(NestedJarHandler.class.getSimpleName() + " already closed"); } - final PhysicalZipFile physicalZipFile = new PhysicalZipFile(canonicalFile, NestedJarHandler.this); + final PhysicalZipFile physicalZipFile = new PhysicalZipFile(canonicalFile, NestedJarHandler.this, log); allocatedPhysicalZipFiles.add(physicalZipFile); return physicalZipFile; @@ -379,8 +379,8 @@ public RecyclableInflater newInstance() throws RuntimeException { * {@link MappedByteBuffer} instances that are currently mapped. (Use {@link ReferenceEqualityKey} so that the * entire contents of the buffers are not compared by {@link ByteBuffer#equals(Object)}). */ - private Set> mappedByteBuffers = Collections - .newSetFromMap(new ConcurrentHashMap, Boolean>()); + private Set> mappedByteBuffers = Collections + .newSetFromMap(new ConcurrentHashMap, Boolean>()); /** {@link MappedByteBufferResources} instances that were allocated for downloading jars from URLs. */ private Set mappedByteBufferResources = Collections @@ -421,21 +421,21 @@ public NestedJarHandler(final ScanSpec scanSpec, final InterruptionChecker inter * @param byteBuffer * the byte buffer */ - public void addMappedByteBuffer(final ByteBuffer byteBuffer) { - mappedByteBuffers.add(new ReferenceEqualityKey(byteBuffer)); + public void addMappedByteBuffer(final ByteBufferWrapper byteBuffer) { + mappedByteBuffers.add(new ReferenceEqualityKey(byteBuffer)); } /** - * Unmap a previously-mapped {@link ByteBuffer}. + * Unmap a possibly previously-mapped {@link ByteBuffer} (wrapped in a {@link ByteBufferWrapper}). * * @param byteBuffer - * the {@link ByteBuffer}. + * the {@link ByteBufferWrapper}. * @param log * the log. */ - public void unmapByteBuffer(final ByteBuffer byteBuffer, final LogNode log) { - if (mappedByteBuffers.remove(new ReferenceEqualityKey(byteBuffer))) { - FileUtils.closeDirectByteBuffer(byteBuffer, log); + public void unmapByteBuffer(final ByteBufferWrapper byteBuffer, final LogNode log) { + if (mappedByteBuffers.remove(new ReferenceEqualityKey(byteBuffer))) { + byteBuffer.close(log); } } @@ -624,7 +624,7 @@ public void close(final LogNode log) { } if (mappedByteBuffers != null) { while (!mappedByteBuffers.isEmpty()) { - for (final ReferenceEqualityKey byteBufferRef : new ArrayList<>( + for (final ReferenceEqualityKey byteBufferRef : new ArrayList<>( mappedByteBuffers)) { unmapByteBuffer(byteBufferRef.get(), log); } 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 f705dd298..cb422b699 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/PhysicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/PhysicalZipFile.java @@ -70,10 +70,13 @@ class PhysicalZipFile implements Closeable { * the file * @param nestedJarHandler * the nested jar handler + * @param log + * the log * @throws IOException * if an I/O exception occurs. */ - PhysicalZipFile(final File file, final NestedJarHandler nestedJarHandler) throws IOException { + PhysicalZipFile(final File file, final NestedJarHandler nestedJarHandler, final LogNode log) + throws IOException { // Make sure the File is readable and is a regular file FileUtils.checkCanReadAndIsFile(file); @@ -82,7 +85,7 @@ class PhysicalZipFile implements Closeable { this.path = FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, file.getPath()); // Map the file to a ByteBuffer - this.byteBufferResources = new MappedByteBufferResources(file, nestedJarHandler); + this.byteBufferResources = new MappedByteBufferResources(file, nestedJarHandler, log); } /** @@ -126,8 +129,6 @@ class PhysicalZipFile implements Closeable { * zipfile * @param nestedJarHandler * the nested jar handler - * @param scanSpec - * the scan spec. * @param log * the log * @throws IOException @@ -142,7 +143,7 @@ class PhysicalZipFile implements Closeable { // Wrap the ByteBuffer this.byteBufferResources = new MappedByteBufferResources(inputStream, inputStreamLengthHint, path, - nestedJarHandler, scanSpec, log); + nestedJarHandler, log); if (this.byteBufferResources.length() == 0L) { throw new IOException("Zipfile is empty: " + path); } @@ -150,8 +151,8 @@ class PhysicalZipFile implements Closeable { } /** - * Get a mmap'd chunk of the file, where chunkIdx denotes which 2GB chunk of the file to return (0 for the first - * 2GB of the file, or for files smaller than 2GB; 1 for the 2-4GB chunk, etc.). + * Get a chunk of the file, where chunkIdx denotes which 2GB chunk of the file to return (0 for the first 2GB of + * the file, or for files smaller than 2GB; 1 for the 2-4GB chunk, etc.). * * @param chunkIdx * The index of the 2GB chunk to read @@ -161,7 +162,7 @@ class PhysicalZipFile implements Closeable { * @throws InterruptedException * If the thread was interrupted. */ - ByteBuffer getByteBuffer(final int chunkIdx) throws IOException, InterruptedException { + ByteBufferWrapper getByteBuffer(final int chunkIdx) throws IOException, InterruptedException { return byteBufferResources.getByteBuffer(chunkIdx); } diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSliceReader.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSliceReader.java index 34836a590..a5fb69ee3 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSliceReader.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSliceReader.java @@ -30,9 +30,6 @@ import java.io.EOFException; import java.io.IOException; -import java.nio.Buffer; -import java.nio.BufferUnderflowException; -import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.Arrays; @@ -49,7 +46,7 @@ class ZipFileSliceReader implements AutoCloseable { * The chunk cache, for duplicates of the ByteBuffers in the ZipFileSlice, so that different threads can work on * the same MappedByteBuffers without interfering with each other's buffer position. */ - private final ByteBuffer[] chunkCache; + private final ByteBufferWrapper[] chunkCache; /** A scratch buffer. */ private final byte[] scratch = new byte[256]; @@ -62,7 +59,7 @@ class ZipFileSliceReader implements AutoCloseable { */ public ZipFileSliceReader(final ZipFileSlice zipFileSlice) { this.zipFileSlice = zipFileSlice; - this.chunkCache = new ByteBuffer[zipFileSlice.physicalZipFile.numChunks()]; + this.chunkCache = new ByteBufferWrapper[zipFileSlice.physicalZipFile.numChunks()]; } /** @@ -76,10 +73,11 @@ public ZipFileSliceReader(final ZipFileSlice zipFileSlice) { * @throws InterruptedException * if the thread was interrupted. */ - private ByteBuffer getChunk(final int chunkIdx) throws IOException, InterruptedException { - ByteBuffer chunk = chunkCache[chunkIdx]; + private ByteBufferWrapper getChunk(final int chunkIdx) throws IOException, InterruptedException { + ByteBufferWrapper chunk = chunkCache[chunkIdx]; if (chunk == null) { - final ByteBuffer byteBufferDup = zipFileSlice.physicalZipFile.getByteBuffer(chunkIdx).duplicate(); + final ByteBufferWrapper byteBufferDup = zipFileSlice.physicalZipFile.getByteBuffer(chunkIdx) + .duplicate(); chunk = chunkCache[chunkIdx] = byteBufferDup; } return chunk; @@ -115,30 +113,17 @@ int read(final long off, final byte[] buf, final int bufStart, final int numByte // Find the ByteBuffer chunk to read from final long currOffAbsolute = zipFileSlice.startOffsetWithinPhysicalZipFile + currOff; final int chunkIdx = (int) (currOffAbsolute / FileUtils.MAX_BUFFER_SIZE); - final ByteBuffer chunk = getChunk(chunkIdx); + final ByteBufferWrapper chunk = getChunk(chunkIdx); final long chunkStartAbsolute = ((long) chunkIdx) * (long) FileUtils.MAX_BUFFER_SIZE; final int startReadPos = (int) (currOffAbsolute - chunkStartAbsolute); - // Read from current chunk. - // N.B. the cast to Buffer is necessary, see: - // https://github.com/plasma-umass/doppio/issues/497#issuecomment-334740243 - // https://github.com/classgraph/classgraph/issues/284#issuecomment-443612800 - // Otherwise compiling in JDK<9 compatibility mode using JDK9+ causes runtime breakage. - ((Buffer) chunk).mark(); - ((Buffer) chunk).position(startReadPos); - final int numBytesRead = Math.min(chunk.remaining(), remainingBytesToRead); - try { - chunk.get(buf, currBufStart, numBytesRead); - } catch (final BufferUnderflowException e) { - // Should not happen - throw new EOFException("Unexpected EOF"); - } - ((Buffer) chunk).reset(); + // Fill buf from chunk + chunk.get(startReadPos, buf, currBufStart, remainingBytesToRead); - currOff += numBytesRead; - currBufStart += numBytesRead; - totBytesRead += numBytesRead; - remainingBytesToRead -= numBytesRead; + currOff += remainingBytesToRead; + currBufStart += remainingBytesToRead; + totBytesRead += remainingBytesToRead; + remainingBytesToRead -= remainingBytesToRead; } return totBytesRead == 0 && numBytesToRead > 0 ? -1 : totBytesRead; } 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 779ed9551..5b820aed8 100644 --- a/src/main/java/nonapi/io/github/classgraph/scanspec/ScanSpec.java +++ b/src/main/java/nonapi/io/github/classgraph/scanspec/ScanSpec.java @@ -29,10 +29,12 @@ package nonapi.io.github.classgraph.scanspec; import java.io.InputStream; +import java.io.RandomAccessFile; import java.lang.reflect.Field; import java.net.URI; import java.net.URL; import java.nio.ByteBuffer; +import java.nio.MappedByteBuffer; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; @@ -253,6 +255,13 @@ public class ScanSpec { */ public int maxBufferedJarRAMSize = 64 * 1024 * 1024; + /** + * If true, use a {@link RandomAccessFile} rather than a {@link MappedByteBuffer} to open jarfiles, which is + * slower, but does not use up virtual memory space. You can disable memory mapping if you get + * {@link OutOfMemoryError} when scanning. + */ + public boolean disableMemoryMapping = false; + // ------------------------------------------------------------------------------------------------------------- /** Constructor for deserialization. */ diff --git a/src/main/java/nonapi/io/github/classgraph/utils/InputStreamOrByteBufferAdapter.java b/src/main/java/nonapi/io/github/classgraph/utils/InputStreamOrByteBufferAdapter.java index f20d9920e..537b31620 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/InputStreamOrByteBufferAdapter.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/InputStreamOrByteBufferAdapter.java @@ -34,6 +34,8 @@ import java.nio.ByteBuffer; import java.util.Arrays; +import nonapi.io.github.classgraph.fastzipfilereader.ByteBufferWrapper; + /** Buffer class that can wrap either an InputStream or a ByteBuffer, depending on which is available. */ public class InputStreamOrByteBufferAdapter implements AutoCloseable { /** @@ -53,8 +55,8 @@ public class InputStreamOrByteBufferAdapter implements AutoCloseable { /** The InputStream, if applicable. */ private InputStream inputStream; - /** The ByteBuffer, if applicable. */ - private ByteBuffer byteBuffer; + /** The {@link ByteBufferWrapper}, if applicable. */ + private ByteBufferWrapper byteBufferWrapper; /** * Bytes read from the beginning of the classfile. Only access an index in this array directly if at least that @@ -70,7 +72,7 @@ public class InputStreamOrByteBufferAdapter implements AutoCloseable { public int curr; /** Bytes used in the buffer. */ - private int used; + private int bufBytesFilled; /** * Create an {@link InputStreamOrByteBufferAdapter} from an {@link InputStream}. @@ -86,15 +88,17 @@ public InputStreamOrByteBufferAdapter(final InputStream inputStream) { /** * Create an {@link InputStreamOrByteBufferAdapter} from an {@link InputStream}. * - * @param byteBuffer - * the byte buffer + * @param byteBufferWrapper + * the byte buffer wrapper */ - public InputStreamOrByteBufferAdapter(final ByteBuffer byteBuffer) { - if (byteBuffer.hasArray()) { + public InputStreamOrByteBufferAdapter(final ByteBufferWrapper byteBufferWrapper) { + final ByteBuffer byteBuffer = byteBufferWrapper.getByteBuffer(); + if (byteBuffer != null && byteBuffer.hasArray()) { // Just use the array behind the buffer as the input buffer this.buf = byteBuffer.array(); + this.bufBytesFilled = this.buf.length; } else { - this.byteBuffer = byteBuffer; + this.byteBufferWrapper = byteBufferWrapper; this.buf = new byte[INITIAL_BUFFER_CHUNK_SIZE]; } } @@ -119,22 +123,23 @@ private int read(final int off, final int len) throws IOException { return inputStream.read(buf, off, len); } else { // Wrapped ByteBuffer - final int bytesRemainingInBuf = byteBuffer != null ? byteBuffer.remaining() : buf.length - off; + final int bytesRemainingInBuf = byteBufferWrapper != null ? byteBufferWrapper.remaining() + : buf.length - off; final int bytesRead = Math.max(0, Math.min(len, bytesRemainingInBuf)); if (bytesRead == 0) { // Return -1, as per InputStream#read() contract return -1; } - if (byteBuffer != null) { + if (byteBufferWrapper != null) { // Copy from the ByteBuffer into the byte array - final int byteBufPositionBefore = byteBuffer.position(); + final int byteBufPositionBefore = byteBufferWrapper.position(); try { - byteBuffer.get(buf, off, bytesRead); + byteBufferWrapper.get(buf, off, bytesRead); } catch (final BufferUnderflowException e) { // Should not happen throw new IOException("Buffer underflow", e); } - return byteBuffer.position() - byteBufPositionBefore; + return byteBufferWrapper.position() - byteBufPositionBefore; } else { // Nothing to read, since ByteBuffer is backed with an array return bytesRead; @@ -151,17 +156,18 @@ private int read(final int off, final int len) throws IOException { * If an I/O exception occurs. */ private void readMore(final int bytesRequired) throws IOException { - if ((long) used + (long) bytesRequired > FileUtils.MAX_BUFFER_SIZE) { + if ((long) bufBytesFilled + (long) bytesRequired > FileUtils.MAX_BUFFER_SIZE) { // Since buf is an array, we're limited to reading 2GB per file throw new IOException("File is larger than 2GB, cannot read it"); } // Read INITIAL_BUFFER_CHUNK_SIZE for first chunk, or SUBSEQUENT_BUFFER_CHUNK_SIZE for subsequent chunks, // but don't try to read past 2GB limit final int targetReadSize = Math.max(bytesRequired, // - used == 0 ? INITIAL_BUFFER_CHUNK_SIZE : SUBSEQUENT_BUFFER_CHUNK_SIZE); + bufBytesFilled == 0 ? INITIAL_BUFFER_CHUNK_SIZE : SUBSEQUENT_BUFFER_CHUNK_SIZE); // Calculate number of bytes to read, based on the target read size, handling integer overflow - final int maxNewUsed = (int) Math.min((long) used + (long) targetReadSize, FileUtils.MAX_BUFFER_SIZE); - final int bytesToRead = maxNewUsed - used; + final int maxNewUsed = (int) Math.min((long) bufBytesFilled + (long) targetReadSize, + FileUtils.MAX_BUFFER_SIZE); + final int bytesToRead = maxNewUsed - bufBytesFilled; if (maxNewUsed > buf.length) { // Ran out of space, need to increase the size of the buffer long newBufLen = buf.length; @@ -173,9 +179,9 @@ private void readMore(final int bytesRequired) throws IOException { int extraBytesStillNotRead = bytesToRead; int totBytesRead = 0; while (extraBytesStillNotRead > 0) { - final int bytesRead = read(used, extraBytesStillNotRead); + final int bytesRead = read(bufBytesFilled, extraBytesStillNotRead); if (bytesRead > 0) { - used += bytesRead; + bufBytesFilled += bytesRead; totBytesRead += bytesRead; extraBytesStillNotRead -= bytesRead; } else { @@ -211,7 +217,7 @@ public int readUnsignedByte() throws IOException { * If there was an exception while reading. */ public int readUnsignedByte(final int offset) throws IOException { - final int bytesToRead = Math.max(0, offset + 1 - used); + final int bytesToRead = Math.max(0, offset + 1 - bufBytesFilled); if (bytesToRead > 0) { readMore(bytesToRead); } @@ -241,7 +247,7 @@ public int readUnsignedShort() throws IOException { * If there was an exception while reading. */ public int readUnsignedShort(final int offset) throws IOException { - final int bytesToRead = Math.max(0, offset + 2 - used); + final int bytesToRead = Math.max(0, offset + 2 - bufBytesFilled); if (bytesToRead > 0) { readMore(bytesToRead); } @@ -272,7 +278,7 @@ public int readInt() throws IOException { * If there was an exception while reading. */ public int readInt(final int offset) throws IOException { - final int bytesToRead = Math.max(0, offset + 4 - used); + final int bytesToRead = Math.max(0, offset + 4 - bufBytesFilled); if (bytesToRead > 0) { readMore(bytesToRead); } @@ -305,7 +311,7 @@ public long readLong() throws IOException { * If there was an exception while reading. */ public long readLong(final int offset) throws IOException { - final int bytesToRead = Math.max(0, offset + 8 - used); + final int bytesToRead = Math.max(0, offset + 8 - bufBytesFilled); if (bytesToRead > 0) { readMore(bytesToRead); } @@ -328,7 +334,7 @@ public long readLong(final int offset) throws IOException { * If there was an exception while reading. */ public void skip(final int bytesToSkip) throws IOException { - final int bytesToRead = Math.max(0, curr + bytesToSkip - used); + final int bytesToRead = Math.max(0, curr + bytesToSkip - bufBytesFilled); if (bytesToRead > 0) { readMore(bytesToRead); } @@ -353,7 +359,7 @@ public String readString(final int strStart, final boolean replaceSlashWithDot, throws IOException { final int utfLen = readUnsignedShort(strStart); final int utfStart = strStart + 2; - final int bufferUnderrunBytes = Math.max(0, utfStart + utfLen - used); + final int bufferUnderrunBytes = Math.max(0, utfStart + utfLen - bufBytesFilled); if (bufferUnderrunBytes > 0) { readMore(bufferUnderrunBytes); } @@ -442,7 +448,7 @@ public void close() { } this.inputStream = null; } - this.byteBuffer = null; + this.byteBufferWrapper = null; this.buf = null; } } From 15565b9fd55a5b7a6fefd12d751ff5c442574588 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3bert=20Papp=20=28TWiStErRob=29?= Date: Sun, 9 Feb 2020 11:35:27 +0000 Subject: [PATCH 0592/1778] Add performance regression test for #400 --- .../classgraph/issues/issue400/Issue400.java | 74 ++++++++++++++++++ .../resources/issue400-nested-deflated.jar | Bin 0 -> 15722 bytes src/test/resources/issue400-nested-stored.jar | Bin 0 -> 17514 bytes 3 files changed, 74 insertions(+) create mode 100644 src/test/perf/io/github/classgraph/issues/issue400/Issue400.java create mode 100644 src/test/resources/issue400-nested-deflated.jar create mode 100644 src/test/resources/issue400-nested-stored.jar diff --git a/src/test/perf/io/github/classgraph/issues/issue400/Issue400.java b/src/test/perf/io/github/classgraph/issues/issue400/Issue400.java new file mode 100644 index 000000000..68b3fdd25 --- /dev/null +++ b/src/test/perf/io/github/classgraph/issues/issue400/Issue400.java @@ -0,0 +1,74 @@ +package io.github.classgraph.issues.issue400; + +import java.net.URL; +import java.net.URLClassLoader; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.data.Offset.offset; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ScanResult; + +/** + * Verify that a large number of stored/deflated nested JAR entries don't cause problems. + */ +public class Issue400 { + + private static final long MB = 1024 * 1024; + private static final long MEMORY_TOLERANCE = 2 * MB; + + @Test + public void loadsStoredJarWithManyNestedEntriesAndDoesNotUseMuchMemory() { + loadsJarWithManyNestedEntriesAndDoesNotUseMuchMemory( + Issue400.class.getClassLoader().getResource("issue400-nested-stored.jar")); + } + + @Test + public void loadsDeflatedJarWithManyNestedEntriesAndDoesNotUseMuchMemory() { + loadsJarWithManyNestedEntriesAndDoesNotUseMuchMemory( + Issue400.class.getClassLoader().getResource("issue400-nested-deflated.jar")); + } + + private void loadsJarWithManyNestedEntriesAndDoesNotUseMuchMemory(URL... jars) { + long ramAtStart = usedRam(); + long ramAfterScan; + try ( + ScanResult scanResult = new ClassGraph() + .overrideClassLoaders(new URLClassLoader(jars)) + .enableAllInfo() + .scan() + ) { + ramAfterScan = usedRam(); + // There are no classes in any of the JARs. + assertThat(scanResult.getAllClassesAsMap()).isEmpty(); + // Check if it contains the JAR and all nested entries. + assertThat(scanResult.getClasspathURLs()).hasSize(1 + 128); + } + long ramAtEnd = usedRam(); + + assertThat(ramAfterScan) + .withFailMessage("Memory usage while using ScanResult should stay within reasonable range: " + + "went from %s to %s MB.", ramAtStart / MB, ramAfterScan / MB) + .isGreaterThanOrEqualTo(ramAtStart) + .isCloseTo(ramAtStart, 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)); + } + + /** + * @return used JVM heap size allocated in RAM + * @see What are Runtime.getRuntime().totalMemory() and freeMemory()? + */ + private long usedRam() { + Runtime runtime = Runtime.getRuntime(); + runtime.gc(); + runtime.gc(); + runtime.gc(); + return (runtime.totalMemory() - runtime.freeMemory()); + } +} diff --git a/src/test/resources/issue400-nested-deflated.jar b/src/test/resources/issue400-nested-deflated.jar new file mode 100644 index 0000000000000000000000000000000000000000..2c5a997317ea01519b6f4b0907a56955e87a2552 GIT binary patch literal 15722 zcmbW;M~@s;6vgp!9((L@8ZrxuoNU*vTq`no7|UwJlISo7Bg6t&kimp+K+fDRw`Wxd-onqRVXHd~x!+5SuBM&ZjpA4|jE zuG_b3>uCIlscgr^bEBDe-#a)J{^juSxpDl6>lbdkX<^UiJkM>*wL5Q^KD2jX_d8j} zTv(_VmgV=(pVJ}{2xq6{0$5c6{$}v@rsd7w}W2zie<(Mj`P~{Y= zoI;gTsB#KbPNB*vR5^tzr%>e-s+>}lQ>t=GRZgkODOEY8DyLNCl&YLkl~by6DpgLU z%BfU2l`5xFlZ)2MP9RZgSIX;e9lDyLE9G^(6dmD8$nT2)S~%4tKU@BXUL|WA)9)JZ0Z@Zsb|Qho*|ohhHUB?vZ-gtrk)|2KSN%$csBe08D$tkhL3ge z%Xnh>_Z9y=vy2bj6uul6KL46=x(ViJJ~ePD1ctj%df)*F40ogS!21vw?n>!_<%|3p z40osWz?l#j?o#Q2n;|gVt z2#^v#fB-3R#S*^;q{OozKuWv?0;I$P5FjOf3jtE%3E^!QlaNS>m-|4xE2cww2m++U z4P--teM<75- z{MZNLTAWneO1Ls13+!Jqy04eb$2#^wgfB-4+ z69|wJr>ET-#7S~Gr1Ky^?umCmfRy+$1W1WLLV%Qb%4)xeq{N*NASFHq0aD_p5FjP4 zUgOt*lz6@m#L0BJ#5*BCN_+(Zq{KrIASIqU;}$VauG80G7X(O&k3)cz_!$I9iEGyS zH6SHk00C0sT|N*e-RZBws}LY1{saM1;%VXdY2yD#aRQ$H6m~;^+!LRG04ecv2#^wI z*84ReC0+;tQsUhZASJ%$195Vm{xbdy0aD`W8{8tsNqYKsum=L| zASJFl%&!3{aSj6Ho_G%gNQrMifRy+v1W1XS!*6>1`=@a{*-wYG7Xsv-_%sAaiC;p1 zl(_x~zXqhliy%NsycYtb#5W;8O8gB1q{J;p`b8uqUhMR literal 0 HcmV?d00001 diff --git a/src/test/resources/issue400-nested-stored.jar b/src/test/resources/issue400-nested-stored.jar new file mode 100644 index 0000000000000000000000000000000000000000..a43d698cd2ef0eb73716fe8282aba162e181d9ca GIT binary patch literal 17514 zcmb{44}{-!9LMqRZnv#%ZENjXYg_BuB+0h#`}_Z!G)Z!f`L$(IGHab|W@b%fCTB`2 zNhC>9k|gPmBoj#`{gGs5Op;_INzx?gJNNzE{NAsX&x_Y_Zrtyl?(?~a?`QLQ-v*O& zN5_(pBuO6IJ~8M%%)X6wf7-X_?#+YQS8Tm?VzBg+FHWrNK6CbcMgNNZQ}-R3x@TjW zrnYJCu7iWgdGou^xHl$~X)?JcQ)n`!CR1rLwIl}2%;QCw*hR~p5YMscN4Txk?n8pX9najj8YYZTWS z#kEFptx;TS6xSNXwMKERQQT-0HyXu_MscH2+-MXx8pVx9aidY(XcRXZ#jQqht5Mu) z6t^11twwRHQQT@2w;IK*MsX`qY^g-CY3}YUl_<7UqS#W2VoN28EtM#?RHE2YiDFAN ziZyq4W|C*IY3}Y!b9ZN&yF1g|-I?a@&NO#-rn$Q_&E1`8?(R%;cW0WrJJa0Vnda`! zGKL1gg}4t#|Hj{K!4`P25y++_n<%RV*?LCpg-qh17AX*KjC8oE4ulu;U8mv zw#NqUfIxq$#|B>XfpDJ322MkuKgnYQo9DVc2xoX~;C={@5 z36l^YB|ZfKQsPMnkP^q|`8^;d-s=P5Ope{eHy}Vt`~w2yGx7Rvd~(P^IDuoAa1;Wh z#Lpo>J`i-VOm$;tLQUCH@2fQsTx7+#ZCPnAjiVAqbEXk3oQxII_?$fqW)T zK!B9^Bm_u_Cm=vdT(`*Y0V#0`0;I&(AwWv}-3P*4ORU7}7P~zN6D_fUX$X)KKZ5`% zaoL4_38cgw5FjN!4*^o*j}RawUc1Ea0V(ky1W1YR`#_jYiG`FbbxR0SDY1cDAV5ld z0s^GO;}9Svu3hH$fRwl!0;I&(AV5m|4FaUZruzr)(BF~5lu4|E2Yn#Sm&67>g#dXv zFIn!E5N1kZzk}N#KuUZL0;I$rAV5mI=3>7Gq{IUdASJ#B0aD^W5Fnq4H+R2BHAFeg zh{QU090H`oZy`WF6IYM-nn^yTfASFHu0aD^O zJ`iR#VkNG+%q<~IX~YJ0LV%R`3Is@rzd(SLSgiJYKuSCe0aD^e5FjNkT;rEON*q9d zl=zGb%+6cHLi!E@FH4et0hH;hjQ{`u literal 0 HcmV?d00001 From ab98f3f7187c5d4e06dd40cac01fb310d2293cba Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 10 Feb 2020 01:38:55 -0700 Subject: [PATCH 0593/1778] Run finalizers (#401) --- .../perf/io/github/classgraph/issues/issue400/Issue400.java | 3 +++ 1 file changed, 3 insertions(+) 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 68b3fdd25..8e2017007 100644 --- a/src/test/perf/io/github/classgraph/issues/issue400/Issue400.java +++ b/src/test/perf/io/github/classgraph/issues/issue400/Issue400.java @@ -67,8 +67,11 @@ private void loadsJarWithManyNestedEntriesAndDoesNotUseMuchMemory(URL... jars) { private long usedRam() { Runtime runtime = Runtime.getRuntime(); runtime.gc(); + System.runFinalization(); runtime.gc(); + System.runFinalization(); runtime.gc(); + System.runFinalization(); return (runtime.totalMemory() - runtime.freeMemory()); } } From 8d4518cc883420a16f0326be55e9be2e29f654be Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 10 Feb 2020 01:47:45 -0700 Subject: [PATCH 0594/1778] Source cleanup --- .../classgraph/issues/issue400/Issue400.java | 63 ++++++++++--------- 1 file changed, 34 insertions(+), 29 deletions(-) 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 8e2017007..071df0aad 100644 --- a/src/test/perf/io/github/classgraph/issues/issue400/Issue400.java +++ b/src/test/perf/io/github/classgraph/issues/issue400/Issue400.java @@ -1,58 +1,44 @@ package io.github.classgraph.issues.issue400; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.data.Offset.offset; + import java.net.URL; import java.net.URLClassLoader; import org.junit.jupiter.api.Test; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.data.Offset.offset; - import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; /** - * Verify that a large number of stored/deflated nested JAR entries don't cause problems. + * Verify that a large number of stored/deflated nested JAR entries don't cause memory problems. + * + * @author Róbert Papp ( https://github.com/TWiStErRob ) */ public class Issue400 { private static final long MB = 1024 * 1024; private static final long MEMORY_TOLERANCE = 2 * MB; - @Test - public void loadsStoredJarWithManyNestedEntriesAndDoesNotUseMuchMemory() { - loadsJarWithManyNestedEntriesAndDoesNotUseMuchMemory( - Issue400.class.getClassLoader().getResource("issue400-nested-stored.jar")); - } - - @Test - public void loadsDeflatedJarWithManyNestedEntriesAndDoesNotUseMuchMemory() { - loadsJarWithManyNestedEntriesAndDoesNotUseMuchMemory( - Issue400.class.getClassLoader().getResource("issue400-nested-deflated.jar")); - } - - private void loadsJarWithManyNestedEntriesAndDoesNotUseMuchMemory(URL... jars) { - long ramAtStart = usedRam(); + @SuppressWarnings("null") + private void loadsJarWithManyNestedEntriesAndDoesNotUseMuchMemory(final URL... jars) { + final long ramAtStart = usedRam(); long ramAfterScan; - try ( - ScanResult scanResult = new ClassGraph() - .overrideClassLoaders(new URLClassLoader(jars)) - .enableAllInfo() - .scan() - ) { + try (ScanResult scanResult = new ClassGraph().overrideClassLoaders(new URLClassLoader(jars)).enableAllInfo() + .scan()) { ramAfterScan = usedRam(); // There are no classes in any of the JARs. assertThat(scanResult.getAllClassesAsMap()).isEmpty(); // Check if it contains the JAR and all nested entries. assertThat(scanResult.getClasspathURLs()).hasSize(1 + 128); } - long ramAtEnd = usedRam(); + final long ramAtEnd = usedRam(); assertThat(ramAfterScan) .withFailMessage("Memory usage while using ScanResult should stay within reasonable range: " + "went from %s to %s MB.", ramAtStart / MB, ramAfterScan / MB) - .isGreaterThanOrEqualTo(ramAtStart) - .isCloseTo(ramAtStart, offset(MEMORY_TOLERANCE)); + .isGreaterThanOrEqualTo(ramAtStart).isCloseTo(ramAtStart, offset(MEMORY_TOLERANCE)); assertThat(ramAtStart) .withFailMessage("Memory usage after cleaning up should stay within reasonable range: " @@ -60,12 +46,31 @@ private void loadsJarWithManyNestedEntriesAndDoesNotUseMuchMemory(URL... jars) { .isCloseTo(ramAtEnd, offset(MEMORY_TOLERANCE)); } + /** + * Test jar with stored entries. + */ + @Test + public void loadsStoredJarWithManyNestedEntriesAndDoesNotUseMuchMemory() { + loadsJarWithManyNestedEntriesAndDoesNotUseMuchMemory( + Issue400.class.getClassLoader().getResource("issue400-nested-stored.jar")); + } + + /** + * Test jar with deflated entries. + */ + @Test + public void loadsDeflatedJarWithManyNestedEntriesAndDoesNotUseMuchMemory() { + loadsJarWithManyNestedEntriesAndDoesNotUseMuchMemory( + Issue400.class.getClassLoader().getResource("issue400-nested-deflated.jar")); + } + /** * @return used JVM heap size allocated in RAM - * @see What are Runtime.getRuntime().totalMemory() and freeMemory()? + * @see What are Runtime.getRuntime().totalMemory() and + * freeMemory()? */ private long usedRam() { - Runtime runtime = Runtime.getRuntime(); + final Runtime runtime = Runtime.getRuntime(); runtime.gc(); System.runFinalization(); runtime.gc(); From 1c3c4dc85df8259f4e4d983b017225a3670ba589 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 10 Feb 2020 02:21:32 -0700 Subject: [PATCH 0595/1778] Fix logging issue --- .../fastzipfilereader/MappedByteBufferResources.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java index 5a6363720..d753e8a0c 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java @@ -140,11 +140,6 @@ public MappedByteBufferResources(final InputStream inputStream, final int inputS } if (spillToDisk) { // bytesRead == 0 => ran out of buffer space, spill over to disk - if (log != null) { - log.log("Could not fit InputStream content into max RAM buffer size of " - + scanSpec.maxBufferedJarRAMSize + " bytes, saving to temporary file: " + tempFileBaseName - + " -> " + this.mappedFile); - } try { this.mappedFile = nestedJarHandler.makeTempFile(tempFileBaseName, /* onlyUseLeafname = */ true); } catch (final IOException e) { @@ -153,6 +148,11 @@ public MappedByteBufferResources(final InputStream inputStream, final int inputS } throw e; } + if (log != null) { + log.log("Could not fit InputStream content into max RAM buffer size of " + + scanSpec.maxBufferedJarRAMSize + " bytes, saving to temporary file: " + tempFileBaseName + + " -> " + this.mappedFile); + } this.mappedFileIsTempFile = true; if (buf != null) { From dfd8d9373d8f799ebbfe823748db99d5b97a3853 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 10 Feb 2020 03:13:22 -0700 Subject: [PATCH 0596/1778] Fix some issues related to detecting stream length --- .../MappedByteBufferResources.java | 140 +++++++++++------- 1 file changed, 85 insertions(+), 55 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java index d753e8a0c..f3be74a22 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java @@ -39,6 +39,7 @@ import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.Arrays; import java.util.concurrent.atomic.AtomicBoolean; @@ -103,77 +104,106 @@ public MappedByteBufferResources(final InputStream inputStream, final int inputS throws IOException { this.nestedJarHandler = nestedJarHandler; final ScanSpec scanSpec = nestedJarHandler.scanSpec; - byte[] buf; - boolean spillToDisk; - if (inputStreamLengthHint != -1 && inputStreamLengthHint <= scanSpec.maxBufferedJarRAMSize) { - // inputStreamLengthHint indicates that inputStream is longer than scanSpec.maxJarRamSize, - // so try downloading to RAM - buf = new byte[inputStreamLengthHint == -1 ? scanSpec.maxBufferedJarRAMSize - : Math.min(scanSpec.maxBufferedJarRAMSize, inputStreamLengthHint)]; + if (inputStreamLengthHint <= scanSpec.maxBufferedJarRAMSize) { + // inputStreamLengthHint is unknown (-1) or shorter than scanSpec.maxJarRamSize, + // so try reading from the InputStream into an array of size scanSpec.maxBufferedJarRAMSize + // or inputStreamLengthHint respectively. + byte[] buf = new byte[inputStreamLengthHint == -1 ? scanSpec.maxBufferedJarRAMSize + : inputStreamLengthHint]; final int bufLength = buf.length; - int totBytesRead = 0; + int bufBytesUsed = 0; int bytesRead = 0; - while ((bytesRead = inputStream.read(buf, totBytesRead, bufLength - totBytesRead)) > 0) { + while ((bytesRead = inputStream.read(buf, bufBytesUsed, bufLength - bufBytesUsed)) > 0) { // Fill buffer until nothing more can be read - totBytesRead += bytesRead; + bufBytesUsed += bytesRead; } - if (bytesRead < 0) { - // Successfully reached end of stream -- wrap array buffer with ByteBuffer - if (totBytesRead < buf.length) { - // Trim array - buf = Arrays.copyOf(buf, totBytesRead); + boolean spilledToDisk = false; + 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 + final byte[] overflowBuf = new byte[1]; + final int overflowBufBytesUsed = inputStream.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, + // and we need to spill to disk, because buf is full + spillToDisk(inputStream, tempFileBaseName, buf, overflowBuf, scanSpec, log); + spilledToDisk = true; + } + // else (overflowBufBytesUsed == -1), so reached the end of the stream => don't spill to disk + } + if (!spilledToDisk) { + // Successfully reached end of stream + if (bufBytesUsed < buf.length) { + // Trim array if needed (this is needed if inputStreamLengthHint was -1, or overestimated + // the length of the InputStream) + buf = Arrays.copyOf(buf, bufBytesUsed); } // Wrap array in a RAM-backed ByteBuffer wrapByteBuffer(new ByteBufferWrapper(buf)); - spillToDisk = false; - } else { - // Didn't reach end of inputStream after buf was filled, so inputStreamLengthHint underestimated - // the length of the stream -- spill to disk, since we don't know how long the stream is now - spillToDisk = true; } + } else { - // inputStreamLengthHint indicates that inputStream is longer than scanSpec.maxJarRamSize, - // so immediately spill to disk - buf = null; - spillToDisk = true; + // inputStreamLengthHint is longer than scanSpec.maxJarRamSize, so immediately spill to disk + spillToDisk(inputStream, tempFileBaseName, /* buf = */ null, /* overflowBuf = */ null, scanSpec, log); } - if (spillToDisk) { - // bytesRead == 0 => ran out of buffer space, spill over to disk - try { - this.mappedFile = nestedJarHandler.makeTempFile(tempFileBaseName, /* onlyUseLeafname = */ true); - } catch (final IOException e) { - if (log != null) { - log.log("Could not create temporary file: " + e); - } - throw e; - } + } + + /** + * Spill an {@link InputStream} to disk if the stream is too large to fit in RAM. + * + * @param inputStream + * The {@link InputStream}. + * @param tempFileBaseName + * 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. + * @param overflowBuf + * The second buffer to write to the beginning of the file, or null if none. (Should have same + * nullity as buf.) + * @param scanSpec + * The scan spec. + * @param log + * The log. + * @throws IOException + * If anything went wrong creating or writing to the temp file. + */ + private void spillToDisk(final InputStream inputStream, final String tempFileBaseName, final byte[] buf, + final byte[] overflowBuf, final ScanSpec scanSpec, final LogNode log) throws IOException { + // Create temp file + try { + this.mappedFile = nestedJarHandler.makeTempFile(tempFileBaseName, /* onlyUseLeafname = */ true); + } catch (final IOException e) { if (log != null) { - log.log("Could not fit InputStream content into max RAM buffer size of " - + scanSpec.maxBufferedJarRAMSize + " bytes, saving to temporary file: " + tempFileBaseName - + " -> " + this.mappedFile); - } - this.mappedFileIsTempFile = true; - - if (buf != null) { - // If any content was already read from inputStream, flush it out to the temporary file - Files.write(this.mappedFile.toPath(), buf, StandardOpenOption.WRITE); - } else { - // Buffer was never allocated -- allocate one for the copy operation below - buf = new byte[8192]; + log.log("Could not create temporary file: " + e); } + throw e; + } + if (log != null) { + log.log("Could not fit InputStream content into max RAM buffer size, saving to temporary file: " + + tempFileBaseName + " -> " + this.mappedFile); + } + this.mappedFileIsTempFile = true; - // Copy the rest of the InputStream to the end of the temporary file - try (OutputStream os = new BufferedOutputStream( - new FileOutputStream(this.mappedFile, /* append = */ true))) { - for (int bytesReadCtd; (bytesReadCtd = inputStream.read(buf, 0, buf.length)) > 0;) { - os.write(buf, 0, bytesReadCtd); - } - } + // Write already-read buffered bytes to temp file, if anything was read + if (buf != null) { + final Path path = this.mappedFile.toPath(); + Files.write(path, buf, StandardOpenOption.WRITE); + Files.write(path, overflowBuf, StandardOpenOption.APPEND); + } - // Map the file to a MappedByteBuffer - mapFile(scanSpec.disableMemoryMapping, log); + // Copy the rest of the InputStream to the end of the temporary file + try (OutputStream os = new BufferedOutputStream( + new FileOutputStream(this.mappedFile, /* append = */ true))) { + // Copy the rest of the InputStream to the file + final byte[] copyBuf = new byte[8192]; + for (int bytesReadCtd; (bytesReadCtd = inputStream.read(copyBuf, 0, copyBuf.length)) > 0;) { + os.write(copyBuf, 0, bytesReadCtd); + } } + + // Map the file to a MappedByteBuffer + mapFile(scanSpec.disableMemoryMapping, log); } /** From 1aba3002e0730668b534a139417b297197eab13b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 10 Feb 2020 03:19:34 -0700 Subject: [PATCH 0597/1778] Handle zero-length InputStream better --- .../fastzipfilereader/MappedByteBufferResources.java | 10 +++++++--- .../nonapi/io/github/classgraph/utils/FileUtils.java | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java index f3be74a22..8e0437eaf 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java @@ -107,9 +107,13 @@ public MappedByteBufferResources(final InputStream inputStream, final int inputS if (inputStreamLengthHint <= scanSpec.maxBufferedJarRAMSize) { // inputStreamLengthHint is unknown (-1) or shorter than scanSpec.maxJarRamSize, // so try reading from the InputStream into an array of size scanSpec.maxBufferedJarRAMSize - // or inputStreamLengthHint respectively. - byte[] buf = new byte[inputStreamLengthHint == -1 ? scanSpec.maxBufferedJarRAMSize - : inputStreamLengthHint]; + // or inputStreamLengthHint respectively. Also if inputStreamLengthHint == 0, which may or + // may not be valid, use a buffer size of 8192 (which is a common buffer size, so the GC + // may be able to reuse an array object) -- also in case there really are zero bytes in the + // stream, this will not allocate a large array. + final int bufSize = inputStreamLengthHint == 0 ? 8192 + : inputStreamLengthHint == -1 ? scanSpec.maxBufferedJarRAMSize : inputStreamLengthHint; + byte[] buf = new byte[bufSize]; final int bufLength = buf.length; int bufBytesUsed = 0; 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 fbf65265f..00ed66abc 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -175,7 +175,7 @@ private static SimpleEntry readAllBytes(final InputStream input throw new IOException("InputStream is too large to read"); } final int bufferSize = fileSizeHint < 1L - // If fileSizeHint is 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 // lengths do not become a memory allocation attack vector From abc23b0f4cd04d246b08b123134eb51a08979fff Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 10 Feb 2020 03:24:10 -0700 Subject: [PATCH 0598/1778] Simplify code --- .../io/github/classgraph/utils/FileUtils.java | 26 ++++++------------- 1 file changed, 8 insertions(+), 18 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 00ed66abc..e53905370 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -42,7 +42,6 @@ import java.nio.file.Paths; import java.security.AccessController; import java.security.PrivilegedAction; -import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -164,12 +163,11 @@ private FileUtils() { * The {@link InputStream}. * @param fileSizeHint * The file size, if known, otherwise -1L. - * @return The contents of the {@link InputStream} as an Entry consisting of the byte array and number of bytes - * used in the array. + * @return The contents of the {@link InputStream} as a byte array. * @throws IOException * If the contents could not be read. */ - private static SimpleEntry readAllBytes(final InputStream inputStream, final long fileSizeHint) + private static byte[] readAllBytes(final InputStream inputStream, final long fileSizeHint) throws IOException { if (fileSizeHint > MAX_BUFFER_SIZE) { throw new IOException("InputStream is too large to read"); @@ -204,8 +202,7 @@ private static SimpleEntry readAllBytes(final InputStream input buf = Arrays.copyOf(buf, bufLength); } // Return buffer and number of bytes read - return new SimpleEntry<>((bufLength == totBytesRead) ? buf : Arrays.copyOf(buf, totBytesRead), - totBytesRead); + return (bufLength == totBytesRead) ? buf : Arrays.copyOf(buf, totBytesRead); } /** @@ -221,10 +218,7 @@ private static SimpleEntry readAllBytes(final InputStream input */ public static byte[] readAllBytesAsArray(final InputStream inputStream, final long fileSizeHint) throws IOException { - final SimpleEntry ent = readAllBytes(inputStream, fileSizeHint); - final byte[] buf = ent.getKey(); - final int bufBytesUsed = ent.getValue(); - return bufBytesUsed == buf.length ? buf : Arrays.copyOf(buf, bufBytesUsed); + return readAllBytes(inputStream, fileSizeHint); } /** @@ -240,10 +234,8 @@ public static byte[] readAllBytesAsArray(final InputStream inputStream, final lo */ public static ByteBuffer readAllBytesAsByteBuffer(final InputStream inputStream, final long fileSizeHint) throws IOException { - final SimpleEntry ent = readAllBytes(inputStream, fileSizeHint); - final byte[] buf = ent.getKey(); - final int bufBytesUsed = ent.getValue(); - return ByteBuffer.wrap(buf, 0, bufBytesUsed); + final byte[] buf = readAllBytes(inputStream, fileSizeHint); + return ByteBuffer.wrap(buf, 0, buf.length); } /** @@ -259,10 +251,8 @@ public static ByteBuffer readAllBytesAsByteBuffer(final InputStream inputStream, */ public static String readAllBytesAsString(final InputStream inputStream, final long fileSizeHint) throws IOException { - final SimpleEntry ent = readAllBytes(inputStream, fileSizeHint); - final byte[] buf = ent.getKey(); - final int bufBytesUsed = ent.getValue(); - return new String(buf, 0, bufBytesUsed, StandardCharsets.UTF_8); + final byte[] buf = readAllBytes(inputStream, fileSizeHint); + return new String(buf, 0, buf.length, StandardCharsets.UTF_8); } // ------------------------------------------------------------------------------------------------------------- From e440493495ea9d0f0a6b51f0a2c36718ec973021 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 10 Feb 2020 03:31:03 -0700 Subject: [PATCH 0599/1778] Add comment --- .../nonapi/io/github/classgraph/classpath/ClassLoaderOrder.java | 1 + 1 file changed, 1 insertion(+) 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 a06e9dfa2..520d57d70 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderOrder.java @@ -169,6 +169,7 @@ public void delegateTo(final ClassLoader classLoader, final boolean isParent, fi if (isParent) { allParentClassLoaders.add(classLoader); } + // Don't delegate to a classloader twice if (delegatedTo.add(classLoader)) { // Find ClassLoaderHandlerRegistryEntry for this classloader final ClassLoaderHandlerRegistryEntry entry = getRegistryEntry(classLoader, log); From f46ebe4cce5a1a6cce07ff33eeb0650bc865eda5 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 10 Feb 2020 03:36:12 -0700 Subject: [PATCH 0600/1778] Don't scan classpath if classloaders were overridden --- .../java/io/github/classgraph/Scanner.java | 44 +------ .../classgraph/classpath/ClasspathFinder.java | 115 ++++++++++++------ 2 files changed, 84 insertions(+), 75 deletions(-) diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 03c8166d9..e7da4472b 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -166,49 +166,13 @@ class Scanner implements Callable { this.classpathFinder = new ClasspathFinder(scanSpec, classpathFinderLog); this.classLoaderOrderRespectingParentDelegation = classpathFinder .getClassLoaderOrderRespectingParentDelegation(); - final ModuleFinder moduleFinder = new ModuleFinder(classpathFinder.getCallStack(), scanSpec, - classpathFinderLog); - - // 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.overrideClasspath != null) { - // Don't scan modules if classpath is overridden - scanModules = false; - } else if (scanSpec.overrideClassLoaders != null) { - // If classloaders are overridden, only scan modules if an override classloader is a JPMS - // AppClassLoader or PlatformClassLoader - scanModules = 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; - } 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 " - + "jdk.internal.loader.ClassLoaders$PlatformClassLoader, which is a system " - + "classloader, so enableSystemJarsAndModules() was called automatically"); - } - scanSpec.enableSystemJarsAndModules = true; - } - } - } - } else { - // If classloaders are not overridden and classpath is not overridden, only scan modules - // if module scanning is enabled - scanModules = scanSpec.scanModules; - } try { this.moduleOrder = new ArrayList<>(); - if (scanModules) { + + // Check if modules should be scanned + final ModuleFinder moduleFinder = classpathFinder.getModuleFinder(); + if (moduleFinder != null) { // Add modules to start of classpath order, before traditional classpath final List systemModuleRefs = moduleFinder.getSystemModuleRefs(); final ClassLoader defaultClassLoader = classLoaderOrderRespectingParentDelegation != null 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 17ce9b991..d70208172 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java @@ -46,8 +46,8 @@ public class ClasspathFinder { /** The classpath order. */ private final ClasspathOrder classpathOrder; - /** The classloader finder. */ - private final ClassLoaderFinder classLoaderFinder; + /** The {@link ModuleFinder}, if modules are to be scanned. */ + private final ModuleFinder moduleFinder; /** * The default order in which ClassLoaders are called to load classes, respecting parent-first/parent-last @@ -67,12 +67,12 @@ public ClasspathOrder getClasspathOrder() { } /** - * Get the callstack. + * Get the {@link ModuleFinder}. * - * @return The callstack. + * @return The {@link ModuleFinder}. */ - public Class[] getCallStack() { - return classLoaderFinder.getCallStack(); + public ModuleFinder getModuleFinder() { + return moduleFinder; } /** @@ -97,16 +97,57 @@ public ClassLoader[] getClassLoaderOrderRespectingParentDelegation() { public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { final LogNode classpathFinderLog = log == null ? null : log.log("Finding classpath and modules"); - // If system jars are not blacklisted, add JRE rt.jar to the beginning of the classpath - final String jreRtJar = SystemJarFinder.getJreRtJarPath(); - final boolean scanAllLibOrExtJars = !scanSpec.libOrExtJarWhiteBlackList.whitelistAndBlacklistAreEmpty(); + // 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) + : null; - classLoaderFinder = new ClassLoaderFinder(scanSpec, classpathFinderLog); + // 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.overrideClasspath != null) { + // Don't scan modules if classpath is overridden + scanModules = false; + } else if (scanSpec.overrideClassLoaders != null) { + // If classloaders are overridden, only scan modules if an override classloader is a JPMS + // AppClassLoader or PlatformClassLoader + scanModules = 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; + } 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 " + + "jdk.internal.loader.ClassLoaders$PlatformClassLoader, which is a system " + + "classloader, so enableSystemJarsAndModules() was called automatically"); + } + scanSpec.enableSystemJarsAndModules = true; + } + } + } + } else { + // If classloaders are not overridden and classpath is not overridden, only scan modules + // if module scanning is enabled + scanModules = scanSpec.scanModules; + } + + moduleFinder = scanModules && classLoaderFinder != null + ? new ModuleFinder(classLoaderFinder.getCallStack(), scanSpec, classpathFinderLog) + : null; classpathOrder = new ClasspathOrder(scanSpec); final ClasspathOrder ignoredClasspathOrder = new ClasspathOrder(scanSpec); - final ClassLoader[] contextClassLoaders = classLoaderFinder.getContextClassLoaders(); + final ClassLoader[] contextClassLoaders = classLoaderFinder == null ? new ClassLoader[0] + : classLoaderFinder.getContextClassLoaders(); final ClassLoader defaultClassLoader = contextClassLoaders != null && contextClassLoaders.length > 0 ? contextClassLoaders[0] : null; @@ -130,7 +171,10 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { } classLoaderOrderRespectingParentDelegation = contextClassLoaders; - } else { + } else if (scanSpec.overrideClassLoaders == null) { + // If system jars are not blacklisted, add JRE rt.jar to the beginning of the classpath + final String jreRtJar = SystemJarFinder.getJreRtJarPath(); + // Add rt.jar and/or lib/ext jars to beginning of classpath, if enabled final LogNode systemJarsLog = classpathFinderLog == null ? null : classpathFinderLog.log("System jars:"); @@ -145,6 +189,7 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { + jreRtJar); } } + final boolean scanAllLibOrExtJars = !scanSpec.libOrExtJarWhiteBlackList.whitelistAndBlacklistAreEmpty(); for (final String libOrExtJarPath : SystemJarFinder.getJreLibOrExtJars()) { if (scanAllLibOrExtJars || scanSpec.libOrExtJarWhiteBlackList .isSpecificallyWhitelistedAndNotBlacklisted(libOrExtJarPath)) { @@ -156,7 +201,9 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { systemJarsLog.log("Scanning disabled for lib or ext jar: " + libOrExtJarPath); } } + } + if (scanSpec.overrideClasspath == null) { // List ClassLoaderHandlers if (classpathFinderLog != null) { final LogNode classLoaderHandlerLog = classpathFinderLog.log("ClassLoaderHandlers:"); @@ -215,30 +262,28 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { // Need to record the classloader delegation order, in particular to respect parent-last delegation // order, since this is not the default (issue #267). classLoaderOrderRespectingParentDelegation = finalClassLoaderOrder.toArray(new ClassLoader[0]); + } - // Get classpath elements from java.class.path, but don't add them if the element is in an ignored - // parent classloader and not in a child classloader (and don't use java.class.path at all if - // overrideClassLoaders is true or overrideClasspath is set) - if (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 - : classpathFinderLog.log("Getting classpath entries from java.class.path"); - for (final String pathElement : pathElements) { - final String pathElementResolved = FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, - pathElement); - if (!ignoredClasspathOrder.getClasspathEntryUniqueResolvedPaths() - .contains(pathElementResolved)) { - // pathElement is not also listed in an ignored parent classloader - classpathOrder.addClasspathEntry(pathElement, defaultClassLoader, scanSpec, sysPropLog); - } else { - // pathElement is also listed in an ignored parent classloader, ignore it (Issue #169) - if (sysPropLog != null) { - sysPropLog.log("Found classpath element in java.class.path that will be ignored, " - + "since it is also found in an ignored parent classloader: " - + pathElement); - } + // Get classpath elements from java.class.path, but don't add them if the element is in an ignored + // parent classloader and not in a child classloader (and don't use java.class.path at all if + // overrideClassLoaders is true or overrideClasspath is set) + if (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 + : classpathFinderLog.log("Getting classpath entries from java.class.path"); + for (final String pathElement : pathElements) { + final String pathElementResolved = FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, + pathElement); + if (!ignoredClasspathOrder.getClasspathEntryUniqueResolvedPaths() + .contains(pathElementResolved)) { + // pathElement is not also listed in an ignored parent classloader + classpathOrder.addClasspathEntry(pathElement, defaultClassLoader, scanSpec, sysPropLog); + } else { + // pathElement is also listed in an ignored parent classloader, ignore it (Issue #169) + if (sysPropLog != null) { + sysPropLog.log("Found classpath element in java.class.path that will be ignored, " + + "since it is also found in an ignored parent classloader: " + pathElement); } } } From af89873370325a4a81e5b14fb26fa57877e9ffa6 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 10 Feb 2020 03:36:43 -0700 Subject: [PATCH 0601/1778] Reorder methods and ignore parent classloaders --- .../classgraph/issues/issue400/Issue400.java | 42 +++++++++++-------- 1 file changed, 24 insertions(+), 18 deletions(-) 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 071df0aad..f3726531d 100644 --- a/src/test/perf/io/github/classgraph/issues/issue400/Issue400.java +++ b/src/test/perf/io/github/classgraph/issues/issue400/Issue400.java @@ -21,12 +21,34 @@ public class Issue400 { private static final long MB = 1024 * 1024; private static final long MEMORY_TOLERANCE = 2 * MB; + /** + * @return used JVM heap size allocated in RAM + * @see What are Runtime.getRuntime().totalMemory() and + * freeMemory()? + */ + private long usedRam() { + final Runtime runtime = Runtime.getRuntime(); + runtime.gc(); + System.runFinalization(); + runtime.gc(); + System.runFinalization(); + runtime.gc(); + System.runFinalization(); + return (runtime.totalMemory() - runtime.freeMemory()); + } + + /** + * Test whether RAM leaks, or whether nested deflated jars cause large RAM overhead. + * + * @param jars + * the jar URLs. + */ @SuppressWarnings("null") private void loadsJarWithManyNestedEntriesAndDoesNotUseMuchMemory(final URL... jars) { final long ramAtStart = usedRam(); long ramAfterScan; - try (ScanResult scanResult = new ClassGraph().overrideClassLoaders(new URLClassLoader(jars)).enableAllInfo() - .scan()) { + try (ScanResult scanResult = new ClassGraph().overrideClassLoaders(new URLClassLoader(jars)) + .ignoreParentClassLoaders().enableAllInfo().scan()) { ramAfterScan = usedRam(); // There are no classes in any of the JARs. assertThat(scanResult.getAllClassesAsMap()).isEmpty(); @@ -63,20 +85,4 @@ public void loadsDeflatedJarWithManyNestedEntriesAndDoesNotUseMuchMemory() { loadsJarWithManyNestedEntriesAndDoesNotUseMuchMemory( Issue400.class.getClassLoader().getResource("issue400-nested-deflated.jar")); } - - /** - * @return used JVM heap size allocated in RAM - * @see What are Runtime.getRuntime().totalMemory() and - * freeMemory()? - */ - private long usedRam() { - final Runtime runtime = Runtime.getRuntime(); - runtime.gc(); - System.runFinalization(); - runtime.gc(); - System.runFinalization(); - runtime.gc(); - System.runFinalization(); - return (runtime.totalMemory() - runtime.freeMemory()); - } } From a1003ae09bd8b635b072fde288ca765c41531323 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 10 Feb 2020 03:40:42 -0700 Subject: [PATCH 0602/1778] Make test more tolerant --- .../perf/io/github/classgraph/issues/issue400/Issue400.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 f3726531d..3e3b87840 100644 --- a/src/test/perf/io/github/classgraph/issues/issue400/Issue400.java +++ b/src/test/perf/io/github/classgraph/issues/issue400/Issue400.java @@ -19,7 +19,7 @@ public class Issue400 { private static final long MB = 1024 * 1024; - private static final long MEMORY_TOLERANCE = 2 * MB; + private static final long MEMORY_TOLERANCE = 4 * MB; /** * @return used JVM heap size allocated in RAM @@ -57,10 +57,10 @@ private void loadsJarWithManyNestedEntriesAndDoesNotUseMuchMemory(final URL... j } final long ramAtEnd = usedRam(); - assertThat(ramAfterScan) + assertThat(ramAtStart) .withFailMessage("Memory usage while using ScanResult should stay within reasonable range: " + "went from %s to %s MB.", ramAtStart / MB, ramAfterScan / MB) - .isGreaterThanOrEqualTo(ramAtStart).isCloseTo(ramAtStart, offset(MEMORY_TOLERANCE)); + .isCloseTo(ramAfterScan, offset(MEMORY_TOLERANCE)); assertThat(ramAtStart) .withFailMessage("Memory usage after cleaning up should stay within reasonable range: " From 2db69e68e5fa571328de92d0bb75f9d333072162 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 10 Feb 2020 04:24:23 -0700 Subject: [PATCH 0603/1778] Avoid infinite loop on interruption (#400) --- .../fastzipfilereader/NestedJarHandler.java | 30 ++++++++++++++----- 1 file changed, 22 insertions(+), 8 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 2a207b8b6..579dd9800 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java @@ -563,18 +563,24 @@ private PhysicalZipFile downloadJarFromURL(final String jarURL, final LogNode lo */ public void close(final LogNode log) { if (!closed.getAndSet(true)) { + boolean interrupted = false; if (inflaterRecycler != null) { inflaterRecycler.forceClose(); inflaterRecycler = null; } if (moduleRefToModuleReaderProxyRecyclerMap != null) { - try { - for (final Recycler recycler : // - moduleRefToModuleReaderProxyRecyclerMap.values()) { - recycler.forceClose(); + boolean completedWithoutInterruption = false; + while (!completedWithoutInterruption) { + try { + for (final Recycler recycler : // + moduleRefToModuleReaderProxyRecyclerMap.values()) { + recycler.forceClose(); + } + completedWithoutInterruption = true; + } catch (final InterruptedException e) { + // Try again if interrupted + interrupted = true; } - } catch (final InterruptedException e) { - interruptionChecker.interrupt(); } moduleRefToModuleReaderProxyRecyclerMap.clear(); moduleRefToModuleReaderProxyRecyclerMap = null; @@ -593,13 +599,18 @@ public void close(final LogNode log) { if (canonicalFileToPhysicalZipFileMap != null) { while (!canonicalFileToPhysicalZipFileMap.isEmpty()) { try { - for (final Entry ent : canonicalFileToPhysicalZipFileMap.entries()) { + for (final Entry ent : new ArrayList<>( + canonicalFileToPhysicalZipFileMap.entries())) { final PhysicalZipFile physicalZipFile = ent.getValue(); physicalZipFile.close(); canonicalFileToPhysicalZipFileMap.remove(ent.getKey()); } } catch (final InterruptedException e) { - interruptionChecker.interrupt(); + // If thread was interrupted, canonicalFileToPhysicalZipFileMap.entries() is interrupted + // above, so canonicalFileToPhysicalZipFileMap.remove(ent.getKey()) is never called, + // which causes the while loop to loop forever if we re-interrupt here (#400). Therefore + // delay re-interruption until the end of this method. + interrupted = false; } } canonicalFileToPhysicalZipFileMap = null; @@ -648,6 +659,9 @@ public void close(final LogNode log) { } tempFiles = null; } + if (interrupted) { + interruptionChecker.interrupt(); + } } } } From 4fa22c37fd640b1e4555a172a08ad6bfac7877f7 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 10 Feb 2020 04:53:08 -0700 Subject: [PATCH 0604/1778] Set minimum buffer size to 16kB, in case size hint is small and wrong --- .../MappedByteBufferResources.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java index 8e0437eaf..0d7e4f051 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java @@ -81,6 +81,13 @@ public class MappedByteBufferResources { /** Set to true once {@link #close()} has been called. */ private final AtomicBoolean closed = new AtomicBoolean(false); + /** + * Minimum buffer size, to ensure that erroneous zip entry sizes, which are used as size hints (e.g. + * uncompressed size == 0) do not result in a buffer being too small, which would result in unnecessary + * temporary files being created. + */ + private static final int MIN_BUFFER_SIZE = 16384; + /** * Read all the bytes in an {@link InputStream}, with spillover to a temporary file on disk if a maximum buffer * size is exceeded. @@ -108,11 +115,10 @@ public MappedByteBufferResources(final InputStream inputStream, final int inputS // inputStreamLengthHint is unknown (-1) or shorter than scanSpec.maxJarRamSize, // 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 8192 (which is a common buffer size, so the GC - // may be able to reuse an array object) -- also in case there really are zero bytes in the - // stream, this will not allocate a large array. - final int bufSize = inputStreamLengthHint == 0 ? 8192 - : inputStreamLengthHint == -1 ? scanSpec.maxBufferedJarRAMSize : inputStreamLengthHint; + // may not be valid (or in general, inputStreamLengthHint < MIN_BUFFER_SIZE), use a buffer + // size of MIN_BUFFER_SIZE. + final int bufSize = inputStreamLengthHint == -1 ? scanSpec.maxBufferedJarRAMSize + : inputStreamLengthHint < MIN_BUFFER_SIZE ? MIN_BUFFER_SIZE : inputStreamLengthHint; byte[] buf = new byte[bufSize]; final int bufLength = buf.length; From 0e2aae8169ec71f5f81ca235374e9649c24f664e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 10 Feb 2020 04:55:47 -0700 Subject: [PATCH 0605/1778] Only open output file once when spilling to disk --- .../MappedByteBufferResources.java | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java index 0d7e4f051..40f0a036c 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java @@ -195,20 +195,17 @@ private void spillToDisk(final InputStream inputStream, final String tempFileBas } this.mappedFileIsTempFile = true; - // Write already-read buffered bytes to temp file, if anything was read - if (buf != null) { - final Path path = this.mappedFile.toPath(); - Files.write(path, buf, StandardOpenOption.WRITE); - Files.write(path, overflowBuf, StandardOpenOption.APPEND); - } - // Copy the rest of the InputStream to the end of the temporary file - try (OutputStream os = new BufferedOutputStream( - new FileOutputStream(this.mappedFile, /* append = */ true))) { + try (OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(this.mappedFile))) { + // Write already-read buffered bytes to temp file, if anything was read + if (buf != null) { + outputStream.write(buf); + outputStream.write(overflowBuf); + } // Copy the rest of the InputStream to the file final byte[] copyBuf = new byte[8192]; - for (int bytesReadCtd; (bytesReadCtd = inputStream.read(copyBuf, 0, copyBuf.length)) > 0;) { - os.write(copyBuf, 0, bytesReadCtd); + for (int bytesRead; (bytesRead = inputStream.read(copyBuf, 0, copyBuf.length)) > 0;) { + outputStream.write(copyBuf, 0, bytesRead); } } From 45a89d8e8b91467b2990a14fc33a80dc7b7d4666 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 10 Feb 2020 04:57:02 -0700 Subject: [PATCH 0606/1778] Small change --- .../fastzipfilereader/MappedByteBufferResources.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java index 40f0a036c..f49a98124 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java @@ -38,9 +38,6 @@ import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardOpenOption; import java.util.Arrays; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; From 5af4d4564c6c2c300b88c6d6b407439b4900648d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 10 Feb 2020 05:45:37 -0700 Subject: [PATCH 0607/1778] Fix comments --- src/main/java/io/github/classgraph/Resource.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/github/classgraph/Resource.java b/src/main/java/io/github/classgraph/Resource.java index e96e06259..c44762065 100644 --- a/src/main/java/io/github/classgraph/Resource.java +++ b/src/main/java/io/github/classgraph/Resource.java @@ -355,7 +355,7 @@ public URI getURI() { /** * Get the {@link URL} representing the resource's location. Use {@link #getURI()} instead if the resource may * have come from a system module, or if this is a jlink'd runtime image, since "jrt:" URI schemes used by - * system modules and jlink'd runtime images are not suppored by {@link URL}, and this will cause + * system modules and jlink'd runtime images are not supported by {@link URL}, and this will cause * {@link IllegalArgumentException} to be thrown. * * @return A {@link URL} representing the resource's location. @@ -382,7 +382,7 @@ public URI getClasspathElementURI() { * Get the {@link URL} of the classpath element or module that this resource was obtained from. Use * {@link #getClasspathElementURI()} instead if the resource may have come from a system module, or if this is a * jlink'd runtime image, since "jrt:" URI schemes used by system modules and jlink'd runtime images are not - * suppored by {@link URL}, and this will cause {@link IllegalArgumentException} to be thrown. + * supported by {@link URL}, and this will cause {@link IllegalArgumentException} to be thrown. * * @return The {@link URL} of the classpath element or module that this resource was found within. * @throws IllegalArgumentException @@ -588,7 +588,7 @@ public int compareTo(final Resource o) { /** Close the underlying InputStream, or release/unmap the underlying ByteBuffer. */ @Override public void close() { - // Override in subclasses, and call super.close() + // Override in subclasses, and call super.close(), then at end, markAsClosed() if (inputStream != null) { try { if (inputStream instanceof InputStreamResourceCloser) { From 05dce2153576b11b66783d80809e0469197efb11 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 10 Feb 2020 05:56:28 -0700 Subject: [PATCH 0608/1778] Remove comment --- src/main/java/io/github/classgraph/ArrayClassInfo.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/ArrayClassInfo.java b/src/main/java/io/github/classgraph/ArrayClassInfo.java index 60f13b40d..75c82eee2 100644 --- a/src/main/java/io/github/classgraph/ArrayClassInfo.java +++ b/src/main/java/io/github/classgraph/ArrayClassInfo.java @@ -71,7 +71,6 @@ public class ArrayClassInfo extends ClassInfo { */ @Override void setScanResult(final ScanResult scanResult) { - // TODO Auto-generated method stub super.setScanResult(scanResult); } From 3c1683d1b7d3ddc010d06551f030a0b5fb4471cd Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 10 Feb 2020 06:03:33 -0700 Subject: [PATCH 0609/1778] Don't mark as open twice --- src/main/java/io/github/classgraph/ClasspathElementDir.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementDir.java b/src/main/java/io/github/classgraph/ClasspathElementDir.java index a9e74763f..521d4c454 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementDir.java @@ -234,11 +234,6 @@ synchronized ByteBufferWrapper readWrapped() throws IOException { @Override public synchronized ByteBuffer read() throws IOException { - if (skipClasspathElement) { - // Shouldn't happen - throw new IOException("Parent directory could not be opened"); - } - markAsOpen(); try { readWrapped(); final ByteBuffer buf = byteBufferWrapper.getByteBuffer(); From 82b5cfbd79f18143a17a901a3f2bfbc76946ce19 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 10 Feb 2020 06:08:36 -0700 Subject: [PATCH 0610/1778] Windows compat fix --- .../java/io/github/classgraph/issues/issue340/Issue340.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/test/java/io/github/classgraph/issues/issue340/Issue340.java b/src/test/java/io/github/classgraph/issues/issue340/Issue340.java index b22a35c36..113d49fe0 100644 --- a/src/test/java/io/github/classgraph/issues/issue340/Issue340.java +++ b/src/test/java/io/github/classgraph/issues/issue340/Issue340.java @@ -10,6 +10,8 @@ import io.github.classgraph.Resource; import io.github.classgraph.ScanResult; import nonapi.io.github.classgraph.utils.FastPathResolver; +import nonapi.io.github.classgraph.utils.VersionFinder; +import nonapi.io.github.classgraph.utils.VersionFinder.OperatingSystem; /** * Unit test. @@ -25,7 +27,8 @@ public void test() { assertThat(FastPathResolver.resolve("/x", "../y")).isEqualTo("/y"); assertThat(FastPathResolver.resolve("/x", "../../y")).isEqualTo("/y"); assertThat(FastPathResolver.resolve("/x/y/z", "..//..////w")).isEqualTo("/x/w"); - assertThat(FastPathResolver.resolve("/x/y/z", "//p//q")).isEqualTo("/p/q"); + assertThat(FastPathResolver.resolve("/x/y/z", "//p//q")) + .isEqualTo(VersionFinder.OS == OperatingSystem.Windows ? "//p/q" : "/p/q"); try (ScanResult scanResult = new ClassGraph() .overrideClasspath(getClass().getClassLoader().getResource("issue340.jar").getPath()).scan()) { From e190e37c7bfd5bb09822b66b8c7633b94f55eac8 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 10 Feb 2020 06:18:00 -0700 Subject: [PATCH 0611/1778] Add comments --- .../nonapi/io/github/classgraph/utils/FastPathResolver.java | 2 ++ 1 file changed, 2 insertions(+) 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 483841e30..8df69964c 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FastPathResolver.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FastPathResolver.java @@ -247,11 +247,13 @@ public static String resolve(final String resolveBasePath, final String relative if (WINDOWS) { if (relativePath.length() - startIdx > 2 && Character.isLetter(relativePath.charAt(startIdx)) && relativePath.charAt(startIdx + 1) == ':') { + // Path like "C:/xyz" isAbsolutePath = true; } else if (relativePath.length() - startIdx > 3 && (relativePath.charAt(startIdx) == '/' || relativePath.charAt(startIdx) == '\\') && Character.isLetter(relativePath.charAt(startIdx + 1)) && relativePath.charAt(startIdx + 2) == ':') { + // Path like "/C:/xyz" isAbsolutePath = true; startIdx++; } From 448c1b8da208385a9bd98f42789856913886d36c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 10 Feb 2020 06:49:40 -0700 Subject: [PATCH 0612/1778] Windows drive letter compatibility fixes --- .../classgraph/utils/URLPathEncoder.java | 62 ++++++++++++++++++- 1 file changed, 59 insertions(+), 3 deletions(-) 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 28fb06bd2..0b9f6bde0 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/URLPathEncoder.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/URLPathEncoder.java @@ -30,6 +30,8 @@ import java.nio.charset.StandardCharsets; +import nonapi.io.github.classgraph.utils.VersionFinder.OperatingSystem; + /** A simple URL path encoder. */ public final class URLPathEncoder { @@ -84,6 +86,18 @@ public static String encodePath(final String path) { break; } } + // 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() - 1 && Character.isLetter(path.charAt(i)) && path.charAt(i + 1) == ':') { + validColonPrefixLen = i + 2; + } + } + + // Apply URL encoding rules to rest of path final byte[] pathBytes = path.getBytes(StandardCharsets.UTF_8); final StringBuilder encodedPath = new StringBuilder(pathBytes.length * 3); for (int i = 0; i < pathBytes.length; i++) { @@ -111,11 +125,53 @@ public static String normalizeURLPath(final String urlPath) { String urlPathNormalized = urlPath; if (!urlPathNormalized.startsWith("jrt:") && !urlPathNormalized.startsWith("http://") && !urlPathNormalized.startsWith("https://")) { - // Any URL with the "jar:" prefix must have "/" after any "!" + + // String "jar:" and/or "file:", if already present + if (urlPathNormalized.startsWith("jar:")) { + urlPathNormalized = urlPathNormalized.substring(4); + } + if (urlPathNormalized.startsWith("file:")) { + urlPathNormalized = urlPathNormalized.substring(4); + } + + // On Windows, remove drive prefix from path, if present (otherwise the ':' after the drive + // letter will be escaped as %3A) + String windowsDrivePrefix = ""; + if (VersionFinder.OS == OperatingSystem.Windows) { + if (urlPathNormalized.length() >= 2 && Character.isLetter(urlPathNormalized.charAt(0)) + && urlPathNormalized.charAt(1) == ':') { + // Path of form "C:/xyz" + windowsDrivePrefix = urlPathNormalized.substring(0, 2); + urlPathNormalized = urlPathNormalized.substring(2); + } else if (urlPathNormalized.length() >= 3 && urlPathNormalized.charAt(0) == '/' + && Character.isLetter(urlPathNormalized.charAt(1)) && urlPathNormalized.charAt(2) == ':') { + // Path of form "/C:/xyz" + windowsDrivePrefix = urlPathNormalized.substring(1, 3); + urlPathNormalized = urlPathNormalized.substring(3); + } + } + + // Any URL containing "!" segments must have "/" after "!" for the "jar:" URL scheme to work urlPathNormalized = urlPathNormalized.replace("/!", "!").replace("!/", "!").replace("!", "!/"); - // Prepend "jar:file:" + + // Prepend "file:/" + if (windowsDrivePrefix.isEmpty()) { + // There is no Windows drive + urlPathNormalized = urlPathNormalized.startsWith("/") ? "file:" + urlPathNormalized + : "file:/" + urlPathNormalized; + } else { + // There is a Windows drive + urlPathNormalized = "file:/" + windowsDrivePrefix + + (urlPathNormalized.startsWith("/") ? urlPathNormalized : "/" + urlPathNormalized); + } + if (!urlPathNormalized.startsWith("file:") && !urlPathNormalized.startsWith("jar:")) { - urlPathNormalized = "file:" + urlPathNormalized; + // Add "/" after "file:" if it's not already there -- all paths should be absolute by this point + if (!urlPathNormalized.startsWith("/") || !(VersionFinder.OS == OperatingSystem.Windows)) { + urlPathNormalized = "file:/" + urlPathNormalized; + } else { + urlPathNormalized = "file:" + urlPathNormalized; + } } if (urlPathNormalized.contains("!") && !urlPathNormalized.startsWith("jar:")) { urlPathNormalized = "jar:" + urlPathNormalized; From c4010c645a9d985751cee3c944c04467d008af4f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 10 Feb 2020 07:00:24 -0700 Subject: [PATCH 0613/1778] Amend previous commit --- .../io/github/classgraph/utils/URLPathEncoder.java | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) 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 0b9f6bde0..4a3abf2f9 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/URLPathEncoder.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/URLPathEncoder.java @@ -165,14 +165,7 @@ public static String normalizeURLPath(final String urlPath) { + (urlPathNormalized.startsWith("/") ? urlPathNormalized : "/" + urlPathNormalized); } - if (!urlPathNormalized.startsWith("file:") && !urlPathNormalized.startsWith("jar:")) { - // Add "/" after "file:" if it's not already there -- all paths should be absolute by this point - if (!urlPathNormalized.startsWith("/") || !(VersionFinder.OS == OperatingSystem.Windows)) { - urlPathNormalized = "file:/" + urlPathNormalized; - } else { - urlPathNormalized = "file:" + urlPathNormalized; - } - } + // Prepend "jar:" if path contains a "!" segment if (urlPathNormalized.contains("!") && !urlPathNormalized.startsWith("jar:")) { urlPathNormalized = "jar:" + urlPathNormalized; } From dbcefd481e1f7b64198ec7fc3d2f328eb22fc646 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 10 Feb 2020 07:00:50 -0700 Subject: [PATCH 0614/1778] Fix comment --- .../java/nonapi/io/github/classgraph/utils/URLPathEncoder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 4a3abf2f9..faf92746b 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/URLPathEncoder.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/URLPathEncoder.java @@ -126,7 +126,7 @@ public static String normalizeURLPath(final String urlPath) { if (!urlPathNormalized.startsWith("jrt:") && !urlPathNormalized.startsWith("http://") && !urlPathNormalized.startsWith("https://")) { - // String "jar:" and/or "file:", if already present + // Strip "jar:" and/or "file:", if already present if (urlPathNormalized.startsWith("jar:")) { urlPathNormalized = urlPathNormalized.substring(4); } From a7fe577adbea4ac0b95ff030d40fb7b7a4ba2714 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 10 Feb 2020 07:19:55 -0700 Subject: [PATCH 0615/1778] Remove unused parameter --- .../github/classgraph/fastzipfilereader/NestedJarHandler.java | 4 ++-- .../github/classgraph/fastzipfilereader/PhysicalZipFile.java | 3 +-- 2 files changed, 3 insertions(+), 4 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 579dd9800..f56e1f52b 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java @@ -127,7 +127,7 @@ public ZipFileSlice newInstance(final FastZipEntry childZipEntry, final LogNode && childZipEntry.uncompressedSize < FileUtils.MAX_BUFFER_SIZE ? (int) childZipEntry.uncompressedSize : -1, - childZipEntry.entryName, NestedJarHandler.this, scanSpec, log); + childZipEntry.entryName, NestedJarHandler.this, log); allocatedPhysicalZipFiles.add(physicalZipFile); // Create a new logical slice of the extracted inner zipfile @@ -537,7 +537,7 @@ private PhysicalZipFile downloadJarFromURL(final String jarURL, final LogNode lo try (InputStream inputStream = url.openStream()) { // 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, /* length unknown */ -1, - jarURL, this, scanSpec, log); + jarURL, this, log); allocatedPhysicalZipFiles.add(physicalZipFile); return physicalZipFile; 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 cb422b699..d64548024 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/PhysicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/PhysicalZipFile.java @@ -38,7 +38,6 @@ import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; -import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.FastPathResolver; import nonapi.io.github.classgraph.utils.FileUtils; import nonapi.io.github.classgraph.utils.LogNode; @@ -135,7 +134,7 @@ class PhysicalZipFile implements Closeable { * if an I/O exception occurs. */ PhysicalZipFile(final InputStream inputStream, final int inputStreamLengthHint, final String path, - final NestedJarHandler nestedJarHandler, final ScanSpec scanSpec, final LogNode log) + final NestedJarHandler nestedJarHandler, final LogNode log) throws IOException { this.nestedJarHandler = nestedJarHandler; this.path = path; From a09cf1b00012a83db4d5aac9617f880dffb912da Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 11 Feb 2020 21:45:22 -0700 Subject: [PATCH 0616/1778] Small fixes for readAllBytes() --- .../io/github/classgraph/utils/FileUtils.java | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 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 e53905370..9b4a5b292 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -167,8 +167,7 @@ private FileUtils() { * @throws IOException * If the contents could not be read. */ - private static byte[] readAllBytes(final InputStream inputStream, final long fileSizeHint) - throws IOException { + private static byte[] readAllBytes(final InputStream inputStream, final long fileSizeHint) throws IOException { if (fileSizeHint > MAX_BUFFER_SIZE) { throw new IOException("InputStream is too large to read"); } @@ -179,10 +178,9 @@ private static byte[] readAllBytes(final InputStream inputStream, final long fil // lengths do not become a memory allocation attack vector : Math.min((int) fileSizeHint, MAX_INITIAL_BUFFER_SIZE); byte[] buf = new byte[bufferSize]; - int bufLength = buf.length; int totBytesRead = 0; for (int bytesRead;;) { - while ((bytesRead = inputStream.read(buf, totBytesRead, bufLength - totBytesRead)) > 0) { + while ((bytesRead = inputStream.read(buf, totBytesRead, buf.length - totBytesRead)) > 0) { // Fill buffer until nothing more can be read totBytesRead += bytesRead; } @@ -190,19 +188,24 @@ private static byte[] readAllBytes(final InputStream inputStream, final long fil // Reached end of stream break; } - // bytesRead == 0 => grow buffer, avoiding overflow - if (bufLength <= MAX_BUFFER_SIZE - bufLength) { - bufLength = bufLength << 1; + // bytesRead == 0 => grow buffer (avoid integer overflow in next line) + if (buf.length <= MAX_BUFFER_SIZE - buf.length) { + buf = Arrays.copyOf(buf, buf.length * 2); } else { - if (bufLength == MAX_BUFFER_SIZE) { - throw new IOException("InputStream too large to read into array"); + if (buf.length == MAX_BUFFER_SIZE) { + // Try reading one more byte, just in case the stream is exactly MAX_BUFFER_SIZE in length + if (inputStream.read() == -1) { + break; + } else { + throw new IOException("InputStream too large to read into array"); + } } - bufLength = MAX_BUFFER_SIZE; + // Can't double the size of the buffer, but increase it to max size + buf = Arrays.copyOf(buf, MAX_BUFFER_SIZE); } - buf = Arrays.copyOf(buf, bufLength); } // Return buffer and number of bytes read - return (bufLength == totBytesRead) ? buf : Arrays.copyOf(buf, totBytesRead); + return totBytesRead == buf.length ? buf : Arrays.copyOf(buf, totBytesRead); } /** From 876ddc04b0f7932c755bfd2738297da6a772fe7e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 13 Feb 2020 09:22:22 -0700 Subject: [PATCH 0617/1778] Replace file slicing layer and remove mmap support (#400) --- .../java/io/github/classgraph/Classfile.java | 274 ++++---- .../classgraph/ClasspathElementDir.java | 142 ++--- .../classgraph/ClasspathElementModule.java | 28 +- .../classgraph/ClasspathElementZip.java | 80 +-- .../java/io/github/classgraph/Resource.java | 212 +------ .../classgraph/classpath/ClasspathOrder.java | 18 + .../fastzipfilereader/ByteBufferWrapper.java | 388 ------------ .../fastzipfilereader/FastZipEntry.java | 481 +------------- .../fastzipfilereader/LogicalZipFile.java | 251 +++----- .../MappedByteBufferResources.java | 470 -------------- .../fastzipfilereader/NestedJarHandler.java | 599 +++++++++++++----- .../fastzipfilereader/PhysicalZipFile.java | 126 ++-- .../fastzipfilereader/RecyclableInflater.java | 2 +- .../fastzipfilereader/ZipFileSlice.java | 89 +-- .../fastzipfilereader/ZipFileSliceReader.java | 411 ------------ .../classgraph/fileslice/ArraySlice.java | 88 +++ .../classgraph/fileslice/FileSlice.java | 126 ++++ .../io/github/classgraph/fileslice/Slice.java | 258 ++++++++ .../fileslice/reader/ClassfileReader.java | 430 +++++++++++++ .../reader/RandomAccessArrayReader.java | 162 +++++ .../reader/RandomAccessFileReader.java | 177 ++++++ .../fileslice/reader/RandomAccessReader.java | 180 ++++++ .../fileslice/reader/SequentialReader.java | 137 ++++ .../github/classgraph/recycler/Recycler.java | 8 +- .../io/github/classgraph/utils/FileUtils.java | 194 ------ .../utils/InputStreamOrByteBufferAdapter.java | 454 ------------- .../github/classgraph/utils/StringUtils.java | 135 ++++ 27 files changed, 2609 insertions(+), 3311 deletions(-) delete mode 100644 src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ByteBufferWrapper.java delete mode 100644 src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java delete mode 100644 src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSliceReader.java create mode 100644 src/main/java/nonapi/io/github/classgraph/fileslice/ArraySlice.java create mode 100644 src/main/java/nonapi/io/github/classgraph/fileslice/FileSlice.java create mode 100644 src/main/java/nonapi/io/github/classgraph/fileslice/Slice.java create mode 100644 src/main/java/nonapi/io/github/classgraph/fileslice/reader/ClassfileReader.java create mode 100644 src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessArrayReader.java create mode 100644 src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessFileReader.java create mode 100644 src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessReader.java create mode 100644 src/main/java/nonapi/io/github/classgraph/fileslice/reader/SequentialReader.java delete mode 100644 src/main/java/nonapi/io/github/classgraph/utils/InputStreamOrByteBufferAdapter.java create mode 100644 src/main/java/nonapi/io/github/classgraph/utils/StringUtils.java diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index 7afaa4d75..15533d121 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -41,10 +41,10 @@ import io.github.classgraph.Scanner.ClassfileScanWorkUnit; import nonapi.io.github.classgraph.concurrency.WorkQueue; +import nonapi.io.github.classgraph.fileslice.reader.ClassfileReader; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.types.ParseException; import nonapi.io.github.classgraph.utils.CollectionUtils; -import nonapi.io.github.classgraph.utils.InputStreamOrByteBufferAdapter; import nonapi.io.github.classgraph.utils.JarUtils; import nonapi.io.github.classgraph.utils.Join; import nonapi.io.github.classgraph.utils.LogNode; @@ -55,8 +55,8 @@ * sequence, to avoid re-allocating buffer memory. */ class Classfile { - /** The InputStream or ByteBuffer for the current classfile. */ - private InputStreamOrByteBufferAdapter inputStreamOrByteBuffer; + /** The {@link ClassfileReader} for the current classfile. */ + private ClassfileReader reader; /** The classpath element that contains this classfile. */ private final ClasspathElement classpathElement; @@ -654,9 +654,15 @@ private int getConstantPoolStringOffset(final int cpIdx, final int subFieldIdx) private String getConstantPoolString(final int cpIdx, final boolean replaceSlashWithDot, final boolean stripLSemicolon) throws ClassfileFormatException, IOException { final int constantPoolStringOffset = getConstantPoolStringOffset(cpIdx, /* subFieldIdx = */ 0); - return constantPoolStringOffset == 0 ? null - : intern(inputStreamOrByteBuffer.readString(constantPoolStringOffset, replaceSlashWithDot, - stripLSemicolon)); + if (constantPoolStringOffset == 0) { + return null; + } + final int utfLen = reader.readUnsignedShort(constantPoolStringOffset); + if (utfLen == 0) { + return ""; + } + return intern( + reader.readString(constantPoolStringOffset + 2, utfLen, replaceSlashWithDot, stripLSemicolon)); } /** @@ -676,9 +682,15 @@ private String getConstantPoolString(final int cpIdx, final boolean replaceSlash private String getConstantPoolString(final int cpIdx, final int subFieldIdx) throws ClassfileFormatException, IOException { final int constantPoolStringOffset = getConstantPoolStringOffset(cpIdx, subFieldIdx); - return constantPoolStringOffset == 0 ? null - : intern(inputStreamOrByteBuffer.readString(constantPoolStringOffset, - /* replaceSlashWithDot = */ false, /* stripLSemicolon = */ false)); + if (constantPoolStringOffset == 0) { + return null; + } + final int utfLen = reader.readUnsignedShort(constantPoolStringOffset); + if (utfLen == 0) { + return ""; + } + return intern(reader.readString(constantPoolStringOffset + 2, utfLen, /* replaceSlashWithDot = */ false, + /* stripLSemicolon = */ false)); } /** @@ -712,11 +724,11 @@ private byte getConstantPoolStringFirstByte(final int cpIdx) throws ClassfileFor if (constantPoolStringOffset == 0) { return '\0'; } - final int utfLen = inputStreamOrByteBuffer.readUnsignedShort(constantPoolStringOffset); + final int utfLen = reader.readUnsignedShort(constantPoolStringOffset); if (utfLen == 0) { return '\0'; } - return inputStreamOrByteBuffer.buf[constantPoolStringOffset + 2]; + return reader.readByte(constantPoolStringOffset + 2); } /** @@ -757,7 +769,7 @@ private String getConstantPoolClassDescriptor(final int cpIdx) throws ClassfileF * * @param cpIdx * the constant pool index - * @param asciiString + * @param asciiStr * the ASCII string to compare to * @return true, if successful * @throws ClassfileFormatException @@ -765,22 +777,24 @@ private String getConstantPoolClassDescriptor(final int cpIdx) throws ClassfileF * @throws IOException * If an IO exception occurs. */ - private boolean constantPoolStringEquals(final int cpIdx, final String asciiString) + private boolean constantPoolStringEquals(final int cpIdx, final String asciiStr) throws ClassfileFormatException, IOException { - final int strOffset = getConstantPoolStringOffset(cpIdx, /* subFieldIdx = */ 0); - if (strOffset == 0) { - return asciiString == null; - } else if (asciiString == null) { + final int cpStrOffset = getConstantPoolStringOffset(cpIdx, /* subFieldIdx = */ 0); + if (cpStrOffset == 0) { + return asciiStr == null; + } else if (asciiStr == null) { return false; } - final int strLen = inputStreamOrByteBuffer.readUnsignedShort(strOffset); - final int otherLen = asciiString.length(); - if (strLen != otherLen) { + final int cpStrLen = reader.readUnsignedShort(cpStrOffset); + final int asciiStrLen = asciiStr.length(); + if (cpStrLen != asciiStrLen) { return false; } - final int strStart = strOffset + 2; - for (int i = 0; i < strLen; i++) { - if ((char) (inputStreamOrByteBuffer.buf[strStart + i] & 0xff) != asciiString.charAt(i)) { + final int cpStrStart = cpStrOffset + 2; + reader.bufferTo(cpStrStart + cpStrLen); + final byte[] buf = reader.buf(); + for (int i = 0; i < cpStrLen; i++) { + if ((char) (buf[cpStrStart + i] & 0xff) != asciiStr.charAt(i)) { return false; } } @@ -804,7 +818,7 @@ private int cpReadUnsignedShort(final int cpIdx) throws IOException { + (cpCount - 1) + "] -- cannot continue reading class. " + "Please report this at https://github.com/classgraph/classgraph/issues"); } - return inputStreamOrByteBuffer.readUnsignedShort(entryOffset[cpIdx]); + return reader.readUnsignedShort(entryOffset[cpIdx]); } /** @@ -822,7 +836,7 @@ private int cpReadInt(final int cpIdx) throws IOException { + (cpCount - 1) + "] -- cannot continue reading class. " + "Please report this at https://github.com/classgraph/classgraph/issues"); } - return inputStreamOrByteBuffer.readInt(entryOffset[cpIdx]); + return reader.readInt(entryOffset[cpIdx]); } /** @@ -840,7 +854,7 @@ private long cpReadLong(final int cpIdx) throws IOException { + (cpCount - 1) + "] -- cannot continue reading class. " + "Please report this at https://github.com/classgraph/classgraph/issues"); } - return inputStreamOrByteBuffer.readLong(entryOffset[cpIdx]); + return reader.readLong(entryOffset[cpIdx]); } // ------------------------------------------------------------------------------------------------------------- @@ -913,14 +927,13 @@ private Object getFieldConstantPoolValue(final int tag, final char fieldTypeDesc */ private AnnotationInfo readAnnotation() throws IOException { // Lcom/xyz/Annotation; -> Lcom.xyz.Annotation; - final String annotationClassName = getConstantPoolClassDescriptor( - inputStreamOrByteBuffer.readUnsignedShort()); - final int numElementValuePairs = inputStreamOrByteBuffer.readUnsignedShort(); + final String annotationClassName = getConstantPoolClassDescriptor(reader.readUnsignedShort()); + final int numElementValuePairs = reader.readUnsignedShort(); AnnotationParameterValueList paramVals = null; if (numElementValuePairs > 0) { paramVals = new AnnotationParameterValueList(numElementValuePairs); for (int i = 0; i < numElementValuePairs; i++) { - final String paramName = getConstantPoolString(inputStreamOrByteBuffer.readUnsignedShort()); + final String paramName = getConstantPoolString(reader.readUnsignedShort()); final Object paramValue = readAnnotationElementValue(); paramVals.add(new AnnotationParameterValue(paramName, paramValue)); } @@ -936,44 +949,42 @@ private AnnotationInfo readAnnotation() throws IOException { * If an IO exception occurs. */ private Object readAnnotationElementValue() throws IOException { - final int tag = (char) inputStreamOrByteBuffer.readUnsignedByte(); + final int tag = (char) reader.readUnsignedByte(); switch (tag) { case 'B': - return (byte) cpReadInt(inputStreamOrByteBuffer.readUnsignedShort()); + return (byte) cpReadInt(reader.readUnsignedShort()); case 'C': - return (char) cpReadInt(inputStreamOrByteBuffer.readUnsignedShort()); + return (char) cpReadInt(reader.readUnsignedShort()); case 'D': - return Double.longBitsToDouble(cpReadLong(inputStreamOrByteBuffer.readUnsignedShort())); + return Double.longBitsToDouble(cpReadLong(reader.readUnsignedShort())); case 'F': - return Float.intBitsToFloat(cpReadInt(inputStreamOrByteBuffer.readUnsignedShort())); + return Float.intBitsToFloat(cpReadInt(reader.readUnsignedShort())); case 'I': - return cpReadInt(inputStreamOrByteBuffer.readUnsignedShort()); + return cpReadInt(reader.readUnsignedShort()); case 'J': - return cpReadLong(inputStreamOrByteBuffer.readUnsignedShort()); + return cpReadLong(reader.readUnsignedShort()); case 'S': - return (short) cpReadUnsignedShort(inputStreamOrByteBuffer.readUnsignedShort()); + return (short) cpReadUnsignedShort(reader.readUnsignedShort()); case 'Z': - return cpReadInt(inputStreamOrByteBuffer.readUnsignedShort()) != 0; + return cpReadInt(reader.readUnsignedShort()) != 0; case 's': - return getConstantPoolString(inputStreamOrByteBuffer.readUnsignedShort()); + return getConstantPoolString(reader.readUnsignedShort()); case 'e': { // Return type is AnnotationEnumVal. - final String annotationClassName = getConstantPoolClassDescriptor( - inputStreamOrByteBuffer.readUnsignedShort()); - final String annotationConstName = getConstantPoolString(inputStreamOrByteBuffer.readUnsignedShort()); + final String annotationClassName = getConstantPoolClassDescriptor(reader.readUnsignedShort()); + final String annotationConstName = getConstantPoolString(reader.readUnsignedShort()); return new AnnotationEnumValue(annotationClassName, annotationConstName); } case 'c': // Return type is AnnotationClassRef (for class references in annotations) - final String classRefTypeDescriptor = getConstantPoolString( - inputStreamOrByteBuffer.readUnsignedShort()); + final String classRefTypeDescriptor = getConstantPoolString(reader.readUnsignedShort()); return new AnnotationClassRef(classRefTypeDescriptor); case '@': // Complex (nested) annotation. Return type is AnnotationInfo. return readAnnotation(); case '[': // Return type is Object[] (of nested annotation element values) - final int count = inputStreamOrByteBuffer.readUnsignedShort(); + final int count = reader.readUnsignedShort(); final Object[] arr = new Object[count]; for (int i = 0; i < count; ++i) { // Nested annotation element value @@ -1005,7 +1016,7 @@ private void readConstantPoolEntries() throws IOException { } // Read size of constant pool - cpCount = inputStreamOrByteBuffer.readUnsignedShort(); + cpCount = reader.readUnsignedShort(); // Allocate storage for constant pool entryOffset = new int[cpCount]; @@ -1020,29 +1031,29 @@ private void readConstantPoolEntries() throws IOException { skipSlot = 0; continue; } - entryTag[i] = inputStreamOrByteBuffer.readUnsignedByte(); - entryOffset[i] = inputStreamOrByteBuffer.curr; + entryTag[i] = reader.readUnsignedByte(); + entryOffset[i] = reader.currPos(); switch (entryTag[i]) { case 0: // Impossible, probably buffer underflow throw new ClassfileFormatException("Unknown 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 = inputStreamOrByteBuffer.readUnsignedShort(); - inputStreamOrByteBuffer.skip(strLen); + final int strLen = reader.readUnsignedShort(); + reader.skip(strLen); break; case 3: // int, short, char, byte, boolean are all represented by Constant_INTEGER case 4: // float - inputStreamOrByteBuffer.skip(4); + reader.skip(4); break; case 5: // long case 6: // double - inputStreamOrByteBuffer.skip(8); + reader.skip(8); skipSlot = 1; // double slot break; case 7: // Class reference (format is e.g. "java/lang/String") // Forward or backward indirect reference to a modified UTF8 entry - indirectStringRefs[i] = inputStreamOrByteBuffer.readUnsignedShort(); + indirectStringRefs[i] = reader.readUnsignedShort(); if (classNameCpIdxs != null) { // If this is a class ref, and inter-class dependencies are enabled, record the dependency classNameCpIdxs.add(indirectStringRefs[i]); @@ -1050,44 +1061,44 @@ private void readConstantPoolEntries() throws IOException { break; case 8: // String // Forward or backward indirect reference to a modified UTF8 entry - indirectStringRefs[i] = inputStreamOrByteBuffer.readUnsignedShort(); + indirectStringRefs[i] = reader.readUnsignedShort(); break; case 9: // field ref // Refers to a class ref (case 7) and then a name and type (case 12) - inputStreamOrByteBuffer.skip(4); + reader.skip(4); break; case 10: // method ref // Refers to a class ref (case 7) and then a name and type (case 12) - inputStreamOrByteBuffer.skip(4); + reader.skip(4); break; case 11: // interface method ref // Refers to a class ref (case 7) and then a name and type (case 12) - inputStreamOrByteBuffer.skip(4); + reader.skip(4); break; case 12: // name and type - final int nameRef = inputStreamOrByteBuffer.readUnsignedShort(); - final int typeRef = inputStreamOrByteBuffer.readUnsignedShort(); + final int nameRef = reader.readUnsignedShort(); + final int typeRef = reader.readUnsignedShort(); if (typeSignatureIdxs != null) { typeSignatureIdxs.add(typeRef); } indirectStringRefs[i] = (nameRef << 16) | typeRef; break; case 15: // method handle - inputStreamOrByteBuffer.skip(3); + reader.skip(3); break; case 16: // method type - inputStreamOrByteBuffer.skip(2); + reader.skip(2); break; case 18: // invoke dynamic - inputStreamOrByteBuffer.skip(4); + reader.skip(4); break; case 19: // module (for module-info.class in JDK9+) // see https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.4 - indirectStringRefs[i] = inputStreamOrByteBuffer.readUnsignedShort(); + indirectStringRefs[i] = reader.readUnsignedShort(); break; case 20: // package (for module-info.class in JDK9+) // see https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.4 - inputStreamOrByteBuffer.skip(2); + reader.skip(2); break; default: throw new ClassfileFormatException("Unknown constant pool tag " + entryTag[i] @@ -1166,13 +1177,13 @@ private void readConstantPoolEntries() throws IOException { */ private void readBasicClassInfo() throws IOException, ClassfileFormatException, SkipClassException { // Modifier flags - classModifiers = inputStreamOrByteBuffer.readUnsignedShort(); + classModifiers = reader.readUnsignedShort(); isInterface = (classModifiers & 0x0200) != 0; isAnnotation = (classModifiers & 0x2000) != 0; // The fully-qualified class name of this class, with slashes replaced with dots - final String classNamePath = getConstantPoolString(inputStreamOrByteBuffer.readUnsignedShort()); + final String classNamePath = getConstantPoolString(reader.readUnsignedShort()); if (classNamePath == null) { throw new ClassfileFormatException("Class name is null"); } @@ -1203,7 +1214,7 @@ private void readBasicClassInfo() throws IOException, ClassfileFormatException, } // Superclass name, with slashes replaced with dots - final int superclassNameCpIdx = inputStreamOrByteBuffer.readUnsignedShort(); + final int superclassNameCpIdx = reader.readUnsignedShort(); if (superclassNameCpIdx > 0) { superclassName = getConstantPoolClassName(superclassNameCpIdx); } @@ -1219,9 +1230,9 @@ private void readBasicClassInfo() throws IOException, ClassfileFormatException, */ private void readInterfaces() throws IOException { // Interfaces - final int interfaceCount = inputStreamOrByteBuffer.readUnsignedShort(); + final int interfaceCount = reader.readUnsignedShort(); for (int i = 0; i < interfaceCount; i++) { - final String interfaceName = getConstantPoolClassName(inputStreamOrByteBuffer.readUnsignedShort()); + final String interfaceName = getConstantPoolClassName(reader.readUnsignedShort()); if (implementedInterfaces == null) { implementedInterfaces = new ArrayList<>(); } @@ -1241,28 +1252,28 @@ private void readInterfaces() throws IOException { */ private void readFields() throws IOException, ClassfileFormatException { // Fields - final int fieldCount = inputStreamOrByteBuffer.readUnsignedShort(); + final int fieldCount = reader.readUnsignedShort(); for (int i = 0; i < fieldCount; i++) { // Info on modifier flags: http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.5 - final int fieldModifierFlags = inputStreamOrByteBuffer.readUnsignedShort(); + final int fieldModifierFlags = reader.readUnsignedShort(); final boolean isPublicField = ((fieldModifierFlags & 0x0001) == 0x0001); final boolean fieldIsVisible = isPublicField || scanSpec.ignoreFieldVisibility; final boolean getStaticFinalFieldConstValue = scanSpec.enableStaticFinalFieldConstantInitializerValues && fieldIsVisible; if (!fieldIsVisible || (!scanSpec.enableFieldInfo && !getStaticFinalFieldConstValue)) { // Skip field - inputStreamOrByteBuffer.readUnsignedShort(); // fieldNameCpIdx - inputStreamOrByteBuffer.readUnsignedShort(); // fieldTypeDescriptorCpIdx - final int attributesCount = inputStreamOrByteBuffer.readUnsignedShort(); + reader.readUnsignedShort(); // fieldNameCpIdx + reader.readUnsignedShort(); // fieldTypeDescriptorCpIdx + final int attributesCount = reader.readUnsignedShort(); for (int j = 0; j < attributesCount; j++) { - inputStreamOrByteBuffer.readUnsignedShort(); // attributeNameCpIdx - final int attributeLength = inputStreamOrByteBuffer.readInt(); // == 2 - inputStreamOrByteBuffer.skip(attributeLength); + reader.readUnsignedShort(); // attributeNameCpIdx + final int attributeLength = reader.readInt(); // == 2 + reader.skip(attributeLength); } } else { - final int fieldNameCpIdx = inputStreamOrByteBuffer.readUnsignedShort(); + final int fieldNameCpIdx = reader.readUnsignedShort(); final String fieldName = getConstantPoolString(fieldNameCpIdx); - final int fieldTypeDescriptorCpIdx = inputStreamOrByteBuffer.readUnsignedShort(); + final int fieldTypeDescriptorCpIdx = reader.readUnsignedShort(); final char fieldTypeDescriptorFirstChar = (char) getConstantPoolStringFirstByte( fieldTypeDescriptorCpIdx); String fieldTypeDescriptor; @@ -1271,16 +1282,16 @@ private void readFields() throws IOException, ClassfileFormatException { Object fieldConstValue = null; AnnotationInfoList fieldAnnotationInfo = null; - final int attributesCount = inputStreamOrByteBuffer.readUnsignedShort(); + final int attributesCount = reader.readUnsignedShort(); for (int j = 0; j < attributesCount; j++) { - final int attributeNameCpIdx = inputStreamOrByteBuffer.readUnsignedShort(); - final int attributeLength = inputStreamOrByteBuffer.readInt(); // == 2 + final int attributeNameCpIdx = reader.readUnsignedShort(); + final int attributeLength = reader.readInt(); // == 2 // See if field name matches one of the requested names for this class, and if it does, // check if it is initialized with a constant value if ((getStaticFinalFieldConstValue) && constantPoolStringEquals(attributeNameCpIdx, "ConstantValue")) { // http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.7.2 - final int cpIdx = inputStreamOrByteBuffer.readUnsignedShort(); + final int cpIdx = reader.readUnsignedShort(); if (cpIdx < 1 || cpIdx >= cpCount) { throw new ClassfileFormatException("Constant pool index " + cpIdx + ", should be in range [1, " + (cpCount - 1) @@ -1290,13 +1301,13 @@ && constantPoolStringEquals(attributeNameCpIdx, "ConstantValue")) { fieldConstValue = getFieldConstantPoolValue(entryTag[cpIdx], fieldTypeDescriptorFirstChar, cpIdx); } else if (fieldIsVisible && constantPoolStringEquals(attributeNameCpIdx, "Signature")) { - fieldTypeSignature = getConstantPoolString(inputStreamOrByteBuffer.readUnsignedShort()); + fieldTypeSignature = getConstantPoolString(reader.readUnsignedShort()); } else if (scanSpec.enableAnnotationInfo // && (constantPoolStringEquals(attributeNameCpIdx, "RuntimeVisibleAnnotations") || (!scanSpec.disableRuntimeInvisibleAnnotations && constantPoolStringEquals( attributeNameCpIdx, "RuntimeInvisibleAnnotations")))) { // Read annotation names - final int fieldAnnotationCount = inputStreamOrByteBuffer.readUnsignedShort(); + final int fieldAnnotationCount = reader.readUnsignedShort(); if (fieldAnnotationCount > 0) { if (fieldAnnotationInfo == null) { fieldAnnotationInfo = new AnnotationInfoList(1); @@ -1308,7 +1319,7 @@ && constantPoolStringEquals(attributeNameCpIdx, "ConstantValue")) { } } else { // No match, just skip attribute - inputStreamOrByteBuffer.skip(attributeLength); + reader.skip(attributeLength); } } if (scanSpec.enableFieldInfo && fieldIsVisible) { @@ -1334,10 +1345,10 @@ && constantPoolStringEquals(attributeNameCpIdx, "ConstantValue")) { */ private void readMethods() throws IOException, ClassfileFormatException { // Methods - final int methodCount = inputStreamOrByteBuffer.readUnsignedShort(); + final int methodCount = reader.readUnsignedShort(); for (int i = 0; i < methodCount; i++) { // Info on modifier flags: http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.6 - final int methodModifierFlags = inputStreamOrByteBuffer.readUnsignedShort(); + final int methodModifierFlags = reader.readUnsignedShort(); final boolean isPublicMethod = ((methodModifierFlags & 0x0001) == 0x0001); final boolean methodIsVisible = isPublicMethod || scanSpec.ignoreMethodVisibility; @@ -1347,14 +1358,14 @@ private void readMethods() throws IOException, ClassfileFormatException { // Always enable MethodInfo for annotations (this is how annotation constants are defined) final boolean enableMethodInfo = scanSpec.enableMethodInfo || isAnnotation; if (enableMethodInfo || isAnnotation) { // Annotations store defaults in method_info - final int methodNameCpIdx = inputStreamOrByteBuffer.readUnsignedShort(); + final int methodNameCpIdx = reader.readUnsignedShort(); methodName = getConstantPoolString(methodNameCpIdx); - final int methodTypeDescriptorCpIdx = inputStreamOrByteBuffer.readUnsignedShort(); + final int methodTypeDescriptorCpIdx = reader.readUnsignedShort(); methodTypeDescriptor = getConstantPoolString(methodTypeDescriptorCpIdx); } else { - inputStreamOrByteBuffer.skip(4); // name_index, descriptor_index + reader.skip(4); // name_index, descriptor_index } - final int attributesCount = inputStreamOrByteBuffer.readUnsignedShort(); + final int attributesCount = reader.readUnsignedShort(); String[] methodParameterNames = null; int[] methodParameterModifiers = null; AnnotationInfo[][] methodParameterAnnotations = null; @@ -1363,20 +1374,20 @@ private void readMethods() throws IOException, ClassfileFormatException { if (!methodIsVisible || (!enableMethodInfo && !isAnnotation)) { // Skip method attributes for (int j = 0; j < attributesCount; j++) { - inputStreamOrByteBuffer.skip(2); // attribute_name_index - final int attributeLength = inputStreamOrByteBuffer.readInt(); - inputStreamOrByteBuffer.skip(attributeLength); + reader.skip(2); // attribute_name_index + final int attributeLength = reader.readInt(); + reader.skip(attributeLength); } } else { // Look for method annotations for (int j = 0; j < attributesCount; j++) { - final int attributeNameCpIdx = inputStreamOrByteBuffer.readUnsignedShort(); - final int attributeLength = inputStreamOrByteBuffer.readInt(); + final int attributeNameCpIdx = reader.readUnsignedShort(); + final int attributeLength = reader.readInt(); if (scanSpec.enableAnnotationInfo && (constantPoolStringEquals(attributeNameCpIdx, "RuntimeVisibleAnnotations") || (!scanSpec.disableRuntimeInvisibleAnnotations && constantPoolStringEquals( attributeNameCpIdx, "RuntimeInvisibleAnnotations")))) { - final int methodAnnotationCount = inputStreamOrByteBuffer.readUnsignedShort(); + final int methodAnnotationCount = reader.readUnsignedShort(); if (methodAnnotationCount > 0) { if (methodAnnotationInfo == null) { methodAnnotationInfo = new AnnotationInfoList(1); @@ -1395,7 +1406,7 @@ private void readMethods() throws IOException, ClassfileFormatException { // annotations are given in separate attributes, so if both attributes are present, // have to make the parameter annotation arrays larger when the second attribute is // encountered). - final int numParams = inputStreamOrByteBuffer.readUnsignedByte(); + final int numParams = reader.readUnsignedByte(); if (methodParameterAnnotations == null) { methodParameterAnnotations = new AnnotationInfo[numParams][]; } else if (methodParameterAnnotations.length != numParams) { @@ -1404,7 +1415,7 @@ private void readMethods() throws IOException, ClassfileFormatException { + "and RuntimeInvisibleParameterAnnotations"); } for (int paramIdx = 0; paramIdx < numParams; paramIdx++) { - final int numAnnotations = inputStreamOrByteBuffer.readUnsignedShort(); + final int numAnnotations = reader.readUnsignedShort(); if (numAnnotations > 0) { int annStartIdx = 0; if (methodParameterAnnotations[paramIdx] != null) { @@ -1424,18 +1435,18 @@ private void readMethods() throws IOException, ClassfileFormatException { } else if (constantPoolStringEquals(attributeNameCpIdx, "MethodParameters")) { // Read method parameters. For Java, these are only produced in JDK8+, and only if the // commandline switch `-parameters` is provided at compiletime. - final int paramCount = inputStreamOrByteBuffer.readUnsignedByte(); + final int paramCount = reader.readUnsignedByte(); methodParameterNames = new String[paramCount]; methodParameterModifiers = new int[paramCount]; for (int k = 0; k < paramCount; k++) { - final int cpIdx = inputStreamOrByteBuffer.readUnsignedShort(); + final int cpIdx = reader.readUnsignedShort(); // If the constant pool index is zero, then the parameter is unnamed => use null methodParameterNames[k] = cpIdx == 0 ? null : getConstantPoolString(cpIdx); - methodParameterModifiers[k] = inputStreamOrByteBuffer.readUnsignedShort(); + methodParameterModifiers[k] = reader.readUnsignedShort(); } } else if (constantPoolStringEquals(attributeNameCpIdx, "Signature")) { // Add type params to method type signature - methodTypeSignature = getConstantPoolString(inputStreamOrByteBuffer.readUnsignedShort()); + methodTypeSignature = getConstantPoolString(reader.readUnsignedShort()); } else if (constantPoolStringEquals(attributeNameCpIdx, "AnnotationDefault")) { if (annotationParamDefaultValues == null) { annotationParamDefaultValues = new AnnotationParameterValueList(); @@ -1445,9 +1456,9 @@ private void readMethods() throws IOException, ClassfileFormatException { readAnnotationElementValue())); } else if (constantPoolStringEquals(attributeNameCpIdx, "Code")) { methodHasBody = true; - inputStreamOrByteBuffer.skip(attributeLength); + reader.skip(attributeLength); } else { - inputStreamOrByteBuffer.skip(attributeLength); + reader.skip(attributeLength); } } // Create MethodInfo @@ -1475,15 +1486,15 @@ private void readMethods() throws IOException, ClassfileFormatException { */ private void readClassAttributes() throws IOException, ClassfileFormatException { // Class attributes (including class annotations, class type variables, module info, etc.) - final int attributesCount = inputStreamOrByteBuffer.readUnsignedShort(); + final int attributesCount = reader.readUnsignedShort(); for (int i = 0; i < attributesCount; i++) { - final int attributeNameCpIdx = inputStreamOrByteBuffer.readUnsignedShort(); - final int attributeLength = inputStreamOrByteBuffer.readInt(); + final int attributeNameCpIdx = reader.readUnsignedShort(); + final int attributeLength = reader.readInt(); if (scanSpec.enableAnnotationInfo // && (constantPoolStringEquals(attributeNameCpIdx, "RuntimeVisibleAnnotations") || (!scanSpec.disableRuntimeInvisibleAnnotations && constantPoolStringEquals( attributeNameCpIdx, "RuntimeInvisibleAnnotations")))) { - final int annotationCount = inputStreamOrByteBuffer.readUnsignedShort(); + final int annotationCount = reader.readUnsignedShort(); if (annotationCount > 0) { if (classAnnotations == null) { classAnnotations = new AnnotationInfoList(); @@ -1493,12 +1504,12 @@ private void readClassAttributes() throws IOException, ClassfileFormatException } } } else if (constantPoolStringEquals(attributeNameCpIdx, "InnerClasses")) { - final int numInnerClasses = inputStreamOrByteBuffer.readUnsignedShort(); + final int numInnerClasses = reader.readUnsignedShort(); for (int j = 0; j < numInnerClasses; j++) { - final int innerClassInfoCpIdx = inputStreamOrByteBuffer.readUnsignedShort(); - final int outerClassInfoCpIdx = inputStreamOrByteBuffer.readUnsignedShort(); - inputStreamOrByteBuffer.skip(2); // inner_name_idx - final int innerClassAccessFlags = inputStreamOrByteBuffer.readUnsignedShort(); + final int innerClassInfoCpIdx = reader.readUnsignedShort(); + final int outerClassInfoCpIdx = reader.readUnsignedShort(); + reader.skip(2); // inner_name_idx + final int innerClassAccessFlags = reader.readUnsignedShort(); if (innerClassInfoCpIdx != 0 && outerClassInfoCpIdx != 0) { final String innerClassName = getConstantPoolClassName(innerClassInfoCpIdx); final String outerClassName = getConstantPoolClassName(outerClassInfoCpIdx); @@ -1511,11 +1522,10 @@ private void readClassAttributes() throws IOException, ClassfileFormatException } } else if (constantPoolStringEquals(attributeNameCpIdx, "Signature")) { // Get class type signature, including type variables - typeSignature = getConstantPoolString(inputStreamOrByteBuffer.readUnsignedShort()); + typeSignature = getConstantPoolString(reader.readUnsignedShort()); } else if (constantPoolStringEquals(attributeNameCpIdx, "EnclosingMethod")) { - final String innermostEnclosingClassName = getConstantPoolClassName( - inputStreamOrByteBuffer.readUnsignedShort()); - final int enclosingMethodCpIdx = inputStreamOrByteBuffer.readUnsignedShort(); + final String innermostEnclosingClassName = getConstantPoolClassName(reader.readUnsignedShort()); + final int enclosingMethodCpIdx = reader.readUnsignedShort(); String definingMethodName; if (enclosingMethodCpIdx == 0) { // A cpIdx of 0 (which is an invalid value) is used for anonymous inner classes declared in @@ -1535,13 +1545,13 @@ private void readClassAttributes() throws IOException, ClassfileFormatException // class this.fullyQualifiedDefiningMethodName = innermostEnclosingClassName + "." + definingMethodName; } else if (constantPoolStringEquals(attributeNameCpIdx, "Module")) { - final int moduleNameCpIdx = inputStreamOrByteBuffer.readUnsignedShort(); + final int moduleNameCpIdx = reader.readUnsignedShort(); classpathElement.moduleNameFromModuleDescriptor = getConstantPoolString(moduleNameCpIdx); // (Future work): parse the rest of the module descriptor fields, and add to ModuleInfo: // https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.25 - inputStreamOrByteBuffer.skip(attributeLength - 2); + reader.skip(attributeLength - 2); } else { - inputStreamOrByteBuffer.skip(attributeLength); + reader.skip(attributeLength); } } } @@ -1602,19 +1612,17 @@ private void readClassAttributes() throws IOException, ClassfileFormatException this.scanSpec = scanSpec; try { - // Open classfile as a ByteBuffer or InputStream - inputStreamOrByteBuffer = classfileResource.openOrRead(); + // Open a BufferedSequentialReader for the classfile + reader = classfileResource.openClassfile(); // Check magic number - if (inputStreamOrByteBuffer.readInt() != 0xCAFEBABE) { + if (reader.readInt() != 0xCAFEBABE) { throw new ClassfileFormatException("Classfile does not have correct magic number"); } - // Read classfile minor version - inputStreamOrByteBuffer.readUnsignedShort(); - - // Read classfile major version - inputStreamOrByteBuffer.readUnsignedShort(); + // Read classfile minor and major version + reader.readUnsignedShort(); + reader.readUnsignedShort(); // Read the constant pool readConstantPoolEntries(); @@ -1635,9 +1643,9 @@ private void readClassAttributes() throws IOException, ClassfileFormatException readClassAttributes(); } finally { - // Close ByteBuffer or InputStream + // Close BufferedSequentialReader classfileResource.close(); - inputStreamOrByteBuffer = null; + reader = null; } // Write class info to log diff --git a/src/main/java/io/github/classgraph/ClasspathElementDir.java b/src/main/java/io/github/classgraph/ClasspathElementDir.java index 521d4c454..ce46ba6e6 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementDir.java @@ -31,6 +31,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.io.RandomAccessFile; import java.net.URI; import java.nio.ByteBuffer; import java.nio.file.Files; @@ -44,14 +45,13 @@ import nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandlerRegistry; import nonapi.io.github.classgraph.classpath.ClasspathOrder.ClasspathElementAndClassLoader; import nonapi.io.github.classgraph.concurrency.WorkQueue; -import nonapi.io.github.classgraph.fastzipfilereader.ByteBufferWrapper; import nonapi.io.github.classgraph.fastzipfilereader.LogicalZipFile; -import nonapi.io.github.classgraph.fastzipfilereader.MappedByteBufferResources; 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.FileUtils; -import nonapi.io.github.classgraph.utils.InputStreamOrByteBufferAdapter; import nonapi.io.github.classgraph.utils.LogNode; /** A directory classpath element. */ @@ -155,23 +155,20 @@ void open(final WorkQueue workQueue, final int classpath * Create a new {@link Resource} object for a resource or classfile discovered while scanning paths. * * @param relativePath - * the relative path - * @param classpathResourceFile - * the classpath resource file + * the relative path of the resource + * @param resourceFile + * the {@link File} for the resource * @param nestedJarHandler * the nested jar handler * @param log * the log * @return the resource */ - private Resource newResource(final String relativePath, final File classpathResourceFile, + private Resource newResource(final String relativePath, final File resourceFile, final NestedJarHandler nestedJarHandler, final LogNode log) { - return new Resource(this, classpathResourceFile.length()) { - /** The {@link ByteBufferWrapper}, or null. */ - protected ByteBufferWrapper byteBufferWrapper; - - /** The mapped file. */ - private MappedByteBufferResources mappedFileResources; + return new Resource(this, resourceFile.length()) { + /** The {@link RandomAccessFile} opened on the file. */ + private RandomAccessFile raf; @Override public String getPath() { @@ -185,7 +182,7 @@ public String getPathRelativeToClasspathElement() { @Override public long getLastModified() { - return classpathResourceFile.lastModified(); + return resourceFile.lastModified(); } @SuppressWarnings("null") @@ -193,8 +190,7 @@ public long getLastModified() { public Set getPosixFilePermissions() { Set posixFilePermissions = null; try { - posixFilePermissions = Files - .readAttributes(classpathResourceFile.toPath(), PosixFileAttributes.class) + posixFilePermissions = Files.readAttributes(resourceFile.toPath(), PosixFileAttributes.class) .permissions(); } catch (UnsupportedOperationException | IOException | SecurityException e) { // POSIX attributes not supported @@ -202,114 +198,62 @@ public Set getPosixFilePermissions() { return posixFilePermissions; } - synchronized ByteBufferWrapper readWrapped() throws IOException { + @Override + public synchronized ByteBuffer read() throws IOException { if (skipClasspathElement) { // Shouldn't happen throw new IOException("Parent directory could not be opened"); } - markAsOpen(); - try { - mappedFileResources = new MappedByteBufferResources(classpathResourceFile, nestedJarHandler, - log); - if (mappedFileResources.numChunks() > 1) { - // We could provide another method that fetches a chunk other than chunk 0, but the need - // to read files larger than 2GB is probably limited (it's not even supported for zipfiles), - // and the caller can use an InputStream if necessary. - throw new IOException( - "File is larger than 2GB -- cannot use read() method, use open() instead"); - } - // Fetch chunk 0 (the first ~2GB of the file) - byteBufferWrapper = mappedFileResources.getByteBuffer(0); - return byteBufferWrapper; - } catch (final IOException | SecurityException | OutOfMemoryError e) { - close(); - throw new IOException("Could not open " + this, e); - } catch (final InterruptedException e) { - // Re-set interrupt status - Thread.currentThread().interrupt(); - close(); - throw new IOException("Could not open " + this, e); - } - } - - @Override - public synchronized ByteBuffer read() throws IOException { - try { - readWrapped(); - final ByteBuffer buf = byteBufferWrapper.getByteBuffer(); - if (buf == null) { - throw new IOException("Could not read resource as a ByteBuffer, because memory mapping " - + "of files was disabled, or an OutOfMemoryError occurred while attempting to " - + "map files"); - } - byteBuffer = buf.duplicate(); - byteBuffer.position(0); - length = byteBuffer.remaining(); - return byteBuffer; - } catch (final IOException | SecurityException | OutOfMemoryError e) { - close(); - throw new IOException("Could not open " + this, e); - } + final FileSlice fileSlice = new FileSlice(resourceFile, nestedJarHandler); + raf = fileSlice.raf; + byteBuffer = fileSlice.read(); + return byteBuffer; } @Override - synchronized InputStreamOrByteBufferAdapter openOrRead() throws IOException { - if (length >= FileUtils.FILECHANNEL_FILE_SIZE_THRESHOLD) { - return new InputStreamOrByteBufferAdapter(readWrapped()); - } else { - return new InputStreamOrByteBufferAdapter(inputStream = new InputStreamResourceCloser(this, - Files.newInputStream(classpathResourceFile.toPath()))); + synchronized ClassfileReader openClassfile() throws IOException { + if (skipClasspathElement) { + // Shouldn't happen + throw new IOException("Parent directory could not be opened"); } + final FileSlice fileSlice = new FileSlice(resourceFile, nestedJarHandler); + raf = fileSlice.raf; + return new ClassfileReader(fileSlice); } @Override public synchronized InputStream open() throws IOException { - if (length >= FileUtils.FILECHANNEL_FILE_SIZE_THRESHOLD && length <= FileUtils.MAX_BUFFER_SIZE) { - read(); - return inputStream = new InputStreamResourceCloser(this, byteBufferToInputStream()); - } else { - markAsOpen(); - try { - return inputStream = new InputStreamResourceCloser(this, - Files.newInputStream(classpathResourceFile.toPath())); - } catch (final IOException | SecurityException e) { - close(); - throw new IOException("Could not open " + this, e); - } + if (skipClasspathElement) { + // Shouldn't happen + throw new IOException("Parent directory could not be opened"); } + final FileSlice fileSlice = new FileSlice(resourceFile, nestedJarHandler); + raf = fileSlice.raf; + inputStream = fileSlice.open(); + return inputStream; } @Override public synchronized byte[] load() throws IOException { - try { - final byte[] byteArray; - if (length > FileUtils.MAX_BUFFER_SIZE) { - throw new IOException("File is larger than 2GB, cannot read into array"); - } else if (length >= FileUtils.FILECHANNEL_FILE_SIZE_THRESHOLD) { - read(); - byteArray = byteBufferToByteArray(); - } else { - open(); - byteArray = FileUtils.readAllBytesAsArray(inputStream, length); - } - length = byteArray.length; - return byteArray; - } finally { - close(); + if (skipClasspathElement) { + // Shouldn't happen + throw new IOException("Parent directory could not be opened"); } + final FileSlice fileSlice = new FileSlice(resourceFile, nestedJarHandler); + raf = fileSlice.raf; + return fileSlice.load(); } @Override public synchronized void close() { super.close(); // Close inputStream - if (byteBufferWrapper != null) { - byteBufferWrapper.close(/* log = */ null); - byteBufferWrapper = null; + if (byteBuffer != null) { + // All ByteBuffers should wrap arrays, so they don't need to be cleaned byteBuffer = null; } - if (mappedFileResources != null) { - mappedFileResources.close(/* log = */ null); - mappedFileResources = null; + if (raf != null) { + nestedJarHandler.closeOpenFile(raf); + raf = null; } markAsClosed(); } diff --git a/src/main/java/io/github/classgraph/ClasspathElementModule.java b/src/main/java/io/github/classgraph/ClasspathElementModule.java index 428549ae1..108a10378 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementModule.java +++ b/src/main/java/io/github/classgraph/ClasspathElementModule.java @@ -43,12 +43,12 @@ import nonapi.io.github.classgraph.concurrency.SingletonMap.NullSingletonException; import nonapi.io.github.classgraph.concurrency.WorkQueue; import nonapi.io.github.classgraph.fastzipfilereader.LogicalZipFile; +import nonapi.io.github.classgraph.fileslice.reader.ClassfileReader; import nonapi.io.github.classgraph.recycler.RecycleOnClose; import nonapi.io.github.classgraph.recycler.Recycler; 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.InputStreamOrByteBufferAdapter; import nonapi.io.github.classgraph.utils.LogNode; /** A module classpath element. */ @@ -165,8 +165,12 @@ public synchronized ByteBuffer read() throws IOException { } @Override - synchronized InputStreamOrByteBufferAdapter openOrRead() throws IOException { - return new InputStreamOrByteBufferAdapter(open()); + synchronized ClassfileReader openClassfile() throws IOException { + if (skipClasspathElement) { + // Shouldn't happen + throw new IOException("Module could not be opened"); + } + return new ClassfileReader(open()); } @Override @@ -178,7 +182,7 @@ public synchronized InputStream open() throws IOException { markAsOpen(); try { moduleReaderProxy = moduleReaderProxyRecycler.acquire(); - inputStream = new InputStreamResourceCloser(this, moduleReaderProxy.open(resourcePath)); + inputStream = moduleReaderProxy.open(resourcePath); // Length cannot be obtained from ModuleReader length = -1L; return inputStream; @@ -193,7 +197,14 @@ public synchronized InputStream open() throws IOException { public synchronized byte[] load() throws IOException { try { read(); - final byte[] byteArray = byteBufferToByteArray(); + final byte[] byteArray; + if (byteBuffer.hasArray() && byteBuffer.position() == 0 + && byteBuffer.limit() == byteBuffer.capacity()) { + byteArray = byteBuffer.array(); + } else { + byteArray = new byte[byteBuffer.remaining()]; + byteBuffer.get(byteArray); + } length = byteArray.length; return byteArray; } finally { @@ -204,14 +215,11 @@ public synchronized byte[] load() throws IOException { @Override public synchronized void close() { super.close(); // Close inputStream - if (byteBuffer != null) { - if (moduleReaderProxy != null) { + if (moduleReaderProxy != null) { + if (byteBuffer != null) { // Release any open ByteBuffer moduleReaderProxy.release(byteBuffer); } - byteBuffer = null; - } - if (moduleReaderProxy != null) { // Recycle the (open) ModuleReaderProxy instance. moduleReaderProxyRecycler.recycle(moduleReaderProxy); // Don't call ModuleReaderProxy#close(), leave the ModuleReaderProxy open in the recycler. diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index c77a3070a..ed06cf69e 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -46,16 +46,15 @@ import nonapi.io.github.classgraph.classpath.ClasspathOrder.ClasspathElementAndClassLoader; import nonapi.io.github.classgraph.concurrency.SingletonMap.NullSingletonException; import nonapi.io.github.classgraph.concurrency.WorkQueue; -import nonapi.io.github.classgraph.fastzipfilereader.ByteBufferWrapper; import nonapi.io.github.classgraph.fastzipfilereader.FastZipEntry; import nonapi.io.github.classgraph.fastzipfilereader.LogicalZipFile; import nonapi.io.github.classgraph.fastzipfilereader.NestedJarHandler; import nonapi.io.github.classgraph.fastzipfilereader.ZipFileSlice; +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.InputStreamOrByteBufferAdapter; import nonapi.io.github.classgraph.utils.JarUtils; import nonapi.io.github.classgraph.utils.LogNode; import nonapi.io.github.classgraph.utils.URLPathEncoder; @@ -291,9 +290,6 @@ void open(final WorkQueue workQueue, final int classpath */ private Resource newResource(final FastZipEntry zipEntry, final String pathRelativeToPackageRoot) { return new Resource(this, zipEntry.uncompressedSize) { - /** The {@link ByteBufferWrapper}, or null. */ - protected ByteBufferWrapper byteBufferWrapper; - /** * Path with package root prefix and/or any Spring Boot prefix ("BOOT-INF/classes/" or * "WEB-INF/classes/") removed. @@ -360,92 +356,62 @@ public synchronized InputStream open() throws IOException { } markAsOpen(); try { - inputStream = new InputStreamResourceCloser(this, zipEntry.open()); + inputStream = zipEntry.getSlice().open(); length = zipEntry.uncompressedSize; return inputStream; } catch (final IOException e) { close(); throw e; - } catch (final InterruptedException e) { - close(); - nestedJarHandler.interruptionChecker.interrupt(); - throw new IOException(e); } } @Override - synchronized InputStreamOrByteBufferAdapter openOrRead() throws IOException { - return new InputStreamOrByteBufferAdapter(open()); + synchronized ClassfileReader openClassfile() throws IOException { + if (skipClasspathElement) { + // Shouldn't happen + throw new IOException("Jarfile could not be opened"); + } + return new ClassfileReader(open()); } @Override public synchronized ByteBuffer read() throws IOException { + if (skipClasspathElement) { + // Shouldn't happen + throw new IOException("Jarfile could not be opened"); + } try { - if (zipEntry.canGetAsSlice()) { - try { - // For STORED entries that do not span multiple 2GB chunks, can create a - // ByteBuffer slice directly from the entry - markAsOpen(); - // compressedSize should have the same value as uncompressedSize for STORED - // entries, but compressedSize is more reliable (uncompressedSize may be -1) - length = zipEntry.compressedSize; - byteBufferWrapper = zipEntry.getAsSlice(); - byteBuffer = byteBufferWrapper.getByteBuffer(); - if (byteBuffer == null) { - throw new IOException( - "Could not read resource as a ByteBuffer, because memory mapping " - + "of files was disabled, or an OutOfMemoryError occurred while attempting to " - + "map files"); - } - return byteBuffer; - - } catch (final IOException e) { - close(); - throw e; - } catch (final InterruptedException e) { - close(); - nestedJarHandler.interruptionChecker.interrupt(); - throw new IOException(e); - } - - } else { - // Otherwise, decompress or extract the entry into a byte[] array, - // then wrap in a ByteBuffer - open(); - return inputStreamToByteBuffer(); - } + byteBuffer = zipEntry.getSlice().read(); + length = byteBuffer.remaining(); + return byteBuffer; } catch (final IOException e) { close(); throw e; - } catch (final InterruptedException e) { - close(); - nestedJarHandler.interruptionChecker.interrupt(); - throw new IOException(e); } } @Override public synchronized byte[] load() throws IOException { + if (skipClasspathElement) { + // Shouldn't happen + throw new IOException("Jarfile could not be opened"); + } try { - open(); - final byte[] byteArray = inputStreamToByteArray(); + final byte[] byteArray = zipEntry.getSlice().load(); length = byteArray.length; return byteArray; - } finally { + } catch (final IOException e) { close(); + throw e; } } @Override public synchronized void close() { super.close(); // Close inputStream - if (byteBufferWrapper != null) { - byteBufferWrapper.close(/* log = */ null); - byteBufferWrapper = null; - byteBuffer = null; - } if (byteBuffer != null) { + // All ByteBuffers should wrap arrays, so they don't need to be cleaned byteBuffer = null; } markAsClosed(); diff --git a/src/main/java/io/github/classgraph/Resource.java b/src/main/java/io/github/classgraph/Resource.java index c44762065..e525d1327 100644 --- a/src/main/java/io/github/classgraph/Resource.java +++ b/src/main/java/io/github/classgraph/Resource.java @@ -42,8 +42,7 @@ import java.util.Set; import java.util.zip.ZipEntry; -import nonapi.io.github.classgraph.utils.FileUtils; -import nonapi.io.github.classgraph.utils.InputStreamOrByteBufferAdapter; +import nonapi.io.github.classgraph.fileslice.reader.ClassfileReader; import nonapi.io.github.classgraph.utils.LogNode; import nonapi.io.github.classgraph.utils.URLPathEncoder; @@ -93,202 +92,6 @@ public Resource(final ClasspathElement classpathElement, final long length) { // ------------------------------------------------------------------------------------------------------------- - /** - * Create an {@link InputStream} from a {@link ByteBuffer}. - * - * @return the input stream - */ - protected InputStream byteBufferToInputStream() { - return inputStream == null ? inputStream = FileUtils.byteBufferToInputStream(byteBuffer) : inputStream; - } - - /** - * Create a {@link ByteBuffer} from an {@link InputStream}. - * - * @return the byte buffer - * @throws IOException - * if an I/O exception occurs. - */ - protected ByteBuffer inputStreamToByteBuffer() throws IOException { - return byteBuffer == null ? byteBuffer = ByteBuffer.wrap(inputStreamToByteArray()) : byteBuffer; - } - - /** - * Read all bytes from an {@link InputStream} and return as a byte array. - * - * @return the contents of the {@link InputStream}. - * @throws IOException - * if an I/O exception occurs. - */ - protected byte[] inputStreamToByteArray() throws IOException { - return FileUtils.readAllBytesAsArray(inputStream, length); - } - - /** - * Read/copy contents of a {@link ByteBuffer} as a byte array. - * - * @return the contents of the {@link ByteBuffer} as a byte array. - */ - protected byte[] byteBufferToByteArray() { - if (byteBuffer.hasArray()) { - return byteBuffer.array(); - } else { - final byte[] byteArray = new byte[byteBuffer.remaining()]; - byteBuffer.get(byteArray); - return byteArray; - } - } - - // ------------------------------------------------------------------------------------------------------------- - - /** - * Class for closing the parent {@link Resource} when an {@link InputStream} opened on the resource is closed. - */ - protected class InputStreamResourceCloser extends InputStream { - - /** The input stream. */ - private InputStream inputStream; - - /** The parent resource. */ - private Resource parentResource; - - /** - * Constructor. - * - * @param parentResource - * the parent resource - * @param inputStream - * the input stream - * @throws IOException - * if an I/O exception occurs. - */ - protected InputStreamResourceCloser(final Resource parentResource, final InputStream inputStream) - throws IOException { - super(); - if (inputStream == null) { - throw new IOException("InputStream cannot be null"); - } - this.inputStream = inputStream; - this.parentResource = parentResource; - } - - /* (non-Javadoc) - * @see java.io.InputStream#read() - */ - @Override - public int read() throws IOException { - if (inputStream == null) { - throw new IOException("InputStream is not open"); - } - return inputStream.read(); - } - - /* (non-Javadoc) - * @see java.io.InputStream#read(byte[], int, int) - */ - @Override - public int read(final byte[] b, final int off, final int len) throws IOException { - if (inputStream == null) { - throw new IOException("InputStream is not open"); - } - return inputStream.read(b, off, len); - } - - /* (non-Javadoc) - * @see java.io.InputStream#read(byte[]) - */ - @Override - public int read(final byte[] b) throws IOException { - if (inputStream == null) { - throw new IOException("InputStream is not open"); - } - return inputStream.read(b); - } - - /* (non-Javadoc) - * @see java.io.InputStream#available() - */ - @Override - public int available() throws IOException { - if (inputStream == null) { - throw new IOException("InputStream is not open"); - } - return inputStream.available(); - } - - /* (non-Javadoc) - * @see java.io.InputStream#skip(long) - */ - @Override - public long skip(final long n) throws IOException { - if (inputStream == null) { - throw new IOException("InputStream is not open"); - } - return inputStream.skip(n); - } - - /* (non-Javadoc) - * @see java.io.InputStream#markSupported() - */ - @Override - public boolean markSupported() { - return inputStream.markSupported(); - } - - /* (non-Javadoc) - * @see java.io.InputStream#mark(int) - */ - @Override - public synchronized void mark(final int readlimit) { - inputStream.mark(readlimit); - } - - /* (non-Javadoc) - * @see java.io.InputStream#reset() - */ - @Override - public synchronized void reset() throws IOException { - if (inputStream == null) { - throw new IOException("InputStream is not open"); - } - inputStream.reset(); - } - - /** - * Close the wrapped InputStream, but don't close parent resource. - * - * @throws IOException - * if an I/O exception occurs. - */ - void closeInputStream() throws IOException { - if (inputStream != null) { - try { - inputStream.close(); - } catch (final IOException e) { - // Ignore - } - inputStream = null; - } - } - - /** - * Close the parent resource by calling {@link Resource#close()}, which will call - * {@link #closeInputStream()}. - * - * @throws IOException - * if an I/O exception occurs. - */ - @Override - public void close() throws IOException { - if (parentResource != null) { - parentResource.close(); - parentResource = null; - } - } - } - - // ------------------------------------------------------------------------------------------------------------- - /** * Mark the resource as open. * @@ -490,14 +293,13 @@ public String getContentAsString() throws IOException { public abstract byte[] load() throws IOException; /** - * Open a {@link ByteBuffer}, if there is an efficient underlying mechanism for opening one, otherwise open an - * {@link InputStream}. + * Open a {@link ClassfileReader} on the resource (for reading classfiles). * - * @return the {@link InputStreamOrByteBufferAdapter} + * @return the {@link ClassfileReader}. * @throws IOException * if an I/O exception occurs. */ - abstract InputStreamOrByteBufferAdapter openOrRead() throws IOException; + abstract ClassfileReader openClassfile() throws IOException; /** * Get the length of the resource. @@ -591,11 +393,7 @@ public void close() { // Override in subclasses, and call super.close(), then at end, markAsClosed() if (inputStream != null) { try { - if (inputStream instanceof InputStreamResourceCloser) { - ((InputStreamResourceCloser) inputStream).closeInputStream(); - } else { - inputStream.close(); - } + inputStream.close(); } catch (final IOException 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 8d5aa848a..f9e88278b 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java @@ -36,6 +36,7 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.Set; import io.github.classgraph.ClassGraph.ClasspathElementFilter; @@ -78,6 +79,23 @@ public ClasspathElementAndClassLoader(final Object classpathElement, final Class this.classpathElement = classpathElement; this.classLoader = classLoader; } + + @Override + public int hashCode() { + return Objects.hash(classpathElement, classLoader); + } + + @Override + public boolean equals(final Object obj) { + if (obj == this) { + return true; + } else if (!(obj instanceof ClasspathElementAndClassLoader)) { + return false; + } + final ClasspathElementAndClassLoader other = (ClasspathElementAndClassLoader) obj; + return Objects.equals(this.classpathElement, other.classpathElement) + && Objects.equals(this.classLoader, other.classLoader); + } } /** diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ByteBufferWrapper.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ByteBufferWrapper.java deleted file mode 100644 index 85050c9df..000000000 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ByteBufferWrapper.java +++ /dev/null @@ -1,388 +0,0 @@ -/* - * This file is part of ClassGraph. - * - * Author: Luke Hutchison - * - * Hosted at: https://github.com/classgraph/classgraph - * - * -- - * - * The MIT License (MIT) - * - * Copyright (c) 2020 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.fastzipfilereader; - -import java.io.EOFException; -import java.io.File; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.nio.Buffer; -import java.nio.BufferUnderflowException; -import java.nio.ByteBuffer; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.nio.channels.NonReadableChannelException; - -import nonapi.io.github.classgraph.utils.FileUtils; -import nonapi.io.github.classgraph.utils.LogNode; - -/** - * A wrapper for either {@link ByteBuffer} (backed by an array in RAM or mapped to a file on disk as a - * {@link MappedByteBuffer}), or a {@link RandomAccessFile} (which is slower, but doesn't consume either resident - * RAM or virtual memory address space). - */ -/** - * @author luke - * - */ -/** - * @author luke - * - */ -public final class ByteBufferWrapper { - - /** The {@link ByteBuffer} to use, or if null, use a {@link RandomAccessFile} instead. */ - private ByteBuffer byteBuffer; - - /** True if byteBuffer is a {@link MemoryMappedByteBuffer}. */ - private boolean isMemoryMappedByteBuffer; - - /** The {@link RandomAccessFile} to use, if byteBuffer is null. */ - private RandomAccessFile raf; - - /** The {@link File} to use, if memory mapping is disabled. */ - private File file; - - /** The start position of the {@link RandomAccessFile} slice. */ - private long rafSliceStart; - - /** The limit of the {@link RandomAccessFile} slice. */ - private int rafSliceLength; - - /** The length of the {@link RandomAccessFile}. */ - private long rafLength; - - /** - * Wrap a range of a {@link RandomAccessFile}. - * - * @param file - * the {@link File} to map. - * @param sliceStart - * The starting position of the slice within the file. - * @param sliceLength - * The length of the slice (must be smaller than (2GB - 8B)). - */ - public ByteBufferWrapper(final File file, final long sliceStart, final long sliceLength) throws IOException { - final long fileLen = file.length(); - if (sliceStart < 0 || sliceStart >= fileLen) { - throw new IllegalArgumentException("start out of range: " + sliceLength); - } - if (sliceLength > FileUtils.MAX_BUFFER_SIZE) { - throw new IllegalArgumentException("len > max (" + FileUtils.MAX_BUFFER_SIZE + ")"); - } - if (sliceLength > fileLen - sliceStart) { - throw new IllegalArgumentException("len > " + (fileLen - sliceStart)); - } - this.file = file; - // RandomAccessFile#length() is not threadsafe, so use File.length(): - // https://bugs.java.com/bugdatabase/view_bug.do?bug_id=4823133 - rafLength = file.length(); - raf = new RandomAccessFile(file, "r"); - raf.seek(sliceStart); - rafSliceStart = sliceStart; - rafSliceLength = (int) sliceLength; - } - - /** - * Memory-map a chunk of a file. - * - * @param fileChannel - * a {@link FileChannel} to map. - * @param start - * The starting position to map. - * @param len - * The number of bytes to map (must be smaller than (2GB - 8B)). - * @throws IOException - * if file cannot be mapped. - * @throws OutOfMemoryError - * if virtual memory space runs out. - */ - public ByteBufferWrapper(final FileChannel fileChannel, final long start, final long len) throws IOException { - if (len > FileUtils.MAX_BUFFER_SIZE) { - throw new IllegalArgumentException("len > " + FileUtils.MAX_BUFFER_SIZE); - } - try { - // Try mapping the FileChannel - byteBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, start, len); - isMemoryMappedByteBuffer = true; - } catch (final OutOfMemoryError e) { - // If map failed, try calling System.gc() to free some allocated MappedByteBuffers - // (there is a limit to the number of mapped files -- 64k on Linux) - // See: http://www.mapdb.org/blog/mmap_files_alloc_and_jvm_crash/ - System.gc(); - System.runFinalization(); - // Then try calling map again - byteBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, start, len); - // May throw OutOfMemoryError again; caller should catch this - isMemoryMappedByteBuffer = true; - } catch (NonReadableChannelException | IllegalArgumentException | UnsupportedOperationException e) { - // None of these exceptions should be thrown, but wrap in IOException for simplicity - throw new IOException(e); - } - } - - /** - * Wrap an array in a {@link ByteBuffer}. - * - * @param arr - * The array to wrap. - * @throws OutOfMemoryError - * if virtual memory space runs out. - */ - public ByteBufferWrapper(final byte[] arr) { - this.byteBuffer = ByteBuffer.wrap(arr, 0, arr.length); - } - - /** - * Wrap a {@link ByteBuffer}. - * - * @param byteBuffer - * The byte buffer to wrap. - * @throws OutOfMemoryError - * if virtual memory space runs out. - */ - public ByteBufferWrapper(final ByteBuffer byteBuffer) { - this.byteBuffer = byteBuffer; - } - - /** - * Duplicate this {@link ByteBufferWrapper} by duplicating the underlying {@link ByteBuffer}, or opening another - * {@link RandomAccessFile} on the same file. - * - * @throws IOException - * if file can no longer be opened. - */ - public ByteBufferWrapper duplicate() throws IOException { - return byteBuffer != null ? new ByteBufferWrapper(byteBuffer.duplicate()) - : new ByteBufferWrapper(file, rafSliceStart, rafLength); - } - - /** - * Slice this {@link ByteBufferWrapper} by duplicating the underlying {@link ByteBuffer}, or opening another - * {@link RandomAccessFile} on the same file, then slicing it. - * - * @throws IOException - * if file can no longer be opened. - */ - public ByteBufferWrapper slice(final int childSliceStart, final int childSliceLength) throws IOException { - if (byteBuffer != null) { - // Slice a ByteBuffer - - // Duplicate the ByteBuffer so that the position and limit are not changed on the original - // (In JDK 13, there is a slice(start, len), which eliminates this need.) - final ByteBuffer dup = byteBuffer.duplicate(); - - // Create and return a slice on the chunk ByteBuffer that contains only this zip entry - // N.B. the cast to Buffer is necessary, see: - // https://github.com/plasma-umass/doppio/issues/497#issuecomment-334740243 - // https://github.com/classgraph/classgraph/issues/284#issuecomment-443612800 - ((Buffer) byteBuffer).mark(); - try { - ((Buffer) dup).position(childSliceStart); - ((Buffer) dup).limit(childSliceStart + childSliceLength); - final ByteBufferWrapper slice = new ByteBufferWrapper(dup.slice()); - return slice; - } finally { - ((Buffer) byteBuffer).reset(); - ((Buffer) dup).limit(dup.capacity()); - } - - } else { - // Memory mapping is disabled -- open another RandomAccessFile on a slice of the current one - if (childSliceStart + childSliceLength > rafLength) { - throw new IOException("Child slice extends past end of file"); - } - if (childSliceLength > rafSliceLength) { - throw new IOException("Child slice larger than parent slice"); - } - return new ByteBufferWrapper(file, rafSliceStart + childSliceStart, childSliceLength); - } - } - - /** Return true if remaining() returns a number greater than zero. */ - public boolean hasRemaining() { - return remaining() > 0; - } - - /** Return the number of bytes remaining in the wrapped file slice. */ - public int remaining() { - try { - if (byteBuffer != null) { - return byteBuffer.remaining(); - } else { - final long rafSliceEnd = rafSliceStart + rafSliceLength; - return Math.max(0, (int) Math.max(0, rafSliceEnd - raf.getFilePointer())); - } - } catch (final IOException e) { - return 0; - } - } - - /** - * Read bytes from the current position in the underlying {@link ByteBuffer} or {@link RandomAccessFile} into a - * byte array. - * - * @param arr - * The byte array to read into. - * @param arrStart - * The start position in the array. - * @param numBytesToRead - * The number of bytes to read. - * @throws IOException - * If the file is closed or the requested number of bytes could not be read. - */ - public void get(final byte[] arr, final int arrStart, final int numBytesToRead) throws IOException { - if (byteBuffer != null) { - // Read from the wrapped ByteBuffer - byteBuffer.get(arr, arrStart, numBytesToRead); - - } else { - // Read from the wrapped RandomAccessFile - if (raf.read(arr, arrStart, numBytesToRead) < numBytesToRead) { - throw new IOException("Unexpected EOF"); - } - } - } - - /** - * Read bytes from the specified position in the underlying {@link ByteBuffer} or {@link RandomAccessFile} into - * a byte array. - * - * @param off - * The offset within the slice to start reading from. - * @param arr - * The byte array to read into. - * @param arrStart - * The start position in the array. - * @param numBytesToRead - * The number of bytes to read. - * @throws IOException - * If the file is closed or the requested number of bytes could not be read. - */ - public void get(final int off, final byte[] arr, final int arrStart, final int numBytesToRead) - throws IOException { - if (byteBuffer != null) { - // Read from the wrapped ByteBuffer - if (numBytesToRead > byteBuffer.remaining()) { - throw new IOException("Unexpected EOF"); - } - // N.B. the cast to Buffer is necessary, see: - // https://github.com/plasma-umass/doppio/issues/497#issuecomment-334740243 - // https://github.com/classgraph/classgraph/issues/284#issuecomment-443612800 - // Otherwise compiling in JDK<9 compatibility mode using JDK9+ causes runtime breakage. - ((Buffer) byteBuffer).mark(); - try { - ((Buffer) byteBuffer).position(off); - byteBuffer.get(arr, arrStart, numBytesToRead); - } catch (final BufferUnderflowException e) { - // Should not happen - throw new EOFException("Unexpected EOF"); - } finally { - ((Buffer) byteBuffer).reset(); - } - - } else { - // Read from the wrapped RandomAccessFile - final long currPos = raf.getFilePointer(); - raf.seek(rafSliceStart + off); - if (raf.read(arr, arrStart, numBytesToRead) < numBytesToRead) { - throw new IOException("Unexpected EOF"); - } - raf.seek(currPos); - } - } - - /** - * Skip the given number of bytes from the current position. - * - * @param numBytesToSkip - * the number of bytes to skip. - */ - public void skip(final int numBytesToSkip) throws IOException { - if (numBytesToSkip == 0) { - return; - } else if (numBytesToSkip < 0) { - throw new IllegalArgumentException("Tried to skip a negative number of bytes"); - } - if (byteBuffer != null) { - try { - // N.B. the cast to Buffer is necessary, see: - // https://github.com/plasma-umass/doppio/issues/497#issuecomment-334740243 - // https://github.com/classgraph/classgraph/issues/284#issuecomment-443612800 - ((Buffer) byteBuffer).position(byteBuffer.position() + numBytesToSkip); - } catch (final IllegalArgumentException e) { - throw new IOException("Unexpected EOF"); - } - } else { - if (raf.skipBytes(numBytesToSkip) < numBytesToSkip) { - throw new IOException("Unexpected EOF"); - } - } - } - - /** - * @return the current position in the file slice. - */ - public int position() throws IOException { - if (byteBuffer != null) { - return byteBuffer.position(); - } else { - return (int) (raf.getFilePointer() - rafSliceStart); - } - } - - /** - * Get the wrapped {@link ByteBuffer}, or returns null if this file is backed by a {@link RandomAccessFile} - * instead. - */ - public ByteBuffer getByteBuffer() { - return byteBuffer; - } - - /** - * Unmap the wrapped {@link ByteBuffer} (if this object wraps a {@link MappedByteBuffer} and not an array-backed - * {@link ByteBuffer} or {@link RandomAccessFile}). - * - * @param log - * the log - */ - public void close(final LogNode log) { - if (isMemoryMappedByteBuffer) { - FileUtils.closeDirectByteBuffer(byteBuffer, log); - } - if (raf != null) { - try { - raf.close(); - } catch (final IOException e) { - // Ignore - } - raf = null; - } - } -} 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 9ddaa1c95..05946aa59 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/FastZipEntry.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/FastZipEntry.java @@ -28,19 +28,12 @@ */ package nonapi.io.github.classgraph.fastzipfilereader; -import java.io.EOFException; import java.io.IOException; -import java.io.InputStream; -import java.nio.BufferUnderflowException; import java.util.Calendar; import java.util.TimeZone; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.zip.DataFormatException; -import java.util.zip.Inflater; -import java.util.zip.ZipException; -import nonapi.io.github.classgraph.recycler.RecycleOnClose; -import nonapi.io.github.classgraph.utils.FileUtils; +import nonapi.io.github.classgraph.fileslice.Slice; +import nonapi.io.github.classgraph.fileslice.reader.RandomAccessReader; import nonapi.io.github.classgraph.utils.VersionFinder; /** A zip entry within a {@link LogicalZipFile}. */ @@ -51,9 +44,6 @@ public class FastZipEntry implements Comparable { /** The offset of the entry's local header, as an offset relative to the parent logical zipfile. */ private final long locHeaderPos; - /** The start offset of the entry's compressed data, as an absolute offset within the physical zipfile. */ - private long entryDataStartOffsetWithinPhysicalZipFile = -1L; - /** The zip entry path. */ public final String entryName; @@ -78,6 +68,9 @@ public class FastZipEntry implements Comparable { /** The file attributes for this resource, or 0 if unknown */ public final int fileAttributes; + /** The {@link Slice} for the zip entry's raw data (which can be either stored or deflated). */ + private Slice slice; + /** * The version code (>= 9), or 8 for the base layer or a non-versioned jar (whether JDK 7 or 8 compatible). */ @@ -88,12 +81,6 @@ public class FastZipEntry implements Comparable { */ public final String entryNameUnversioned; - /** The nested jar handler. */ - private final NestedJarHandler nestedJarHandler; - - /** The {@link RecyclableInflater} instance wrapping recyclable {@link Inflater} instances. */ - private RecyclableInflater recyclableInflaterInstance; - // ------------------------------------------------------------------------------------------------------------- /** @@ -111,8 +98,6 @@ public class FastZipEntry implements Comparable { * The compressed size of the entry. * @param uncompressedSize * The uncompressed size of the entry. - * @param nestedJarHandler - * The {@link NestedJarHandler}. * @param lastModifiedTimeMillis * The last modified date/time in millis since the epoch, or 0L if unknown (in which case, the MSDOS * time and date fields will be provided). @@ -125,15 +110,14 @@ 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 NestedJarHandler nestedJarHandler, final long lastModifiedTimeMillis, - final int lastModifiedTimeMSDOS, final int lastModifiedDateMSDOS, final int fileAttributes) { + final long lastModifiedTimeMillis, final int lastModifiedTimeMSDOS, final int lastModifiedDateMSDOS, + final int fileAttributes) { this.parentLogicalZipFile = parentLogicalZipFile; this.locHeaderPos = locHeaderPos; this.entryName = entryName; this.isDeflated = isDeflated; this.compressedSize = compressedSize; this.uncompressedSize = !isDeflated && uncompressedSize < 0 ? compressedSize : uncompressedSize; - this.nestedJarHandler = nestedJarHandler; this.lastModifiedTimeMillis = lastModifiedTimeMillis; this.lastModifiedTimeMSDOS = lastModifiedTimeMSDOS; this.lastModifiedDateMSDOS = lastModifiedDateMSDOS; @@ -195,430 +179,31 @@ public class FastZipEntry implements Comparable { // ------------------------------------------------------------------------------------------------------------- /** - * Lazily find zip entry data start offset -- this is deferred until zip entry data needs to be read, in order - * to avoid randomly seeking within zipfile for every entry as the central directory is read. + * Lazily get zip entry slice -- this is deferred until zip entry data needs to be read, in order to avoid + * randomly seeking within zipfile for every entry as the central directory is read. * * @return the offset within the physical zip file of the entry's start offset. * @throws IOException * If an I/O exception occurs. - * @throws InterruptedException - * If the thread was interrupted. - */ - long getEntryDataStartOffsetWithinPhysicalZipFile() throws IOException, InterruptedException { - if (entryDataStartOffsetWithinPhysicalZipFile == -1L) { - // Create zipfile slice reader for zip entry - try (RecycleOnClose zipFileSliceReaderRecycleOnClose = // - parentLogicalZipFile.zipFileSliceReaderRecycler.acquireRecycleOnClose()) { - final ZipFileSliceReader headerReader = zipFileSliceReaderRecycleOnClose.get(); - // Check header magic - if (headerReader.getInt(locHeaderPos) != 0x04034b50) { - throw new IOException("Zip entry has bad LOC header: " + entryName); - } - final long dataStartPos = locHeaderPos + 30 + headerReader.getShort(locHeaderPos + 26) - + headerReader.getShort(locHeaderPos + 28); - if (dataStartPos > parentLogicalZipFile.len) { - throw new IOException("Unexpected EOF when trying to read zip entry data: " + entryName); - } - entryDataStartOffsetWithinPhysicalZipFile = parentLogicalZipFile.startOffsetWithinPhysicalZipFile - + dataStartPos; - } - } - return entryDataStartOffsetWithinPhysicalZipFile; - } - - // ------------------------------------------------------------------------------------------------------------- - - /** - * True if the entire zip entry can be opened as a single ByteBuffer slice. - * - * @return true if the entire zip entry can be opened as a single ByteBuffer slice -- the entry must be STORED, - * and span only one 2GB buffer chunk. - * @throws IOException - * If an I/O exception occurs. - * @throws InterruptedException - * If the thread was interrupted. */ - public boolean canGetAsSlice() throws IOException, InterruptedException { - final long dataStartOffsetWithinPhysicalZipFile = getEntryDataStartOffsetWithinPhysicalZipFile(); - return !isDeflated // - && dataStartOffsetWithinPhysicalZipFile / FileUtils.MAX_BUFFER_SIZE // - == (dataStartOffsetWithinPhysicalZipFile + uncompressedSize) / FileUtils.MAX_BUFFER_SIZE; - } - - /** - * Open the ZipEntry as a ByteBuffer slice. Only call this method if {@link #canGetAsSlice()} returned true. - * - * @return the ZipEntry as a ByteBuffer. - * @throws IOException - * If an I/O exception occurs. - * @throws InterruptedException - * If the thread was interrupted. - */ - public ByteBufferWrapper getAsSlice() throws IOException, InterruptedException { - // Check the file is STORED and resides in only one chunk - if (!canGetAsSlice()) { - throw new IllegalArgumentException("Cannot open zip entry as a slice"); - } - final int sliceLength = (int) uncompressedSize; - - // Fetch the ByteBuffer for the applicable chunk - final long dataStartOffsetWithinPhysicalZipFile = getEntryDataStartOffsetWithinPhysicalZipFile(); - final int chunkIdx = (int) (dataStartOffsetWithinPhysicalZipFile / FileUtils.MAX_BUFFER_SIZE); - final long chunkStart = chunkIdx * (long) FileUtils.MAX_BUFFER_SIZE; - final int sliceStart = (int) (dataStartOffsetWithinPhysicalZipFile - chunkStart); - - // Duplicate and slice the ByteBuffer - return parentLogicalZipFile.physicalZipFile.getByteBuffer(chunkIdx).slice(sliceStart, sliceLength); - } - - // ------------------------------------------------------------------------------------------------------------- - - /** - * Open the data of the zip entry as an {@link InputStream}, inflating the data if the entry is deflated. - * - * @return the input stream - * @throws IOException - * If an I/O exception occurs. - * @throws InterruptedException - * if the thread was interrupted. - */ - public InputStream open() throws IOException, InterruptedException { - if (recyclableInflaterInstance != null) { - throw new IOException("Zip entry already open"); - } - if (isDeflated) { - recyclableInflaterInstance = nestedJarHandler.inflaterRecycler.acquire(); - } - return new InputStream() { - /** The data start offset within the physical zip file. */ - private final long dataStartOffsetWithinPhysicalZipFile = getEntryDataStartOffsetWithinPhysicalZipFile(); - - /** A scratch buffer. */ - private final byte[] scratch = new byte[8 * 1024]; - - /** The current 2GB chunk of the zip entry. */ - private ByteBufferWrapper currChunkByteBuf; - - /** True if the current 2GB chunk is the last chunk in the zip entry. */ - private boolean isLastChunk; - - /** The index of the current 2GB chunk. */ - private int currChunkIdx; - - /** True if the end of the zip entry has been reached. */ - private boolean eof; - - /** The {@link Inflater} instance, or null if the entry is stored rather than deflated. */ - private final Inflater inflater = isDeflated ? recyclableInflaterInstance.getInflater() : null; - - /** True if this {@link InputStream} has been closed. */ - private final AtomicBoolean closed = new AtomicBoolean(false); - - /** The size of the {@link Inflate} buffer to use. */ - private static final int INFLATE_BUF_SIZE = 8 * 1024; - - // Open the first 2GB chunk. - { - // Calculate the chunk index for the first chunk - currChunkIdx = (int) (dataStartOffsetWithinPhysicalZipFile / FileUtils.MAX_BUFFER_SIZE); - - // Calculate the start position within the first chunk, and set the position of the slice. - // N.B. the cast to Buffer is necessary, see: - // https://github.com/plasma-umass/doppio/issues/497#issuecomment-334740243 - // https://github.com/classgraph/classgraph/issues/284#issuecomment-443612800 - final int chunkPos = (int) (dataStartOffsetWithinPhysicalZipFile - - (((long) currChunkIdx) * (long) FileUtils.MAX_BUFFER_SIZE)); - - // Calculate end pos for the first chunk, and truncate it if it overflows 2GB - final int chunkLength = (int) Math.min(FileUtils.MAX_BUFFER_SIZE, compressedSize); - // True if there's only one chunk (first chunk is also last chunk) - isLastChunk = chunkLength == compressedSize; - - // Get the MappedByteBuffer for the 2GB chunk, duplicate it and slice it - currChunkByteBuf = parentLogicalZipFile.physicalZipFile.getByteBuffer(currChunkIdx).slice(chunkPos, - chunkLength); - } - - /** Advance to the next 2GB chunk. */ - private boolean readNextChunk() throws IOException, InterruptedException { - currChunkIdx++; - isLastChunk = currChunkIdx >= parentLogicalZipFile.physicalZipFile.numChunks() - 1; - if (currChunkIdx >= parentLogicalZipFile.physicalZipFile.numChunks()) { - // Ran out of chunks - return false; - } - - // Get the MappedByteBuffer for the next 2GB chunk, and duplicate it - currChunkByteBuf = parentLogicalZipFile.physicalZipFile.getByteBuffer(currChunkIdx).duplicate(); - return true; - } - - /** - * Inflate deflated data. - * - * @param buf - * the buffer to inflate into. - * @param off - * the offset within buf to start writing. - * @param len - * the number of bytes of uncompressed data to read. - * @return the number of bytes read. - * @throws IOException - * if an I/O exception occurred. - * @throws InterruptedException - * if the thread was interrupted. - */ - private int readDeflated(final byte[] buf, final int off, final int len) - throws IOException, InterruptedException { - try { - final byte[] inflateBuf = new byte[INFLATE_BUF_SIZE]; - int numInflatedBytes; - while ((numInflatedBytes = inflater.inflate(buf, off, len)) == 0) { - if (inflater.finished() || inflater.needsDictionary()) { - eof = true; - return -1; - } - if (inflater.needsInput()) { - // Check if there's still data left in the current chunk - if (!currChunkByteBuf.hasRemaining() - // No more bytes in current chunk -- get next chunk, and then make sure - // that currChunkByteBuf.hasRemaining() subsequently returns true - && !(readNextChunk() && currChunkByteBuf.hasRemaining())) { - // Ran out of data in the current chunk, and could not read a new chunk - throw new IOException("Unexpected EOF in deflated data"); - } - // Set inflater input for the current chunk - - // In JDK11+: simply use the following instead of all the lines below: - // inflater.setInput(currChunkByteBuf); - // N.B. the ByteBuffer version of setInput doesn't seem to need the extra - // padding byte at the end when using the "nowrap" Inflater option. - - // Copy from the ByteBuffer into a temporary byte[] array (needed for JDK<11). - try { - final int remaining = currChunkByteBuf.remaining(); - if (isLastChunk && remaining < inflateBuf.length) { - // An extra dummy byte is needed at the end of the input stream when - // using the "nowrap" Inflater option. - // See: ZipFile.ZipFileInputStream.fill() - currChunkByteBuf.get(inflateBuf, 0, remaining); - inflateBuf[remaining] = (byte) 0; - inflater.setInput(inflateBuf, 0, remaining + 1); - } else if (isLastChunk && remaining == inflateBuf.length) { - // If this is the last chunk to read, and the number of remaining - // bytes is exactly the size of the buffer, read one byte fewer than - // the number of remaining bytes, to cause the last byte to be read - // in an extra pass. - currChunkByteBuf.get(inflateBuf, 0, remaining - 1); - inflater.setInput(inflateBuf, 0, remaining - 1); - } else { - // There are more than inflateBuf.length bytes remaining to be read, - // or this is not the last chunk (i.e. read all remaining bytes in - // this chunk, which will trigger the next chunk to be read on the - // next loop iteration) - final int bytesToRead = Math.min(inflateBuf.length, remaining); - currChunkByteBuf.get(inflateBuf, 0, bytesToRead); - inflater.setInput(inflateBuf, 0, bytesToRead); - } - } catch (final BufferUnderflowException e) { - // Should not happen - throw new IOException("Unexpected EOF in deflated data"); - } - } - } - return numInflatedBytes; - } catch (final DataFormatException e) { - throw new ZipException( - e.getMessage() != null ? e.getMessage() : "Invalid deflated zip entry data"); - } - } - - /** - * Copy stored (non-deflated) data from ByteBuffer to target buffer. - * - * @param buf - * the buffer to copy the stored entry into. - * @param off - * the offset within buf to start writing. - * @param len - * the number of bytes to read. - * @return the number of bytes read. - * @throws IOException - * if an I/O exception occurred. - * @throws InterruptedException - * if the thread was interrupted. - */ - private int readStored(final byte[] buf, final int off, final int len) - throws IOException, InterruptedException { - int read = 0; - while (read < len) { - if (!currChunkByteBuf.hasRemaining() && !readNextChunk()) { - return read == 0 ? -1 : read; - } - final int remainingToRead = len - read; - final int remainingInBuf = currChunkByteBuf.remaining(); - final int numBytesRead = Math.min(remainingToRead, remainingInBuf); - currChunkByteBuf.get(buf, off + read, numBytesRead); - read += numBytesRead; - } - return read; - } - - /** - * Skip stored (non-deflated) data in ByteBuffer. - * - * @param n - * the number of bytes to skip. - * @throws IOException - * if an I/O exception occurred or the thread was interrupted. - */ - private void skipStored(final long n) throws IOException { - try { - long skipped = 0; - while (skipped < n) { - if (!currChunkByteBuf.hasRemaining() && !readNextChunk()) { - throw new EOFException("Unexpected EOF while skipping (non-deflated) zip entry data"); - } - final long remainingToSkip = n - skipped; - final int remainingInBuf = currChunkByteBuf.remaining(); - final int numBytesToSkip = (int) Math.min(FileUtils.MAX_BUFFER_SIZE, - Math.min(remainingToSkip, remainingInBuf)); - currChunkByteBuf.skip(numBytesToSkip); - skipped += numBytesToSkip; - } - } catch (final InterruptedException e) { - nestedJarHandler.interruptionChecker.interrupt(); - throw new IOException("Thread was interrupted"); - } - } - - @Override - public int read(final byte[] buf, final int off, final int len) throws IOException { - if (closed.get()) { - throw new IOException("Stream closed"); - } - if (buf == null) { - throw new NullPointerException(); - } else if (off < 0 || len < 0 || len > buf.length - off) { - throw new IndexOutOfBoundsException(); - } else if (len == 0) { - return 0; - } else if (parentLogicalZipFile.physicalZipFile.length() == 0) { - return -1; - } - try { - if (isDeflated) { - return readDeflated(buf, off, len); - } else { - return readStored(buf, off, len); - } - } catch (final InterruptedException e) { - nestedJarHandler.interruptionChecker.interrupt(); - throw new IOException("Thread was interrupted"); - } - } - - @Override - public int read() throws IOException { - if (closed.get()) { - throw new IOException("Stream closed"); - } - return read(scratch, 0, 1) == -1 ? -1 : scratch[0] & 0xff; - } - - @Override - public int available() throws IOException { - if (closed.get()) { - throw new IOException("Stream closed"); - } - if (inflater.finished()) { - eof = true; - } - return eof ? 0 : 1; - } - - @Override - public long skip(final long n) throws IOException { - if (closed.get()) { - throw new IOException("Stream closed"); - } - if (n < 0) { - throw new IllegalArgumentException("Invalid skip value"); - } - if (isDeflated) { - long total = 0; - while (total < n) { - final int bytesToSkip = (int) Math.min(n - total, scratch.length); - final int numSkipped = read(scratch, 0, bytesToSkip); - if (numSkipped == -1) { - eof = true; - break; - } - total += numSkipped; - } - } else { - skipStored(n); - } - return n; - } + public Slice getSlice() throws IOException { + if (slice == null) { + final RandomAccessReader randomAccessReader = parentLogicalZipFile.slice.randomAccessReader(); - @Override - public boolean markSupported() { - return false; + // Check header magic + if (randomAccessReader.readInt(locHeaderPos) != 0x04034b50) { + throw new IOException("Zip entry has bad LOC header: " + entryName); } - - @Override - public synchronized void mark(final int readlimit) { - throw new IllegalArgumentException("Not supported"); + final long dataStartPos = locHeaderPos + 30 + randomAccessReader.readShort(locHeaderPos + 26) + + randomAccessReader.readShort(locHeaderPos + 28); + if (dataStartPos > parentLogicalZipFile.slice.sliceLength) { + throw new IOException("Unexpected EOF when trying to read zip entry data: " + entryName); } - @Override - public synchronized void reset() throws IOException { - throw new IllegalArgumentException("Not supported"); - } - - @Override - public void close() throws IOException { - if (!closed.getAndSet(true)) { - currChunkByteBuf = null; - if (recyclableInflaterInstance != null) { - // Reset and recycle the Inflater - nestedJarHandler.inflaterRecycler.recycle(recyclableInflaterInstance); - recyclableInflaterInstance = null; - } - } - } - }; - } - - /** - * Load the content of the zip entry, and return it as a byte array. - * - * @return the entry as a byte[] array - * @throws IOException - * If an I/O exception occurs. - * @throws InterruptedException - * If the thread was interrupted. - */ - public byte[] load() throws IOException, InterruptedException { - try (InputStream is = open()) { - return FileUtils.readAllBytesAsArray(is, uncompressedSize); - } - } - - /** - * Load the content of the zip entry, and return it as a String (converting from UTF-8 byte format). - * - * @return the entry as a String - * @throws IOException - * If an I/O exception occurs. - * @throws InterruptedException - * If the thread was interrupted. - */ - public String loadAsString() throws IOException, InterruptedException { - try (InputStream is = open()) { - return FileUtils.readAllBytesAsString(is, uncompressedSize); + // Create a new Slice that wraps just the data of the zip entry, and mark whether it is deflated + slice = parentLogicalZipFile.slice.slice(dataStartPos, compressedSize, isDeflated, uncompressedSize); } + return slice; } // ------------------------------------------------------------------------------------------------------------- @@ -662,14 +247,6 @@ public long getLastModifiedTimeMillis() { return lastModifiedTimeMillis; } - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return "jar:file:" + getPath(); - } - /** * Sort in decreasing order of version number, then lexicographically increasing order of unversioned entry * path. @@ -698,6 +275,14 @@ public int compareTo(final FastZipEntry o) { return diff3 < 0L ? -1 : diff3 > 0L ? 1 : 0; } + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return parentLogicalZipFile.hashCode() ^ version ^ entryName.hashCode() ^ (int) locHeaderPos; + } + /* (non-Javadoc) * @see java.lang.Object#equals(java.lang.Object) */ @@ -713,10 +298,10 @@ public boolean equals(final Object obj) { } /* (non-Javadoc) - * @see java.lang.Object#hashCode() + * @see java.lang.Object#toString() */ @Override - public int hashCode() { - return parentLogicalZipFile.hashCode() ^ version ^ entryName.hashCode() ^ (int) locHeaderPos; + public String toString() { + return "jar:file:" + getPath(); } } 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 b4ad5627b..6f30e7ede 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java @@ -32,10 +32,6 @@ import java.io.EOFException; import java.io.IOException; import java.io.UnsupportedEncodingException; -import java.nio.ByteBuffer; -import java.nio.charset.CharacterCodingException; -import java.nio.charset.CharsetDecoder; -import java.nio.charset.CodingErrorAction; import java.nio.charset.StandardCharsets; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; @@ -49,7 +45,8 @@ import java.util.concurrent.ConcurrentHashMap; import io.github.classgraph.ClassGraphException; -import nonapi.io.github.classgraph.recycler.RecycleOnClose; +import nonapi.io.github.classgraph.fileslice.ArraySlice; +import nonapi.io.github.classgraph.fileslice.reader.RandomAccessReader; import nonapi.io.github.classgraph.utils.CollectionUtils; import nonapi.io.github.classgraph.utils.FileUtils; import nonapi.io.github.classgraph.utils.Join; @@ -59,7 +56,7 @@ /** * A logical zipfile, which represents a zipfile contained within a ZipFileSlice of a PhysicalZipFile. */ -public class LogicalZipFile extends ZipFileSlice implements AutoCloseable { +public class LogicalZipFile extends ZipFileSlice { /** The zipfile entries. */ public List entries; @@ -152,10 +149,7 @@ public class LogicalZipFile extends ZipFileSlice implements AutoCloseable { */ LogicalZipFile(final ZipFileSlice zipFileSlice, final LogNode log) throws IOException, InterruptedException { super(zipFileSlice); - try (RecycleOnClose zipFileSliceReaderRecycleOnClose = // - zipFileSliceReaderRecycler.acquireRecycleOnClose()) { - readCentralDirectory(zipFileSliceReaderRecycleOnClose.get(), log); - } + readCentralDirectory(log); } // ------------------------------------------------------------------------------------------------------------- @@ -288,7 +282,7 @@ private static boolean keyMatchesAtPosition(final byte[] manifest, final byte[] private void parseManifest(final FastZipEntry manifestZipEntry, final LogNode log) throws IOException, InterruptedException { // Load contents of manifest entry as a byte array - final byte[] manifest = manifestZipEntry.load(); + final byte[] manifest = manifestZipEntry.getSlice().load(); // Find field keys (separated by newlines) for (int i = 0; i < manifest.length;) { @@ -428,8 +422,6 @@ private void parseManifest(final FastZipEntry manifestZipEntry, final LogNode lo /** * Read the central directory of the zipfile. * - * @param zipFileSliceReader - * the zipfile slice reader * @param log * the log * @throws IOException @@ -437,47 +429,68 @@ private void parseManifest(final FastZipEntry manifestZipEntry, final LogNode lo * @throws InterruptedException * if the thread was interrupted. */ - private void readCentralDirectory(final ZipFileSliceReader zipFileSliceReader, final LogNode log) - throws IOException, InterruptedException { - // Scan for End Of Central Directory (EOCD) signature + private void readCentralDirectory(final LogNode log) throws IOException, InterruptedException { + final RandomAccessReader reader = slice.randomAccessReader(); + + // Scan for End Of Central Directory (EOCD) signature. Final comment can be up to 64kB in length, + // so need to scan back that far to determine if this is a valid zipfile. However for speed, + // initially just try reading back a maximum of 32 characters. long eocdPos = -1; - for (long i = len - 22; i >= 0; --i) { - if (zipFileSliceReader.getInt(i) == 0x06054b50) { + for (long i = slice.sliceLength - 22, iMin = slice.sliceLength - 22 - 32; i >= iMin; --i) { + if (reader.readInt(i) == 0x06054b50) { eocdPos = i; break; } } + if (eocdPos < 0) { + // If EOCD signature was not found, read the last 64kB of file to RAM in a single chunk + // so that we can scan back through it at higher speed to locate the EOCD signature + final int bytesToRead = (int) Math.min(slice.sliceLength, 22 + (1 << 16)); + final byte[] eocdBytes = new byte[bytesToRead]; + final long readStartOff = slice.sliceLength - bytesToRead; + if (reader.read(readStartOff, eocdBytes, 0, bytesToRead) < bytesToRead) { + // Should not happen + throw new IOException("Zipfile is truncated"); + } + final RandomAccessReader eocdReader = new ArraySlice(eocdBytes, /* isDeflatedZipEntry = */ false, + /* inflatedLengthHint = */ 0L, physicalZipFile.nestedJarHandler).randomAccessReader(); + for (long i = eocdBytes.length - 22; i >= 0; --i) { + if (eocdReader.readInt(i) == 0x06054b50) { + eocdPos = i + readStartOff; + break; + } + } + } if (eocdPos < 0) { throw new IOException("Jarfile central directory signature not found: " + getPath()); } - long numEnt = zipFileSliceReader.getShort(eocdPos + 8); - if (zipFileSliceReader.getShort(eocdPos + 4) > 0 || zipFileSliceReader.getShort(eocdPos + 6) > 0 - || numEnt != zipFileSliceReader.getShort(eocdPos + 10)) { + long numEnt = reader.readUnsignedShort(eocdPos + 8); + if (reader.readUnsignedShort(eocdPos + 4) > 0 || reader.readUnsignedShort(eocdPos + 6) > 0 + || numEnt != reader.readUnsignedShort(eocdPos + 10)) { throw new IOException("Multi-disk jarfiles not supported: " + getPath()); } - long cenSize = zipFileSliceReader.getInt(eocdPos + 12) & 0xffffffffL; + long cenSize = reader.readUnsignedInt(eocdPos + 12); if (cenSize > eocdPos) { throw new IOException( "Central directory size out of range: " + cenSize + " vs. " + eocdPos + ": " + getPath()); } - long cenOff = zipFileSliceReader.getInt(eocdPos + 16) & 0xffffffffL; + long cenOff = reader.readUnsignedInt(eocdPos + 16); long cenPos = eocdPos - cenSize; // Check for Zip64 End Of Central Directory Locator record final long zip64cdLocIdx = eocdPos - 20; - if (zip64cdLocIdx >= 0 && zipFileSliceReader.getInt(zip64cdLocIdx) == 0x07064b50) { - if (zipFileSliceReader.getInt(zip64cdLocIdx + 4) > 0 - || zipFileSliceReader.getInt(zip64cdLocIdx + 16) > 1) { + if (zip64cdLocIdx >= 0 && reader.readInt(zip64cdLocIdx) == 0x07064b50) { + if (reader.readInt(zip64cdLocIdx + 4) > 0 || reader.readInt(zip64cdLocIdx + 16) > 1) { throw new IOException("Multi-disk jarfiles not supported: " + getPath()); } - final long eocdPos64 = zipFileSliceReader.getLong(zip64cdLocIdx + 8); - if (zipFileSliceReader.getInt(eocdPos64) != 0x06064b50) { + final long eocdPos64 = reader.readLong(zip64cdLocIdx + 8); + if (reader.readInt(eocdPos64) != 0x06064b50) { throw new IOException("Zip64 central directory at location " + eocdPos64 + " does not have Zip64 central directory header: " + getPath()); } - final long numEnt64 = zipFileSliceReader.getLong(eocdPos64 + 24); - if (zipFileSliceReader.getInt(eocdPos64 + 16) > 0 || zipFileSliceReader.getInt(eocdPos64 + 20) > 0 - || numEnt64 != zipFileSliceReader.getLong(eocdPos64 + 32)) { + final long numEnt64 = reader.readLong(eocdPos64 + 24); + if (reader.readInt(eocdPos64 + 16) > 0 || reader.readInt(eocdPos64 + 20) > 0 + || numEnt64 != reader.readLong(eocdPos64 + 32)) { throw new IOException("Multi-disk jarfiles not supported: " + getPath()); } if (numEnt == 0xffff) { @@ -487,7 +500,7 @@ private void readCentralDirectory(final ZipFileSliceReader zipFileSliceReader, f numEnt = -1L; } - final long cenSize64 = zipFileSliceReader.getLong(eocdPos64 + 40); + final long cenSize64 = reader.readLong(eocdPos64 + 40); if (cenSize == 0xffffffffL) { cenSize = cenSize64; } else if (cenSize != cenSize64) { @@ -498,7 +511,7 @@ private void readCentralDirectory(final ZipFileSliceReader zipFileSliceReader, f // Recalculate the central directory position cenPos = eocdPos64 - cenSize; - final long cenOff64 = zipFileSliceReader.getLong(eocdPos64 + 48); + final long cenOff64 = reader.readLong(eocdPos64 + 48); if (cenOff == 0xffffffffL) { cenOff = cenOff64; } else if (cenOff != cenOff64) { @@ -515,27 +528,39 @@ private void readCentralDirectory(final ZipFileSliceReader zipFileSliceReader, f // Read entries into a byte array, if central directory is smaller than 2GB. If central directory // is larger than 2GB, need to read each entry field from the file directly using ZipFileSliceReader. - final byte[] entryBytes = cenSize > FileUtils.MAX_BUFFER_SIZE ? null : new byte[(int) cenSize]; - if (entryBytes != null) { - zipFileSliceReader.read(cenPos, entryBytes, 0, (int) cenSize); + RandomAccessReader cenReader; + if (cenSize > FileUtils.MAX_BUFFER_SIZE) { + // Create a slice that covers the central directory (this allows a central directory larger than + // 2GB to be accessed using the slower FileSlice API, which reads the file directly, but also + // the slice can be accessed without adding cenPos to each read offset, so that this slice or + // the slice in the "else" clause below are accessed with the same index, which is the offset + // from the start of the central directory). + cenReader = slice.slice(cenPos, cenSize, /* isDeflatedZipEntry = */ false, /* inflatedSizeHint = */ 0L) + .randomAccessReader(); + } else { + // Read the central directory into RAM for speed, then wrap it in an ArraySlice + // (random access is faster for ArraySlice than for FileSlice) + final byte[] entryBytes = new byte[(int) cenSize]; + if (reader.read(cenPos, entryBytes, 0, (int) cenSize) < cenSize) { + // Should not happen + throw new IOException("Zipfile is truncated"); + } + cenReader = new ArraySlice(entryBytes, /* isDeflatedZipEntry = */ false, /* inflatedSizeHint = */ 0L, + physicalZipFile.nestedJarHandler).randomAccessReader(); } if (numEnt == -1L) { // numEnt and numEnt64 were inconsistent -- manually count entries numEnt = 0; for (long entOff = 0; entOff + 46 <= cenSize;) { - final int sig = entryBytes != null ? ZipFileSliceReader.getInt(entryBytes, entOff) - : zipFileSliceReader.getInt(cenPos + entOff); + final int sig = cenReader.readInt(entOff); if (sig != 0x02014b50) { throw new IOException("Invalid central directory signature: 0x" + Integer.toString(sig, 16) + ": " + getPath()); } - final int filenameLen = entryBytes != null ? ZipFileSliceReader.getShort(entryBytes, entOff + 28) - : zipFileSliceReader.getShort(cenPos + entOff + 28); - final int extraFieldLen = entryBytes != null ? ZipFileSliceReader.getShort(entryBytes, entOff + 30) - : zipFileSliceReader.getShort(cenPos + entOff + 30); - final int commentLen = entryBytes != null ? ZipFileSliceReader.getShort(entryBytes, entOff + 32) - : zipFileSliceReader.getShort(cenPos + entOff + 32); + final int filenameLen = cenReader.readUnsignedShort(entOff + 28); + final int extraFieldLen = cenReader.readUnsignedShort(entOff + 30); + final int commentLen = cenReader.readUnsignedShort(entOff + 32); entOff += 46 + filenameLen + extraFieldLen + commentLen; numEnt++; } @@ -548,31 +573,26 @@ private void readCentralDirectory(final ZipFileSliceReader zipFileSliceReader, f } // Make sure there's no DoS attack vector by using a fake number of entries - if (entryBytes != null && numEnt > entryBytes.length / 46) { + if (numEnt > cenSize / 46) { // The smallest directory entry is 46 bytes in size - throw new IOException("Too many zipfile entries: " + numEnt + " (expected a max of " - + entryBytes.length / 46 + " based on central directory size)"); + throw new IOException("Too many zipfile entries: " + numEnt + " (expected a max of " + cenSize / 46 + + " based on central directory size)"); } // Enumerate entries entries = new ArrayList<>((int) numEnt); FastZipEntry manifestZipEntry = null; - CharsetDecoder decoder = null; try { int entSize = 0; for (long entOff = 0; entOff + 46 <= cenSize; entOff += entSize) { - final int sig = entryBytes != null ? ZipFileSliceReader.getInt(entryBytes, entOff) - : zipFileSliceReader.getInt(cenPos + entOff); + final int sig = cenReader.readInt(entOff); if (sig != 0x02014b50) { throw new IOException("Invalid central directory signature: 0x" + Integer.toString(sig, 16) + ": " + getPath()); } - final int filenameLen = entryBytes != null ? ZipFileSliceReader.getShort(entryBytes, entOff + 28) - : zipFileSliceReader.getShort(cenPos + entOff + 28); - final int extraFieldLen = entryBytes != null ? ZipFileSliceReader.getShort(entryBytes, entOff + 30) - : zipFileSliceReader.getShort(cenPos + entOff + 30); - final int commentLen = entryBytes != null ? ZipFileSliceReader.getShort(entryBytes, entOff + 32) - : zipFileSliceReader.getShort(cenPos + entOff + 32); + final int filenameLen = cenReader.readUnsignedShort(entOff + 28); + final int extraFieldLen = cenReader.readUnsignedShort(entOff + 30); + final int commentLen = cenReader.readUnsignedShort(entOff + 32); entSize = 46 + filenameLen + extraFieldLen + commentLen; // Get and sanitize entry name @@ -584,9 +604,7 @@ private void readCentralDirectory(final ZipFileSliceReader zipFileSliceReader, f } break; } - final String entryName = entryBytes != null - ? ZipFileSliceReader.getString(entryBytes, filenameStartOff, filenameLen) - : zipFileSliceReader.getString(cenPos + filenameStartOff, filenameLen); + final String entryName = cenReader.readString(filenameStartOff, filenameLen); String entryNameSanitized = FileUtils.sanitizeEntryPath(entryName, /* removeInitialSlash = */ true); if (entryNameSanitized.isEmpty() || entryName.endsWith("/")) { // Skip directory entries @@ -594,8 +612,7 @@ private void readCentralDirectory(final ZipFileSliceReader zipFileSliceReader, f } // Check entry flag bits - final int flags = entryBytes != null ? ZipFileSliceReader.getShort(entryBytes, entOff + 8) - : zipFileSliceReader.getShort(cenPos + entOff + 8); + final int flags = cenReader.readUnsignedShort(entOff + 8); if ((flags & 1) != 0) { if (log != null) { log.log("Skipping encrypted zip entry: " + entryNameSanitized); @@ -604,9 +621,7 @@ private void readCentralDirectory(final ZipFileSliceReader zipFileSliceReader, f } // Check compression method - final int compressionMethod = entryBytes != null - ? ZipFileSliceReader.getShort(entryBytes, entOff + 10) - : zipFileSliceReader.getShort(cenPos + entOff + 10); + final int compressionMethod = cenReader.readUnsignedShort(entOff + 10); if (compressionMethod != /* stored */ 0 && compressionMethod != /* deflated */ 8) { if (log != null) { log.log("Skipping zip entry with invalid compression method " + compressionMethod + ": " @@ -617,17 +632,13 @@ private void readCentralDirectory(final ZipFileSliceReader zipFileSliceReader, f final boolean isDeflated = compressionMethod == /* deflated */ 8; // Get compressed and uncompressed size - long compressedSize = (entryBytes != null ? ZipFileSliceReader.getInt(entryBytes, entOff + 20) - : zipFileSliceReader.getInt(cenPos + entOff + 20)) & 0xffffffffL; - long uncompressedSize = (entryBytes != null ? ZipFileSliceReader.getInt(entryBytes, entOff + 24) - : zipFileSliceReader.getInt(cenPos + entOff + 24)) & 0xffffffffL; + long compressedSize = (cenReader.readUnsignedInt(entOff + 20)); + long uncompressedSize = (cenReader.readUnsignedInt(entOff + 24)); // Get external file attributes - final int fileAttributes = entryBytes != null ? ZipFileSliceReader.getShort(entryBytes, entOff + 40) - : zipFileSliceReader.getShort(cenPos + entOff + 40); + final int fileAttributes = cenReader.readUnsignedShort(entOff + 40); - long pos = entryBytes != null ? ZipFileSliceReader.getInt(entryBytes, entOff + 42) - : zipFileSliceReader.getInt(cenPos + entOff + 42); + long pos = cenReader.readInt(entOff + 42); // Check for Zip64 header in extra fields // See: @@ -637,10 +648,8 @@ private void readCentralDirectory(final ZipFileSliceReader zipFileSliceReader, f if (extraFieldLen > 0) { for (int extraFieldOff = 0; extraFieldOff + 4 < extraFieldLen;) { final long tagOff = filenameEndOff + extraFieldOff; - final int tag = entryBytes != null ? ZipFileSliceReader.getShort(entryBytes, tagOff) - : zipFileSliceReader.getShort(cenPos + tagOff); - final int size = entryBytes != null ? ZipFileSliceReader.getShort(entryBytes, tagOff + 2) - : zipFileSliceReader.getShort(cenPos + tagOff + 2); + final int tag = cenReader.readUnsignedShort(tagOff); + final int size = cenReader.readUnsignedShort(tagOff + 2); if (extraFieldOff + 4 + size > extraFieldLen) { // Invalid size if (log != null) { @@ -650,18 +659,14 @@ private void readCentralDirectory(final ZipFileSliceReader zipFileSliceReader, f } if (tag == 1 && size >= 20) { // Zip64 extended information extra field - final long uncompressedSize64 = entryBytes != null - ? ZipFileSliceReader.getLong(entryBytes, tagOff + 4 + 0) - : zipFileSliceReader.getLong(cenPos + tagOff + 4 + 0); + final long uncompressedSize64 = cenReader.readLong(tagOff + 4 + 0); if (uncompressedSize == 0xffffffffL) { uncompressedSize = uncompressedSize64; } else if (uncompressedSize != uncompressedSize64) { throw new IOException("Mismatch in uncompressed size: " + uncompressedSize + " vs. " + uncompressedSize64 + ": " + entryNameSanitized); } - final long compressedSize64 = entryBytes != null - ? ZipFileSliceReader.getLong(entryBytes, tagOff + 4 + 8) - : zipFileSliceReader.getLong(cenPos + tagOff + 4 + 8); + final long compressedSize64 = cenReader.readLong(tagOff + 4 + 8); if (compressedSize == 0xffffffffL) { compressedSize = compressedSize64; } else if (compressedSize != compressedSize64) { @@ -670,9 +675,7 @@ private void readCentralDirectory(final ZipFileSliceReader zipFileSliceReader, f } // Only compressed size and uncompressed size are required fields if (size >= 28) { - final long pos64 = entryBytes != null - ? ZipFileSliceReader.getLong(entryBytes, tagOff + 4 + 16) - : zipFileSliceReader.getLong(cenPos + tagOff + 4 + 16); + final long pos64 = cenReader.readLong(tagOff + 4 + 16); if (pos == 0xffffffffL) { pos = pos64; } else if (pos != pos64) { @@ -684,20 +687,14 @@ private void readCentralDirectory(final ZipFileSliceReader zipFileSliceReader, f } else if (tag == 0x5455 && size >= 5) { // Extended Unix timestamp - final byte bits = entryBytes != null - ? ZipFileSliceReader.getByte(entryBytes, tagOff + 4 + 0) - : zipFileSliceReader.getByte(cenPos + tagOff + 4 + 0); + final byte bits = cenReader.readByte(tagOff + 4 + 0); if ((bits & 1) == 1 && size >= 5 + 8) { - lastModifiedMillis = (entryBytes != null - ? ZipFileSliceReader.getLong(entryBytes, tagOff + 4 + 1) - : zipFileSliceReader.getLong(cenPos + tagOff + 4 + 1)) * 1000L; + lastModifiedMillis = cenReader.readLong(tagOff + 4 + 1) * 1000L; } } else if (tag == 0x5855 && size >= 20) { // Unix extra field (deprecated) - lastModifiedMillis = (entryBytes != null - ? ZipFileSliceReader.getLong(entryBytes, tagOff + 4 + 8) - : zipFileSliceReader.getLong(cenPos + tagOff + 4 + 8)) * 1000L; + lastModifiedMillis = cenReader.readLong(tagOff + 4 + 8) * 1000L; // There are also optional UID and GID fields in this extra field (currently ignored) } else if (tag == 0x7855) { @@ -705,26 +702,17 @@ private void readCentralDirectory(final ZipFileSliceReader zipFileSliceReader, f } else if (tag == 0x7075) { // Info-ZIP Unicode path extra field - final byte version = entryBytes != null - ? ZipFileSliceReader.getByte(entryBytes, tagOff + 4 + 0) - : zipFileSliceReader.getByte(cenPos + tagOff + 4 + 0); + final byte version = cenReader.readByte(tagOff + 4 + 0); if (version != 1) { throw new IOException("Unknown Unicode entry name format " + version + " in extra field: " + entryNameSanitized); } else if (size > 9) { - final byte[] utf8Bytes = (entryBytes != null - ? ZipFileSliceReader.getBytes(entryBytes, tagOff + 9, size - 9) - : zipFileSliceReader.getBytes(cenPos + tagOff + 9, size - 9)); - if (decoder == null) { - decoder = StandardCharsets.UTF_8.newDecoder(); - decoder.onMalformedInput(CodingErrorAction.REPORT) - .onUnmappableCharacter(CodingErrorAction.REPORT); - } + // Replace non-Unicode entry name with Unicode version try { - // Replace non-Unicode entry name with Unicode version - entryNameSanitized = decoder.decode(ByteBuffer.wrap(utf8Bytes)).toString(); - } catch (final CharacterCodingException e) { - throw new IOException("Malformed Unicode entry name: " + entryNameSanitized); + entryNameSanitized = cenReader.readString(tagOff + 9, size - 9); + } catch (final IllegalArgumentException e) { + throw new IOException("Malformed extended Unicode entry name for entry: " + + entryNameSanitized); } } } @@ -736,13 +724,8 @@ private void readCentralDirectory(final ZipFileSliceReader zipFileSliceReader, f int lastModifiedDateMSDOS = 0; if (lastModifiedMillis == 0L) { // If Unix timestamp was not provided, convert zip entry timestamp from MS-DOS format - lastModifiedTimeMSDOS = entryBytes != null - ? ZipFileSliceReader.getShort(entryBytes, entOff + 12) - : zipFileSliceReader.getShort(cenPos + entOff + 12); - - lastModifiedDateMSDOS = entryBytes != null - ? ZipFileSliceReader.getShort(entryBytes, entOff + 14) - : zipFileSliceReader.getShort(cenPos + entOff + 14); + lastModifiedTimeMSDOS = cenReader.readUnsignedShort(entOff + 12); + lastModifiedDateMSDOS = cenReader.readUnsignedShort(entOff + 14); } if (compressedSize < 0 || pos < 0) { @@ -756,7 +739,7 @@ private void readCentralDirectory(final ZipFileSliceReader zipFileSliceReader, f } continue; } - if (locHeaderPos + 4 >= len) { + if (locHeaderPos + 4 >= slice.sliceLength) { if (log != null) { log.log("Unexpected EOF when trying to read LOC header: " + entryNameSanitized); } @@ -765,8 +748,8 @@ private void readCentralDirectory(final ZipFileSliceReader zipFileSliceReader, f // Add zip entry final FastZipEntry entry = new FastZipEntry(this, locHeaderPos, entryNameSanitized, isDeflated, - compressedSize, uncompressedSize, physicalZipFile.nestedJarHandler, lastModifiedMillis, - lastModifiedTimeMSDOS, lastModifiedDateMSDOS, fileAttributes); + compressedSize, uncompressedSize, lastModifiedMillis, lastModifiedTimeMSDOS, + lastModifiedDateMSDOS, fileAttributes); entries.add(entry); // Record manifest entry @@ -836,22 +819,6 @@ private void readCentralDirectory(final ZipFileSliceReader zipFileSliceReader, f // ------------------------------------------------------------------------------------------------------------- - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.fastzipfilereader.ZipFileSlice#equals(java.lang.Object) - */ - @Override - public boolean equals(final Object o) { - return super.equals(o); - } - - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.fastzipfilereader.ZipFileSlice#hashCode() - */ - @Override - public int hashCode() { - return super.hashCode(); - } - /* (non-Javadoc) * @see nonapi.io.github.classgraph.fastzipfilereader.ZipFileSlice#toString() */ @@ -859,18 +826,4 @@ public int hashCode() { public String toString() { return getPath(); } - - /* (non-Javadoc) - * @see java.lang.AutoCloseable#close() - */ - @Override - public void close() { - if (zipFileSliceReaderRecycler != null) { - zipFileSliceReaderRecycler.close(); - } - if (entries != null) { - entries.clear(); - entries = null; - } - } } diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java deleted file mode 100644 index f49a98124..000000000 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/MappedByteBufferResources.java +++ /dev/null @@ -1,470 +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.fastzipfilereader; - -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.RandomAccessFile; -import java.nio.ByteBuffer; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.Arrays; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicLong; -import java.util.concurrent.atomic.AtomicReferenceArray; - -import nonapi.io.github.classgraph.concurrency.SingletonMap; -import nonapi.io.github.classgraph.concurrency.SingletonMap.NullSingletonException; -import nonapi.io.github.classgraph.scanspec.ScanSpec; -import nonapi.io.github.classgraph.utils.FileUtils; -import nonapi.io.github.classgraph.utils.LogNode; - -/** Resources for a mapped file. */ -public class MappedByteBufferResources { - /** If true, a file was mapped from a {@link FileChannel}. */ - private File mappedFile; - - /** If true, the mapped file was created as a temp file when the InputStream wouldn't fit in RAM. */ - private boolean mappedFileIsTempFile; - - /** The raf. */ - private RandomAccessFile raf; - - /** The file channel. */ - private FileChannel fileChannel; - - /** The total length. */ - private final AtomicLong length = new AtomicLong(); - - /** The cached mapped byte buffers for each 2GB chunk. */ - private AtomicReferenceArray byteBufferChunksCached; - - /** A singleton map from chunk index to byte buffer, ensuring that any given chunk is only mapped once. */ - private SingletonMap chunkIdxToByteBufferSingletonMap; - - /** The nested jar handler. */ - private final NestedJarHandler nestedJarHandler; - - /** Set to true once {@link #close()} has been called. */ - private final AtomicBoolean closed = new AtomicBoolean(false); - - /** - * Minimum buffer size, to ensure that erroneous zip entry sizes, which are used as size hints (e.g. - * uncompressed size == 0) do not result in a buffer being too small, which would result in unnecessary - * temporary files being created. - */ - private static final int MIN_BUFFER_SIZE = 16384; - - /** - * 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}. - * @param inputStreamLengthHint - * The number of bytes to read in inputStream, or -1 if unknown. - * @param tempFileBaseName - * the source URL or zip entry that inputStream was opened from (used to name temporary file, if - * needed). - * @param nestedJarHandler - * the nested jar handler - * @param log - * the log. - * @throws IOException - * If the contents could not be read. - */ - public MappedByteBufferResources(final InputStream inputStream, final int inputStreamLengthHint, - final String tempFileBaseName, final NestedJarHandler nestedJarHandler, final LogNode log) - throws IOException { - this.nestedJarHandler = nestedJarHandler; - final ScanSpec scanSpec = nestedJarHandler.scanSpec; - if (inputStreamLengthHint <= scanSpec.maxBufferedJarRAMSize) { - // inputStreamLengthHint is unknown (-1) or shorter than scanSpec.maxJarRamSize, - // 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 (or in general, inputStreamLengthHint < MIN_BUFFER_SIZE), use a buffer - // size of MIN_BUFFER_SIZE. - final int bufSize = inputStreamLengthHint == -1 ? scanSpec.maxBufferedJarRAMSize - : inputStreamLengthHint < MIN_BUFFER_SIZE ? MIN_BUFFER_SIZE : inputStreamLengthHint; - byte[] buf = new byte[bufSize]; - final int bufLength = buf.length; - - int bufBytesUsed = 0; - int bytesRead = 0; - while ((bytesRead = inputStream.read(buf, bufBytesUsed, bufLength - bufBytesUsed)) > 0) { - // Fill buffer until nothing more can be read - bufBytesUsed += bytesRead; - } - boolean spilledToDisk = false; - 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 - final byte[] overflowBuf = new byte[1]; - final int overflowBufBytesUsed = inputStream.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, - // and we need to spill to disk, because buf is full - spillToDisk(inputStream, tempFileBaseName, buf, overflowBuf, scanSpec, log); - spilledToDisk = true; - } - // else (overflowBufBytesUsed == -1), so reached the end of the stream => don't spill to disk - } - if (!spilledToDisk) { - // Successfully reached end of stream - if (bufBytesUsed < buf.length) { - // Trim array if needed (this is needed if inputStreamLengthHint was -1, or overestimated - // the length of the InputStream) - buf = Arrays.copyOf(buf, bufBytesUsed); - } - // Wrap array in a RAM-backed ByteBuffer - wrapByteBuffer(new ByteBufferWrapper(buf)); - } - - } else { - // inputStreamLengthHint is longer than scanSpec.maxJarRamSize, so immediately spill to disk - spillToDisk(inputStream, tempFileBaseName, /* buf = */ null, /* overflowBuf = */ null, scanSpec, log); - } - } - - /** - * Spill an {@link InputStream} to disk if the stream is too large to fit in RAM. - * - * @param inputStream - * The {@link InputStream}. - * @param tempFileBaseName - * 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. - * @param overflowBuf - * The second buffer to write to the beginning of the file, or null if none. (Should have same - * nullity as buf.) - * @param scanSpec - * The scan spec. - * @param log - * The log. - * @throws IOException - * If anything went wrong creating or writing to the temp file. - */ - private void spillToDisk(final InputStream inputStream, final String tempFileBaseName, final byte[] buf, - final byte[] overflowBuf, final ScanSpec scanSpec, final LogNode log) throws IOException { - // Create temp file - try { - this.mappedFile = nestedJarHandler.makeTempFile(tempFileBaseName, /* onlyUseLeafname = */ true); - } catch (final IOException e) { - if (log != null) { - log.log("Could not create temporary file: " + e); - } - throw e; - } - if (log != null) { - log.log("Could not fit InputStream content into max RAM buffer size, saving to temporary file: " - + tempFileBaseName + " -> " + this.mappedFile); - } - this.mappedFileIsTempFile = true; - - // Copy the rest of the InputStream to the end of the temporary file - try (OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(this.mappedFile))) { - // Write already-read buffered bytes to temp file, if anything was read - if (buf != null) { - outputStream.write(buf); - outputStream.write(overflowBuf); - } - // Copy the rest of the InputStream to the file - final byte[] copyBuf = new byte[8192]; - for (int bytesRead; (bytesRead = inputStream.read(copyBuf, 0, copyBuf.length)) > 0;) { - outputStream.write(copyBuf, 0, bytesRead); - } - } - - // Map the file to a MappedByteBuffer - mapFile(scanSpec.disableMemoryMapping, log); - } - - /** - * Map a {@link File} to a {@link MappedByteBuffer}. - * - * @param file - * the file - * @param nestedJarHandler - * the nested jar handler - * @param log - * the log - * @throws IOException - * If the contents could not be read. - */ - public MappedByteBufferResources(final File file, final NestedJarHandler nestedJarHandler, final LogNode log) - throws IOException { - this.nestedJarHandler = nestedJarHandler; - this.mappedFile = file; - // Map the file to a MappedByteBuffer - mapFile(nestedJarHandler.scanSpec.disableMemoryMapping, log); - } - - /** - * Wrap an existing ByteBuffer. - * - * @param byteBuffer - * the byte buffer - * @param nestedJarHandler - * the nested jar handler - * @throws IOException - * If the contents could not be read. - */ - public MappedByteBufferResources(final ByteBuffer byteBuffer, final NestedJarHandler nestedJarHandler) - throws IOException { - this.nestedJarHandler = nestedJarHandler; - // Wrap the existing byte buffer - wrapByteBuffer(new ByteBufferWrapper(byteBuffer)); - } - - /** - * Wrap an existing single-chunk {@link ByteBuffer}. - * - * @param byteBuffer - * the {@link ByteBuffer}. - */ - private void wrapByteBuffer(final ByteBufferWrapper byteBuffer) { - length.set(byteBuffer.remaining()); - // Put the ByteBuffer into the cache, so that the singleton map code for file mapping is never called - byteBufferChunksCached = new AtomicReferenceArray(1); - byteBufferChunksCached.set(0, byteBuffer); - // Don't set mappedFile, fileChannel or raf, they are unneeded. - } - - /** - * Map a {@link File} to a {@link MappedByteBuffer} or {@link RandomAccessFile}. - * - * @param disableMemoryMapping - * If true, use a {@link RandomAccessFile} rather than a {@link MappedByteBuffer} so that virtual - * memory space is not used when reading jarfiles. - * @param log - * the log. - * @throws IOException - * Signals that an I/O exception has occurred. - */ - private void mapFile(final boolean disableMemoryMapping, final LogNode log) throws IOException { - // If memory mapping is enabled, share one instance of RandomAccessFile and FileChannel across - // all memory-mapped chunks, to avoid opening a new file or channel for every new chunk mapping - if (!disableMemoryMapping) { - try { - raf = new RandomAccessFile(mappedFile, "r"); - fileChannel = raf.getChannel(); - } catch (final IOException e) { - close(log); - throw e; - } catch (final IllegalArgumentException | SecurityException e) { - close(log); - throw new IOException(e); - } - } - length.set(mappedFile.length()); - - // Implement an array of MappedByteBuffers to support jarfiles >2GB in size: - // https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6347833 - final int numByteBufferChunks = (int) ((length.get() + FileUtils.MAX_BUFFER_SIZE) - / FileUtils.MAX_BUFFER_SIZE); - byteBufferChunksCached = new AtomicReferenceArray(numByteBufferChunks); - chunkIdxToByteBufferSingletonMap = new SingletonMap() { - @Override - public ByteBufferWrapper newInstance(final Integer chunkIdxI, final LogNode log) throws IOException { - // Map the indexed 2GB chunk of the file to a MappedByteBuffer - final long pos = chunkIdxI.longValue() * FileUtils.MAX_BUFFER_SIZE; - final long chunkSize = Math.min(FileUtils.MAX_BUFFER_SIZE, length.get() - pos); - - ByteBufferWrapper byteBuffer = null; - if (!disableMemoryMapping) { - if (fileChannel == null) { - // Should not happen - throw new IOException("Cannot map a null FileChannel"); - } - - try { - // Try memory-mapping the file channel - byteBuffer = new ByteBufferWrapper(fileChannel, pos, chunkSize); - - } catch (final IOException e) { - // Should not happen, since if raf was opened, the file must exist. - // (TODO: I saw mention somewhere that on some operating systems, the JRE - // may throw IOException and not OutOfMemoryError if memory mapping runs - // out of virtual address space -- need to investigate this.) - MappedByteBufferResources.this.close(log); - throw e; - - } catch (final OutOfMemoryError e) { - if (log != null) { - log.log("Out of memory when trying to memory map file " + mappedFile); - } - // Failover to non-memory-mapped mode - // (fileChannel and raf will be closed when this MappedByteBufferResources object is closed) - } - } - - if (byteBuffer == null) { - // Memory mapping was disabled or failed -- use a RandomAccessFile to access the file instead - if (log != null) { - log.log("Memory mapping is disabled for file " + mappedFile - + " -- using slower RandomAccessFile method to open file instead"); - } - - // If memory mapping was disabled or failed, every duplicate of this ByteBufferWrapper - // needs to open its own RAF, for thread safety - byteBuffer = new ByteBufferWrapper(mappedFile, pos, chunkSize); - } - - // Record that the byte buffer has been mapped - nestedJarHandler.addMappedByteBuffer(byteBuffer); - - return byteBuffer; - } - }; - } - - /** - * Get a mmap'd chunk of the file, where chunkIdx denotes which 2GB chunk of the file to return (0 for the first - * 2GB of the file, or for files smaller than 2GB; 1 for the 2-4GB chunk, etc.). - * - * @param chunkIdx - * The index of the 2GB chunk to read, between 0 and {@link #numChunks()} - 1. - * @return The {@link MappedByteBuffer} for the requested file chunk, up to 2GB in size. - * @throws IOException - * If the chunk could not be mmap'd. - * @throws InterruptedException - * If the thread was interrupted. - */ - public ByteBufferWrapper getByteBuffer(final int chunkIdx) throws IOException, InterruptedException { - if (closed.get()) { - throw new IOException(getClass().getSimpleName() + " already closed"); - } - if (chunkIdx < 0 || chunkIdx >= numChunks()) { - throw new IOException("Chunk index out of range"); - } - // Fast path: only look up singleton map if mappedByteBuffersCached is null - ByteBufferWrapper cachedBuf = byteBufferChunksCached.get(chunkIdx); - if (cachedBuf == null) { - // This 2GB chunk has not yet been read -- mmap it and cache it. - // (Use a singleton map so that the mmap doesn't happen more than once) - if (chunkIdxToByteBufferSingletonMap == null) { - // Should not happen - throw new IOException("chunkIdxToByteBufferSingletonMap is null"); - } - try { - cachedBuf = chunkIdxToByteBufferSingletonMap.get(chunkIdx, /* log = */ null); - byteBufferChunksCached.set(chunkIdx, cachedBuf); - } catch (final NullSingletonException e) { - throw new IOException("Cannot get ByteBuffer chunk " + chunkIdx + " : " + e); - } - } - return cachedBuf; - } - - /** - * Get the mapped file (or null if an in-memory {@link ByteBuffer} was wrapped instead). - * - * @return the mapped file - */ - public File getMappedFile() { - return mappedFile; - } - - /** - * Get the length of the mapped file, or the initial remaining bytes in the wrapped ByteBuffer if a buffer was - * wrapped. - */ - public long length() { - return length.get(); - } - - /** - * Get the number of 2GB chunks that are available in this mapped file or wrapped ByteBuffer. - */ - public int numChunks() { - return byteBufferChunksCached == null ? 0 : byteBufferChunksCached.length(); - } - - /** - * Free resources. - * - * @param log - * the log - */ - public void close(final LogNode log) { - if (!closed.getAndSet(true)) { - if (chunkIdxToByteBufferSingletonMap != null) { - chunkIdxToByteBufferSingletonMap.clear(); - chunkIdxToByteBufferSingletonMap = null; - } - if (byteBufferChunksCached != null) { - // Only unmap bytebuffers if they came from a mapped file - if (mappedFile != null) { - for (int i = 0; i < byteBufferChunksCached.length(); i++) { - final ByteBufferWrapper mappedByteBuffer = byteBufferChunksCached.get(i); - if (mappedByteBuffer != null) { - nestedJarHandler.unmapByteBuffer(mappedByteBuffer, log); - byteBufferChunksCached.set(i, null); - } - } - } - byteBufferChunksCached = null; - } - if (fileChannel != null) { - try { - fileChannel.close(); - } catch (final IOException e) { - // Ignore - } - fileChannel = null; - } - if (raf != null) { - try { - raf.close(); - } catch (final IOException e) { - // Ignore - } - raf = null; - } - if (mappedFile != null) { - // If mapped file was a temp file, remove it - if (mappedFileIsTempFile) { - try { - nestedJarHandler.removeTempFile(mappedFile); - } catch (IOException | SecurityException e) { - if (log != null) { - log.log("Removing temporary file failed: " + mappedFile); - } - } - } - mappedFile = null; - } - } - } -} \ No newline at end of file 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 f56e1f52b..f03801c48 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java @@ -28,36 +28,43 @@ */ package nonapi.io.github.classgraph.fastzipfilereader; +import java.io.BufferedOutputStream; import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.io.RandomAccessFile; +import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; +import java.net.URLConnection; import java.nio.ByteBuffer; -import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; 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.zip.DataFormatException; import java.util.zip.Inflater; +import java.util.zip.InflaterInputStream; +import java.util.zip.ZipException; -import io.github.classgraph.ClassGraphException; import io.github.classgraph.ModuleReaderProxy; import io.github.classgraph.ModuleRef; import io.github.classgraph.ScanResult; import nonapi.io.github.classgraph.concurrency.InterruptionChecker; import nonapi.io.github.classgraph.concurrency.SingletonMap; -import nonapi.io.github.classgraph.json.ReferenceEqualityKey; +import nonapi.io.github.classgraph.fileslice.ArraySlice; +import nonapi.io.github.classgraph.fileslice.FileSlice; +import nonapi.io.github.classgraph.fileslice.Slice; import nonapi.io.github.classgraph.recycler.Recycler; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.FastPathResolver; @@ -68,7 +75,7 @@ /** Open and read jarfiles, which may be nested within other jarfiles. */ public class NestedJarHandler { /** The {@link ScanSpec}. */ - final ScanSpec scanSpec; + public final ScanSpec scanSpec; /** * A singleton map from a zipfile's {@link File} to the {@link PhysicalZipFile} for that file, used to ensure @@ -78,23 +85,10 @@ public class NestedJarHandler { canonicalFileToPhysicalZipFileMap = new SingletonMap() { @Override public PhysicalZipFile newInstance(final File canonicalFile, final LogNode log) throws IOException { - if (closed.get()) { - throw ClassGraphException - .newClassGraphException(NestedJarHandler.class.getSimpleName() + " already closed"); - } - final PhysicalZipFile physicalZipFile = new PhysicalZipFile(canonicalFile, NestedJarHandler.this, log); - allocatedPhysicalZipFiles.add(physicalZipFile); - - return physicalZipFile; + return new PhysicalZipFile(canonicalFile, NestedJarHandler.this, log); } }; - /** The allocated {@link PhysicalZipFile} instances. */ - private Queue allocatedPhysicalZipFiles = new ConcurrentLinkedQueue<>(); - - /** The allocated {@link LogicalZipFile} instances. */ - private final Queue allocatedLogicalZipFiles = new ConcurrentLinkedQueue<>(); - /** * 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 @@ -107,9 +101,8 @@ public ZipFileSlice newInstance(final FastZipEntry childZipEntry, final LogNode throws IOException, InterruptedException { ZipFileSlice childZipEntrySlice; if (!childZipEntry.isDeflated) { - // Wrap the child entry (a stored nested zipfile) in a new ZipFileSlice -- there is - // nothing else to do. (Most nested zipfiles are stored, not deflated, so this fast - // path will be followed most often.) + // 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); } else { @@ -122,13 +115,12 @@ public ZipFileSlice newInstance(final FastZipEntry childZipEntry, final LogNode } // 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.open(), - childZipEntry.uncompressedSize > 0L - && childZipEntry.uncompressedSize < FileUtils.MAX_BUFFER_SIZE + final PhysicalZipFile physicalZipFile = new PhysicalZipFile(childZipEntry.getSlice().open(), + childZipEntry.uncompressedSize >= 0L + && childZipEntry.uncompressedSize <= FileUtils.MAX_BUFFER_SIZE ? (int) childZipEntry.uncompressedSize : -1, childZipEntry.entryName, NestedJarHandler.this, log); - allocatedPhysicalZipFiles.add(physicalZipFile); // Create a new logical slice of the extracted inner zipfile childZipEntrySlice = new ZipFileSlice(physicalZipFile, childZipEntry); @@ -143,14 +135,8 @@ public ZipFileSlice newInstance(final FastZipEntry childZipEntry, final LogNode @Override public LogicalZipFile newInstance(final ZipFileSlice zipFileSlice, final LogNode log) throws IOException, InterruptedException { - if (closed.get()) { - throw ClassGraphException - .newClassGraphException(NestedJarHandler.class.getSimpleName() + " already closed"); - } - // Read the central directory for the logical zipfile slice - final LogicalZipFile logicalZipFile = new LogicalZipFile(zipFileSlice, log); - allocatedLogicalZipFiles.add(logicalZipFile); - return logicalZipFile; + // Read the central directory for the zipfile + return new LogicalZipFile(zipFileSlice, log); } }; @@ -164,10 +150,6 @@ public LogicalZipFile newInstance(final ZipFileSlice zipFileSlice, final LogNode @Override public Entry newInstance(final String nestedJarPathRaw, final LogNode log) throws IOException, InterruptedException { - if (closed.get()) { - throw ClassGraphException - .newClassGraphException(NestedJarHandler.class.getSimpleName() + " already closed"); - } final String nestedJarPath = FastPathResolver.resolve(nestedJarPathRaw); final int lastPlingIdx = nestedJarPath.lastIndexOf('!'); if (lastPlingIdx < 0) { @@ -259,7 +241,13 @@ public Entry newInstance(final String nestedJarPathRaw, 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 - // ending with a slash when reading the central directory of a zipfile) + // 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 + // 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). for (final FastZipEntry entry : parentLogicalZipFile.entries) { if (entry.entryName.equals(childPath)) { childZipEntry = entry; @@ -352,10 +340,6 @@ public Recycler newInstance(final ModuleRef modu return new Recycler() { @Override public ModuleReaderProxy newInstance() throws IOException { - if (closed.get()) { - throw ClassGraphException.newClassGraphException( - NestedJarHandler.class.getSimpleName() + " already closed"); - } return moduleRef.open(); } }; @@ -363,28 +347,17 @@ public ModuleReaderProxy newInstance() throws IOException { }; /** A recycler for {@link Inflater} instances. */ - Recycler // + private Recycler // inflaterRecycler = new Recycler() { @Override public RecyclableInflater newInstance() throws RuntimeException { - if (closed.get()) { - throw ClassGraphException - .newClassGraphException(NestedJarHandler.class.getSimpleName() + " already closed"); - } return new RecyclableInflater(); } }; - /** - * {@link MappedByteBuffer} instances that are currently mapped. (Use {@link ReferenceEqualityKey} so that the - * entire contents of the buffers are not compared by {@link ByteBuffer#equals(Object)}). - */ - private Set> mappedByteBuffers = Collections - .newSetFromMap(new ConcurrentHashMap, Boolean>()); - - /** {@link MappedByteBufferResources} instances that were allocated for downloading jars from URLs. */ - private Set mappedByteBufferResources = Collections - .newSetFromMap(new ConcurrentHashMap()); + /** {@link RandomAccessFile} instances that are currently open (typically one per classpath element). */ + private Set openFiles = Collections + .newSetFromMap(new ConcurrentHashMap()); /** Any temporary files created while scanning. */ private Set tempFiles = Collections.newSetFromMap(new ConcurrentHashMap()); @@ -398,6 +371,12 @@ public RecyclableInflater newInstance() throws RuntimeException { /** The interruption checker. */ public InterruptionChecker interruptionChecker; + /** The default size of a file buffer. */ + private static final int DEFAULT_BUFFER_SIZE = 16384; + + /** The maximum initial buffer size. */ + private static final int MAX_INITIAL_BUFFER_SIZE = 16 * 1024 * 1024; + // ------------------------------------------------------------------------------------------------------------- /** @@ -415,32 +394,6 @@ public NestedJarHandler(final ScanSpec scanSpec, final InterruptionChecker inter // ------------------------------------------------------------------------------------------------------------- - /** - * Record that a {@link FileChannel} was mapped to a {@link MappedByteBuffer}. - * - * @param byteBuffer - * the byte buffer - */ - public void addMappedByteBuffer(final ByteBufferWrapper byteBuffer) { - mappedByteBuffers.add(new ReferenceEqualityKey(byteBuffer)); - } - - /** - * Unmap a possibly previously-mapped {@link ByteBuffer} (wrapped in a {@link ByteBufferWrapper}). - * - * @param byteBuffer - * the {@link ByteBufferWrapper}. - * @param log - * the log. - */ - public void unmapByteBuffer(final ByteBufferWrapper byteBuffer, final LogNode log) { - if (mappedByteBuffers.remove(new ReferenceEqualityKey(byteBuffer))) { - byteBuffer.close(log); - } - } - - // ------------------------------------------------------------------------------------------------------------- - /** * Get the leafname of a path. * @@ -475,7 +428,7 @@ private String sanitizeFilename(final String filename) { * @throws IOException * If the temporary file could not be created. */ - File makeTempFile(final String filePathBase, final boolean onlyUseLeafname) throws IOException { + public File makeTempFile(final String filePathBase, final boolean onlyUseLeafname) throws IOException { final File tempFile = File.createTempFile("ClassGraph--", TEMP_FILENAME_LEAF_SEPARATOR + sanitizeFilename(onlyUseLeafname ? leafname(filePathBase) : filePathBase)); tempFile.deleteOnExit(); @@ -505,6 +458,38 @@ void removeTempFile(final File tempFile) throws IOException, SecurityException { } } + /** + * Open a file as a {@link RandomAccessFile}. + * + * @param file + * the file to open. + */ + public RandomAccessFile openFile(final File file) throws IOException { + try { + final RandomAccessFile raf = new RandomAccessFile(file, "r"); + openFiles.add(raf); + return raf; + } catch (final SecurityException e) { + throw new IOException("Could not open file " + file + " : " + e.getMessage()); + } + } + + /** + * Close an open {@link RandomAccessFile}, and remove it from the list of files to close when + * {@link #close(LogNode)} is called. + * + * @param raf + * the {@link RandomAccessFile} to close. + */ + public void closeOpenFile(final RandomAccessFile raf) { + openFiles.remove(raf); + try { + raf.close(); + } catch (final IOException e) { + // Ignore + } + } + /** * 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. @@ -534,22 +519,377 @@ private PhysicalZipFile downloadJarFromURL(final String jarURL, final LogNode lo throw new IOException("Could not parse URL: " + jarURL); } } - try (InputStream inputStream = url.openStream()) { - // 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, /* length unknown */ -1, - jarURL, this, log); - allocatedPhysicalZipFiles.add(physicalZipFile); - return physicalZipFile; - - } catch (final MalformedURLException e) { - throw new IOException("Malformed URL: " + jarURL); + + 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(); + contentLengthHint = httpConn.getContentLengthLong(); + if (contentLengthHint < -1L) { + contentLengthHint = -1L; + } + } 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 + } + } + + // Fetch content from URL + 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, log); + if (log != null) { + log.addElapsedTime(); + log.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); + } } finally { - if (log != null) { - log.addElapsedTime(); - log.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 *****"); + if (httpConn != null) { + httpConn.disconnect(); + } + } + } + + // ------------------------------------------------------------------------------------------------------------- + + /** Wrap an {@link InputStream} with an {@link InflaterInputStream}, recycling the {@link Inflater} instance. */ + public InputStream openInflaterInputStream(final InputStream rawInputStream) throws IOException { + return new InputStream() { + // Gen Inflater instance with nowrap set to true (needed by zip entries) + 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; + + @Override + public int read() throws IOException { + if (closed.get()) { + throw new IOException("Already closed"); + } else if (inflater.finished()) { + return -1; + } + final int numDeflatedBytesRead = read(buf, 0, 1); + if (numDeflatedBytesRead < 0) { + return -1; + } else { + return buf[0] & 0xff; + } + } + + @Override + public int read(final byte outBuf[], final int off, final int len) throws IOException { + if (closed.get()) { + throw new IOException("Already closed"); + } else if (len < 0) { + throw new IllegalArgumentException("len cannot be negative"); + } else if (len == 0) { + return 0; + } + try { + // Keep fetching data from rawInputStream until + int totInflatedBytes = 0; + while (!inflater.finished() && totInflatedBytes < len) { + final int numInflatedBytes = inflater.inflate(outBuf, off + totInflatedBytes, + len - totInflatedBytes); + if (numInflatedBytes == 0) { + if (inflater.needsDictionary()) { + // Should not happen for jarfiles + throw new IOException("Inflater needs preset dictionary"); + } else if (inflater.needsInput()) { + // Read a chunk of data from the raw InputStream + final int numRawBytesRead = rawInputStream.read(buf, 0, buf.length); + if (numRawBytesRead == -1) { + // An extra dummy byte is needed at the end of the input stream when + // using the "nowrap" Inflater option. + // See: ZipFile.ZipFileInflaterInputStream.fill() + buf[0] = (byte) 0; + inflater.setInput(buf, 0, 1); + } else { + // Deflate the chunk of data + inflater.setInput(buf, 0, numRawBytesRead); + } + } + } else { + totInflatedBytes += numInflatedBytes; + } + } + if (totInflatedBytes == 0) { + // If no bytes were inflated, return -1 as required by read() API contract + return -1; + } + return totInflatedBytes; + + } catch (final DataFormatException e) { + throw new ZipException( + e.getMessage() != null ? e.getMessage() : "Invalid deflated zip entry data"); + } + } + + @Override + public long skip(final long numToSkip) throws IOException { + if (closed.get()) { + throw new IOException("Already closed"); + } else if (numToSkip < 0) { + throw new IllegalArgumentException("numToSkip cannot be negative"); + } else if (numToSkip == 0) { + return 0; + } else if (inflater.finished()) { + return -1; + } + long totBytesSkipped = 0L; + for (;;) { + final int readLen = (int) Math.min(numToSkip - totBytesSkipped, buf.length); + final int numBytesRead = read(buf, 0, readLen); + if (numBytesRead > 0) { + totBytesSkipped -= numBytesRead; + } else { + break; + } + } + return totBytesSkipped; + } + + @Override + public int available() throws IOException { + if (closed.get()) { + 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 + // relies on this and ends up reading just one byte at a time. + return inflater.finished() ? 0 : 1; + } + + @Override + public void mark(final int readlimit) { + throw new IllegalArgumentException("Not supported"); + } + + @Override + public void reset() throws IOException { + throw new IllegalArgumentException("Not supported"); + } + + @Override + public boolean markSupported() { + return false; + } + + @Override + public void close() { + if (!closed.getAndSet(true)) { + try { + rawInputStream.close(); + } catch (final IOException e) { + // Ignore + } finally { + // Reset and recycle inflater instance + inflaterRecycler.recycle(recyclableInflater); + } + } + } + }; + } + + /** + * 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. + * @param tempFileBaseName + * 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. + * @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 + * returned. + * + * @throws IOException + * 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 + // wrong but the file is still small. + final int bufSize = inputStreamLengthHint == -1L ? scanSpec.maxBufferedJarRAMSize + : inputStreamLengthHint == 0L ? 16384 + : Math.min((int) inputStreamLengthHint, scanSpec.maxBufferedJarRAMSize); + byte[] buf = new byte[bufSize]; + final int bufLength = buf.length; + + int bufBytesUsed = 0; + int bytesRead = 0; + while ((bytesRead = inptStream.read(buf, bufBytesUsed, bufLength - bufBytesUsed)) > 0) { + // Fill buffer until nothing more can be read + 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 + 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, + // 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 + } + // Successfully reached end of stream + if (bufBytesUsed < buf.length) { + // Trim array if needed (this is needed if inputStreamLengthHint was -1, or overestimated + // the length of the InputStream) + buf = Arrays.copyOf(buf, bufBytesUsed); + } + // Return buf as new ArraySlice + return new ArraySlice(buf, /* isDeflatedZipEntry = */ false, /* inflatedSizeHint = */ + 0L, this); + } + // 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. + * + * @param inputStream + * The {@link InputStream}. + * @param tempFileBaseName + * 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. + * @param overflowBuf + * 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. + * @throws IOException + * 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 { + // Create temp file + File tempFile; + try { + tempFile = makeTempFile(tempFileBaseName, /* onlyUseLeafname = */ true); + } catch (final IOException e) { + throw new IOException("Could not create temporary file: " + e.getMessage()); + } + if (log != null) { + log.log("Could not fit InputStream content into max RAM buffer size, saving to temporary file: " + + tempFileBaseName + " -> " + tempFile); + } + + // 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) { + outputStream.write(buf); + outputStream.write(overflowBuf); + } + // Copy the rest of the InputStream to the file + final byte[] copyBuf = new byte[8192]; + for (int bytesRead; (bytesRead = inputStream.read(copyBuf, 0, copyBuf.length)) > 0;) { + outputStream.write(copyBuf, 0, bytesRead); + } + } + + // Return a new FileSlice for the temporary file + return new FileSlice(tempFile, this); + } + + /** + * Read all the bytes in an {@link InputStream}. + * + * @param inputStream + * The {@link InputStream}. + * @param uncompressedLengthHint + * 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. + */ + public static byte[] readAllBytesAsArray(final InputStream inputStream, final long uncompressedLengthHint) + throws IOException { + try (InputStream inptStream = inputStream) { + if (uncompressedLengthHint > FileUtils.MAX_BUFFER_SIZE) { + throw new IOException("InputStream is too large to read"); + } + final int bufferSize = uncompressedLengthHint < 1L + // 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 + // lengths do not become a memory allocation attack vector + : Math.min((int) uncompressedLengthHint, MAX_INITIAL_BUFFER_SIZE); + byte[] buf = new byte[bufferSize]; + int totBytesRead = 0; + for (int bytesRead;;) { + while ((bytesRead = inptStream.read(buf, totBytesRead, buf.length - totBytesRead)) > 0) { + // Fill buffer until nothing more can be read + totBytesRead += bytesRead; + } + if (bytesRead < 0) { + // Reached end of stream without filling buf + break; + } + + // Reached end of stream, and buf is full + final int extraByte; + try { + // 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. + extraByte = inptStream.read(); + if (extraByte == -1) { + // Reached end of stream + break; + } + } catch (final ZipException e) { + // FIXME temp + throw new RuntimeException(e); + } + + // 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"); + } + buf = Arrays.copyOf(buf, (int) Math.min(buf.length * 2L, FileUtils.MAX_BUFFER_SIZE)); + buf[totBytesRead++] = (byte) extraByte; + } + // Return buffer and number of bytes read + return totBytesRead == buf.length ? buf : Arrays.copyOf(buf, totBytesRead); } } @@ -564,10 +904,6 @@ private PhysicalZipFile downloadJarFromURL(final String jarURL, final LogNode lo public void close(final LogNode log) { if (!closed.getAndSet(true)) { boolean interrupted = false; - if (inflaterRecycler != null) { - inflaterRecycler.forceClose(); - inflaterRecycler = null; - } if (moduleRefToModuleReaderProxyRecyclerMap != null) { boolean completedWithoutInterruption = false; while (!completedWithoutInterruption) { @@ -593,54 +929,31 @@ public void close(final LogNode log) { nestedPathToLogicalZipFileAndPackageRootMap.clear(); nestedPathToLogicalZipFileAndPackageRootMap = null; } - for (LogicalZipFile logicalZipFile; (logicalZipFile = allocatedLogicalZipFiles.poll()) != null;) { - logicalZipFile.close(); - } if (canonicalFileToPhysicalZipFileMap != null) { - while (!canonicalFileToPhysicalZipFileMap.isEmpty()) { - try { - for (final Entry ent : new ArrayList<>( - canonicalFileToPhysicalZipFileMap.entries())) { - final PhysicalZipFile physicalZipFile = ent.getValue(); - physicalZipFile.close(); - canonicalFileToPhysicalZipFileMap.remove(ent.getKey()); - } - } catch (final InterruptedException e) { - // If thread was interrupted, canonicalFileToPhysicalZipFileMap.entries() is interrupted - // above, so canonicalFileToPhysicalZipFileMap.remove(ent.getKey()) is never called, - // which causes the while loop to loop forever if we re-interrupt here (#400). Therefore - // delay re-interruption until the end of this method. - interrupted = false; - } - } + canonicalFileToPhysicalZipFileMap.clear(); canonicalFileToPhysicalZipFileMap = null; } - if (allocatedPhysicalZipFiles != null) { - for (PhysicalZipFile physicalZipFile; (physicalZipFile = allocatedPhysicalZipFiles - .poll()) != null;) { - physicalZipFile.close(); - } - allocatedPhysicalZipFiles.clear(); - allocatedPhysicalZipFiles = null; - } if (fastZipEntryToZipFileSliceMap != null) { fastZipEntryToZipFileSliceMap.clear(); fastZipEntryToZipFileSliceMap = null; } - if (mappedByteBufferResources != null) { - for (final MappedByteBufferResources bufRes : mappedByteBufferResources) { - bufRes.close(log); - } - mappedByteBufferResources = null; - } - if (mappedByteBuffers != null) { - while (!mappedByteBuffers.isEmpty()) { - for (final ReferenceEqualityKey byteBufferRef : new ArrayList<>( - mappedByteBuffers)) { - unmapByteBuffer(byteBufferRef.get(), log); + if (openFiles != null) { + while (!openFiles.isEmpty()) { + for (final RandomAccessFile openFile : new ArrayList<>(openFiles)) { + try { + openFile.close(); + } catch (final IOException e) { + // Ignore + } + openFiles.remove(openFile); } } - mappedByteBuffers = null; + openFiles.clear(); + openFiles = null; + } + if (inflaterRecycler != null) { + inflaterRecycler.forceClose(); + inflaterRecycler = null; } // Temp files have to be deleted last, after all PhysicalZipFiles are closed and files are unmapped if (tempFiles != null) { 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 d64548024..c1ee0d028 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/PhysicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/PhysicalZipFile.java @@ -28,39 +28,35 @@ */ package nonapi.io.github.classgraph.fastzipfilereader; -import java.io.Closeable; import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.nio.ByteBuffer; -import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.util.Objects; -import java.util.concurrent.atomic.AtomicBoolean; +import nonapi.io.github.classgraph.fileslice.ArraySlice; +import nonapi.io.github.classgraph.fileslice.FileSlice; +import nonapi.io.github.classgraph.fileslice.Slice; import nonapi.io.github.classgraph.utils.FastPathResolver; import nonapi.io.github.classgraph.utils.FileUtils; import nonapi.io.github.classgraph.utils.LogNode; /** A physical zipfile, which is mmap'd using a {@link FileChannel}. */ -class PhysicalZipFile implements Closeable { +class PhysicalZipFile { /** The {@link File} backing this {@link PhysicalZipFile}, if any. */ private final File file; /** The path to the zipfile. */ private final String path; - /** The byte buffer resources. */ - private MappedByteBufferResources byteBufferResources; + /** The {@link Slice} for the zipfile. */ + Slice slice; /** The nested jar handler. */ NestedJarHandler nestedJarHandler; - /** True if the zipfile was deflated to RAM, rather than mapped from disk. */ - boolean isDeflatedToRam; - - /** Set to true once {@link #close()} has been called. */ - private final AtomicBoolean closed = new AtomicBoolean(false); + /** The cached hashCode. */ + private int hashCode; /** * Construct a {@link PhysicalZipFile} from a file on disk. @@ -82,16 +78,14 @@ class PhysicalZipFile implements Closeable { this.file = file; this.nestedJarHandler = nestedJarHandler; this.path = FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, file.getPath()); - - // Map the file to a ByteBuffer - this.byteBufferResources = new MappedByteBufferResources(file, nestedJarHandler, log); + this.slice = new FileSlice(file, nestedJarHandler); } /** - * Construct a {@link PhysicalZipFile} from a ByteBuffer in memory. + * Construct a {@link PhysicalZipFile} from a byte array. * - * @param byteBuffer - * the byte buffer + * @param arr + * the array containing the zipfile. * @param outermostFile * the outermost file * @param path @@ -101,23 +95,18 @@ class PhysicalZipFile implements Closeable { * @throws IOException * if an I/O exception occurs. */ - PhysicalZipFile(final ByteBuffer byteBuffer, final File outermostFile, final String path, + PhysicalZipFile(final byte[] arr, final File outermostFile, final String path, final NestedJarHandler nestedJarHandler) throws IOException { this.file = outermostFile; this.path = path; this.nestedJarHandler = nestedJarHandler; - this.isDeflatedToRam = true; - - // Wrap the ByteBuffer - this.byteBufferResources = new MappedByteBufferResources(byteBuffer, nestedJarHandler); - if (this.byteBufferResources.length() == 0L) { - throw new IOException("Zipfile is empty: " + path); - } + this.slice = new ArraySlice(arr, /* isDeflatedZipEntry = */ false, /* inflatedSizeHint = */ 0L, + nestedJarHandler); } /** - * Construct a {@link PhysicalZipFile} from an InputStream, which is downloaded to a {@link ByteBuffer} in RAM, - * or spilled to disk if the content of the InputStream is too large. + * Construct a {@link PhysicalZipFile} by reading from the {@link InputStream} to an array in RAM, or spill to + * disk if the {@link InputStream} is too long. * * @param inputStream * the input stream @@ -133,36 +122,15 @@ class PhysicalZipFile implements Closeable { * @throws IOException * if an I/O exception occurs. */ - PhysicalZipFile(final InputStream inputStream, final int inputStreamLengthHint, final String path, - final NestedJarHandler nestedJarHandler, final LogNode log) - throws IOException { + PhysicalZipFile(final InputStream inputStream, final long inputStreamLengthHint, final String path, + final NestedJarHandler nestedJarHandler, final LogNode log) throws IOException { this.nestedJarHandler = nestedJarHandler; this.path = path; - this.isDeflatedToRam = true; - - // Wrap the ByteBuffer - this.byteBufferResources = new MappedByteBufferResources(inputStream, inputStreamLengthHint, path, - nestedJarHandler, log); - if (this.byteBufferResources.length() == 0L) { - throw new IOException("Zipfile is empty: " + path); - } - this.file = byteBufferResources.getMappedFile(); - } - - /** - * Get a chunk of the file, where chunkIdx denotes which 2GB chunk of the file to return (0 for the first 2GB of - * the file, or for files smaller than 2GB; 1 for the 2-4GB chunk, etc.). - * - * @param chunkIdx - * The index of the 2GB chunk to read - * @return The {@link MappedByteBuffer} for the requested file chunk, up to 2GB in size. - * @throws IOException - * If the chunk could not be mmap'd. - * @throws InterruptedException - * If the thread was interrupted. - */ - ByteBufferWrapper getByteBuffer(final int chunkIdx) throws IOException, InterruptedException { - return byteBufferResources.getByteBuffer(chunkIdx); + // Try downloading the InputStream to a byte array. If this succeeds, this will result in an ArraySlice. + // If it fails, the InputStream will be spilled to disk, resulting in a FileSlice. + this.slice = nestedJarHandler.readAllBytesWithSpilloverToDisk(inputStream, /* tempFileBaseName = */ path, + inputStreamLengthHint, log); + this.file = this.slice instanceof FileSlice ? ((FileSlice) this.slice).file : null; } /** @@ -191,22 +159,7 @@ public String getPath() { * wrapped. */ public long length() { - return byteBufferResources.length(); - } - - /** - * Get the number of 2GB chunks that are available in this PhysicalZipFile. - */ - public int numChunks() { - return byteBufferResources.numChunks(); - } - - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return path; + return slice.sliceLength; } /* (non-Javadoc) @@ -214,33 +167,34 @@ public String toString() { */ @Override public int hashCode() { - return file == null ? 0 : file.hashCode(); + if (hashCode == 0) { + hashCode = (file == null ? 0 : file.hashCode()); + if (hashCode == 0) { + hashCode = 1; + } + } + return hashCode; } /* (non-Javadoc) * @see java.lang.Object#equals(java.lang.Object) */ @Override - public boolean equals(final Object obj) { - if (obj == this) { + public boolean equals(final Object o) { + if (o == this) { return true; - } else if (!(obj instanceof PhysicalZipFile)) { + } else if (!(o instanceof PhysicalZipFile)) { return false; } - return Objects.equals(file, ((PhysicalZipFile) obj).file); + final PhysicalZipFile other = (PhysicalZipFile) o; + return Objects.equals(file, other.file); } /* (non-Javadoc) - * @see java.io.Closeable#close() + * @see java.lang.Object#toString() */ @Override - public void close() { - if (!closed.getAndSet(true)) { - if (byteBufferResources != null) { - byteBufferResources.close(/* log = */ null); - } - byteBufferResources = null; - nestedJarHandler = null; - } + public String toString() { + return path; } } \ No newline at end of file diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/RecyclableInflater.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/RecyclableInflater.java index 4635f4bf4..b56b995fd 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/RecyclableInflater.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/RecyclableInflater.java @@ -37,7 +37,7 @@ * Wrapper class that allows an {@link Inflater} instance to be reset for reuse and then recycled by a * {@link Recycler}. */ -class RecyclableInflater implements Resettable, AutoCloseable { +public class RecyclableInflater implements Resettable, AutoCloseable { /** Create a new {@link Inflater} instance with the "nowrap" option (which is needed for zipfile entries). */ private final Inflater inflater = new Inflater(/* nowrap = */ true); diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSlice.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSlice.java index c223f0227..d533a19f3 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSlice.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSlice.java @@ -30,8 +30,9 @@ import java.io.File; import java.io.IOException; +import java.util.Objects; -import nonapi.io.github.classgraph.recycler.Recycler; +import nonapi.io.github.classgraph.fileslice.Slice; import nonapi.io.github.classgraph.scanspec.WhiteBlackList.WhiteBlackListLeafname; /** A zipfile slice (a sub-range of bytes within a PhysicalZipFile. */ @@ -40,32 +41,10 @@ public class ZipFileSlice { private final ZipFileSlice parentZipFileSlice; /** The underlying physical zipfile. */ public final PhysicalZipFile physicalZipFile; - /** The start offset of the slice within the physical zipfile. */ - final long startOffsetWithinPhysicalZipFile; - /** The compressed or stored size of the zipfile slice or entry. */ - final long len; /** For the toplevel zipfile slice, the zipfile path; For nested slices, the name/path of the zipfile entry. */ private final String pathWithinParentZipFileSlice; - /** A {@link Recycler} for {@link ZipFileSliceReader} instances. */ - final Recycler zipFileSliceReaderRecycler; - // N.B. if any fields are added, make sure the clone constructor below is updated - - /** - * Create a new {@link Recycler} for {@link ZipFileSliceReader} instances. - * - * @return A new {@link Recycler} for {@link ZipFileSliceReader} instances. - */ - private Recycler newZipFileSliceReaderRecycler() { - return new Recycler() { - /* (non-Javadoc) - * @see nonapi.io.github.classgraph.concurrency.LazyReference#newInstance() - */ - @Override - public ZipFileSliceReader newInstance() throws RuntimeException { - return new ZipFileSliceReader(ZipFileSlice.this); - } - }; - } + /** The {@link Slice} containing the zipfile. */ + public Slice slice; /** * Create a ZipFileSlice that wraps a toplevel {@link PhysicalZipFile}. @@ -76,14 +55,13 @@ public ZipFileSliceReader newInstance() throws RuntimeException { ZipFileSlice(final PhysicalZipFile physicalZipFile) { this.parentZipFileSlice = null; this.physicalZipFile = physicalZipFile; - this.startOffsetWithinPhysicalZipFile = 0; - this.len = physicalZipFile.length(); + this.slice = physicalZipFile.slice; this.pathWithinParentZipFileSlice = physicalZipFile.getPath(); - this.zipFileSliceReaderRecycler = newZipFileSliceReaderRecycler(); } /** - * Create a ZipFileSlice that wraps a {@link PhysicalZipFile} extracted to a ByteBuffer in memory. + * Create a ZipFileSlice that wraps a {@link PhysicalZipFile} that was extracted or inflated from a nested jar + * to memory or disk. * * @param physicalZipFile * a physical zipfile that has been extracted to RAM @@ -93,14 +71,12 @@ public ZipFileSliceReader newInstance() throws RuntimeException { ZipFileSlice(final PhysicalZipFile physicalZipFile, final FastZipEntry zipEntry) { this.parentZipFileSlice = zipEntry.parentLogicalZipFile; this.physicalZipFile = physicalZipFile; - this.startOffsetWithinPhysicalZipFile = 0; - this.len = physicalZipFile.length(); + this.slice = physicalZipFile.slice; this.pathWithinParentZipFileSlice = zipEntry.entryName; - this.zipFileSliceReaderRecycler = newZipFileSliceReaderRecycler(); } /** - * Create a ZipFileSlice that wraps a single {@link FastZipEntry}. + * Create a ZipFileSlice that wraps a single stored (not deflated) {@link FastZipEntry}. * * @param zipEntry * the zip entry @@ -112,10 +88,8 @@ public ZipFileSliceReader newInstance() throws RuntimeException { ZipFileSlice(final FastZipEntry zipEntry) throws IOException, InterruptedException { this.parentZipFileSlice = zipEntry.parentLogicalZipFile; this.physicalZipFile = zipEntry.parentLogicalZipFile.physicalZipFile; - this.startOffsetWithinPhysicalZipFile = zipEntry.getEntryDataStartOffsetWithinPhysicalZipFile(); - this.len = zipEntry.compressedSize; + this.slice = zipEntry.getSlice(); this.pathWithinParentZipFileSlice = zipEntry.entryName; - this.zipFileSliceReaderRecycler = newZipFileSliceReaderRecycler(); } /** @@ -127,11 +101,8 @@ public ZipFileSliceReader newInstance() throws RuntimeException { ZipFileSlice(final ZipFileSlice other) { this.parentZipFileSlice = other.parentZipFileSlice; this.physicalZipFile = other.physicalZipFile; - this.startOffsetWithinPhysicalZipFile = other.startOffsetWithinPhysicalZipFile; - this.len = other.len; + this.slice = other.slice; this.pathWithinParentZipFileSlice = other.pathWithinParentZipFileSlice; - // Reuse the recycler for clones - this.zipFileSliceReaderRecycler = other.zipFileSliceReaderRecycler; } /** @@ -207,26 +178,27 @@ public File getPhysicalFile() { } /* (non-Javadoc) - * @see java.lang.Object#hashCode() + * @see nonapi.io.github.classgraph.fastzipfilereader.ZipFileSlice#equals(java.lang.Object) */ @Override - public int hashCode() { - return physicalZipFile.getPath().hashCode() ^ (int) startOffsetWithinPhysicalZipFile ^ (int) len; + public boolean equals(final Object o) { + if (o == this) { + return true; + } else if (!(o instanceof ZipFileSlice)) { + return false; + } else { + final ZipFileSlice other = (ZipFileSlice) o; + return Objects.equals(physicalZipFile, other.physicalZipFile) && Objects.equals(slice, other.slice) + && Objects.equals(pathWithinParentZipFileSlice, other.pathWithinParentZipFileSlice); + } } /* (non-Javadoc) - * @see java.lang.Object#equals(java.lang.Object) + * @see nonapi.io.github.classgraph.fastzipfilereader.ZipFileSlice#hashCode() */ @Override - public boolean equals(final Object obj) { - if (obj == this) { - return true; - } else if (!(obj instanceof ZipFileSlice)) { - return false; - } - final ZipFileSlice other = (ZipFileSlice) obj; - return startOffsetWithinPhysicalZipFile == other.startOffsetWithinPhysicalZipFile && len == other.len - && this.physicalZipFile.equals(other.physicalZipFile); + public int hashCode() { + return Objects.hash(physicalZipFile, slice, pathWithinParentZipFileSlice); } /* (non-Javadoc) @@ -234,11 +206,10 @@ public boolean equals(final Object obj) { */ @Override public String toString() { - return "[" - + (physicalZipFile.isDeflatedToRam ? "ByteBuffer deflated to RAM from " + getPath() - : physicalZipFile.getFile() == null ? "ByteBuffer downloaded to RAM from " + getPath() - : physicalZipFile.getFile()) - + " ; byte range: " + startOffsetWithinPhysicalZipFile + ".." - + (startOffsetWithinPhysicalZipFile + len) + " / " + physicalZipFile.length() + "]"; + final String path = getPath(); + final String fileStr = physicalZipFile.getFile() == null ? null : physicalZipFile.getFile().toString(); + return "[" + (fileStr == null || !fileStr.equals(path) ? path + " -> " + fileStr : path) + " ; byte range: " + + slice.sliceStartPos + ".." + (slice.sliceStartPos + slice.sliceLength) + " / " + + physicalZipFile.length() + "]"; } } \ No newline at end of file diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSliceReader.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSliceReader.java deleted file mode 100644 index a5fb69ee3..000000000 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSliceReader.java +++ /dev/null @@ -1,411 +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.fastzipfilereader; - -import java.io.EOFException; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; - -import nonapi.io.github.classgraph.utils.FileUtils; - -/** - * A class for reading from a ZipFileSlice. - */ -class ZipFileSliceReader implements AutoCloseable { - /** The zipfile slice. */ - private final ZipFileSlice zipFileSlice; - - /** - * The chunk cache, for duplicates of the ByteBuffers in the ZipFileSlice, so that different threads can work on - * the same MappedByteBuffers without interfering with each other's buffer position. - */ - private final ByteBufferWrapper[] chunkCache; - - /** A scratch buffer. */ - private final byte[] scratch = new byte[256]; - - /** - * Constructor. - * - * @param zipFileSlice - * the zipfile slice - */ - public ZipFileSliceReader(final ZipFileSlice zipFileSlice) { - this.zipFileSlice = zipFileSlice; - this.chunkCache = new ByteBufferWrapper[zipFileSlice.physicalZipFile.numChunks()]; - } - - /** - * Get the 2GB chunk of the zipfile with the given chunk index. - * - * @param chunkIdx - * the chunk index - * @return the chunk - * @throws IOException - * if an I/O exception occurs. - * @throws InterruptedException - * if the thread was interrupted. - */ - private ByteBufferWrapper getChunk(final int chunkIdx) throws IOException, InterruptedException { - ByteBufferWrapper chunk = chunkCache[chunkIdx]; - if (chunk == null) { - final ByteBufferWrapper byteBufferDup = zipFileSlice.physicalZipFile.getByteBuffer(chunkIdx) - .duplicate(); - chunk = chunkCache[chunkIdx] = byteBufferDup; - } - return chunk; - } - - /** - * Copy from an offset within the file into a byte[] array (possibly spanning the boundary between two 2GB - * chunks). - * - * @param off - * the offset - * @param buf - * the buffer to copy into - * @param bufStart - * the start index within the buffer - * @param numBytesToRead - * the number of bytes to read - * @return the number of bytes read - * @throws IOException - * if an I/O exception occurs. - * @throws InterruptedException - * if the thread was interrupted. - */ - int read(final long off, final byte[] buf, final int bufStart, final int numBytesToRead) - throws IOException, InterruptedException { - if (off < 0 || bufStart < 0 || bufStart + numBytesToRead > buf.length) { - throw new IndexOutOfBoundsException(); - } - int currBufStart = bufStart; - int remainingBytesToRead = numBytesToRead; - int totBytesRead = 0; - for (long currOff = off; remainingBytesToRead > 0;) { - // Find the ByteBuffer chunk to read from - final long currOffAbsolute = zipFileSlice.startOffsetWithinPhysicalZipFile + currOff; - final int chunkIdx = (int) (currOffAbsolute / FileUtils.MAX_BUFFER_SIZE); - final ByteBufferWrapper chunk = getChunk(chunkIdx); - final long chunkStartAbsolute = ((long) chunkIdx) * (long) FileUtils.MAX_BUFFER_SIZE; - final int startReadPos = (int) (currOffAbsolute - chunkStartAbsolute); - - // Fill buf from chunk - chunk.get(startReadPos, buf, currBufStart, remainingBytesToRead); - - currOff += remainingBytesToRead; - currBufStart += remainingBytesToRead; - totBytesRead += remainingBytesToRead; - remainingBytesToRead -= remainingBytesToRead; - } - return totBytesRead == 0 && numBytesToRead > 0 ? -1 : totBytesRead; - } - - /** - * Get a byte subarray from a byte array. - * - * @param arr - * the byte array - * @param off - * the offset to start reading from - * @param len - * the number of bytes to read - * @return the byte subarray - * @throws IndexOutOfBoundsException - * the index out of bounds exception - */ - static byte[] getBytes(final byte[] arr, final long off, final int len) throws IndexOutOfBoundsException { - if (off > 0xffffffffL) { - throw new IndexOutOfBoundsException(); - } - final int ioff = (int) off; - return Arrays.copyOfRange(arr, ioff, ioff + len); - } - - /** - * Get a byte array from the zipfile slice. - * - * @param off - * the offset to start reading from - * @param len - * the number of bytes to read - * @return the byte array - * @throws IOException - * if an I/O exception occurs. - * @throws InterruptedException - * if the thread was interrupted. - */ - byte[] getBytes(final long off, final int len) throws IOException, InterruptedException { - if (off < 0 || off > zipFileSlice.len - len) { - throw new IndexOutOfBoundsException(); - } - final byte[] bytes = new byte[len]; - if (read(off, bytes, 0, len) < len) { - throw new EOFException("Unexpected EOF"); - } - return bytes; - } - - /** - * Get a byte from a byte array. - * - * @param arr - * the byte array - * @param off - * the offset to start reading from - * @return the byte - * @throws IndexOutOfBoundsException - * the index out of bounds exception - */ - static byte getByte(final byte[] arr, final long off) throws IndexOutOfBoundsException { - if (off > 0xffffffffL) { - throw new IndexOutOfBoundsException(); - } - final int ioff = (int) off; - return (byte) (arr[ioff] & 0xff); - } - - /** - * Get a byte from the zipfile slice. - * - * @param off - * the offset to start reading from - * @return the byte - * @throws IOException - * if an I/O exception occurs. - * @throws InterruptedException - * if the thread was interrupted. - */ - byte getByte(final long off) throws IOException, InterruptedException { - if (off < 0 || off > zipFileSlice.len - 1) { - throw new IndexOutOfBoundsException(); - } - if (read(off, scratch, 0, 1) < 1) { - throw new EOFException("Unexpected EOF"); - } - return (byte) (scratch[0] & 0xff); - } - - /** - * Get a short from a byte array. - * - * @param arr - * the byte array - * @param off - * the offset to start reading from - * @return the short - * @throws IndexOutOfBoundsException - * the index out of bounds exception - */ - static int getShort(final byte[] arr, final long off) throws IndexOutOfBoundsException { - if (off > 0xffffffffL) { - throw new IndexOutOfBoundsException(); - } - final int ioff = (int) off; - return ((arr[ioff + 1] & 0xff) << 8) | (arr[ioff] & 0xff); - } - - /** - * Get a short from the zipfile slice. - * - * @param off - * the offset to start reading from - * @return the short - * @throws IOException - * if an I/O exception occurs. - * @throws InterruptedException - * if the thread was interrupted. - */ - int getShort(final long off) throws IOException, InterruptedException { - if (off < 0 || off > zipFileSlice.len - 2) { - throw new IndexOutOfBoundsException(); - } - if (read(off, scratch, 0, 2) < 2) { - throw new EOFException("Unexpected EOF"); - } - return ((scratch[1] & 0xff) << 8) | (scratch[0] & 0xff); - } - - /** - * Get an int from a byte array. - * - * @param arr - * the byte array - * @param off - * the offset to start reading from - * @return the int - * @throws IOException - * if an I/O exception occurs. - */ - static int getInt(final byte[] arr, final long off) throws IOException { - if (off > 0xffffffffL) { - throw new IndexOutOfBoundsException(); - } - final int ioff = (int) off; - return ((arr[ioff + 3] & 0xff) << 24) // - | ((arr[ioff + 2] & 0xff) << 16) // - | ((arr[ioff + 1] & 0xff) << 8) // - | (arr[ioff] & 0xff); - } - - /** - * Get an int from the zipfile slice. - * - * @param off - * the offset to start reading from - * @return the int - * @throws IOException - * if an I/O exception occurs. - * @throws InterruptedException - * if the thread was interrupted. - */ - int getInt(final long off) throws IOException, InterruptedException { - if (off < 0 || off > zipFileSlice.len - 4) { - throw new IndexOutOfBoundsException(); - } - if (read(off, scratch, 0, 4) < 4) { - throw new EOFException("Unexpected EOF"); - } - return ((scratch[3] & 0xff) << 24) // - | ((scratch[2] & 0xff) << 16) // - | ((scratch[1] & 0xff) << 8) // - | (scratch[0] & 0xff); - } - - /** - * Get a long from a byte array. - * - * @param arr - * the byte array - * @param off - * the offset to start reading from - * @return the long - * @throws IOException - * if an I/O exception occurs. - */ - static long getLong(final byte[] arr, final long off) throws IOException { - if (off > 0xffffffffL) { - throw new IndexOutOfBoundsException(); - } - final int ioff = (int) off; - return ((arr[ioff + 7] & 0xffL) << 56) // - | ((arr[ioff + 6] & 0xffL) << 48) // - | ((arr[ioff + 5] & 0xffL) << 40) // - | ((arr[ioff + 4] & 0xffL) << 32) // - | ((arr[ioff + 3] & 0xffL) << 24) // - | ((arr[ioff + 2] & 0xffL) << 16) // - | ((arr[ioff + 1] & 0xffL) << 8) // - | (arr[ioff] & 0xffL); - } - - /** - * Get a long from the zipfile slice. - * - * @param off - * the offset to start reading from - * @return the long - * @throws IOException - * if an I/O exception occurs. - * @throws InterruptedException - * if the thread was interrupted. - */ - long getLong(final long off) throws IOException, InterruptedException { - if (off < 0 || off > zipFileSlice.len - 8) { - throw new IndexOutOfBoundsException(); - } - if (read(off, scratch, 0, 8) < 8) { - throw new EOFException("Unexpected EOF"); - } - return ((scratch[7] & 0xffL) << 56) // - | ((scratch[6] & 0xffL) << 48) // - | ((scratch[5] & 0xffL) << 40) // - | ((scratch[4] & 0xffL) << 32) // - | ((scratch[3] & 0xffL) << 24) // - | ((scratch[2] & 0xffL) << 16) // - | ((scratch[1] & 0xffL) << 8) // - | (scratch[0] & 0xffL); - } - - /** - * Get a string from a byte array. - * - * @param arr - * the byte array - * @param off - * the offset to start reading from - * @param lenBytes - * the length of the string in bytes - * @return the string - * @throws IOException - * if an I/O exception occurs. - */ - static String getString(final byte[] arr, final long off, final int lenBytes) throws IOException { - if (off > 0xffffffffL) { - throw new IndexOutOfBoundsException(); - } - final int ioff = (int) off; - return new String(arr, ioff, lenBytes, StandardCharsets.UTF_8); - } - - /** - * Get a string from the zipfile slice. - * - * @param off - * the offset to start reading from - * @param lenBytes - * the length of the string in bytes - * @return the string - * @throws IOException - * if an I/O exception occurs. - * @throws InterruptedException - * if the thread was interrupted. - */ - String getString(final long off, final int lenBytes) throws IOException, InterruptedException { - if (off < 0 || off > zipFileSlice.len - lenBytes) { - throw new IndexOutOfBoundsException(); - } - final byte[] scratchToUse = lenBytes <= scratch.length ? scratch : new byte[lenBytes]; - if (read(off, scratchToUse, 0, lenBytes) < lenBytes) { - throw new EOFException("Unexpected EOF"); - } - // Assume the entry names are encoded in UTF-8 (should be the case for all jars; the only other - // valid zipfile charset is CP437, which is the same as ASCII for printable high-bit-clear chars) - return new String(scratchToUse, 0, lenBytes, StandardCharsets.UTF_8); - } - - /* (non-Javadoc) - * @see java.lang.AutoCloseable#close() - */ - @Override - public void close() { - // Drop refs to ByteBuffer chunks so they can be garbage collected - Arrays.fill(chunkCache, null); - } -} \ No newline at end of file diff --git a/src/main/java/nonapi/io/github/classgraph/fileslice/ArraySlice.java b/src/main/java/nonapi/io/github/classgraph/fileslice/ArraySlice.java new file mode 100644 index 000000000..2bdead992 --- /dev/null +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/ArraySlice.java @@ -0,0 +1,88 @@ +/* + * This file is part of ClassGraph. + * + * Author: Luke Hutchison + * + * Hosted at: https://github.com/classgraph/classgraph + * + * -- + * + * The MIT License (MIT) + * + * Copyright (c) 2020 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.fileslice; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; + +import nonapi.io.github.classgraph.fastzipfilereader.NestedJarHandler; +import nonapi.io.github.classgraph.fileslice.reader.RandomAccessArrayReader; +import nonapi.io.github.classgraph.fileslice.reader.RandomAccessReader; + +/** A byte array slice. */ +public class ArraySlice extends Slice { + /** The wrapped byte array. */ + public byte[] arr; + + /** Constructor. */ + private ArraySlice(final ArraySlice parentSlice, final long offset, final long length, + final boolean isDeflatedZipEntry, final long inflatedLengthHint, + final NestedJarHandler nestedJarHandler) { + super(parentSlice, offset, length, isDeflatedZipEntry, inflatedLengthHint, nestedJarHandler); + this.arr = parentSlice.arr; + } + + /** Constructor. */ + public ArraySlice(final byte[] arr, final boolean isDeflatedZipEntry, final long inflatedLengthHint, + final NestedJarHandler nestedJarHandler) { + super(arr.length, isDeflatedZipEntry, inflatedLengthHint, nestedJarHandler); + this.arr = arr; + } + + @Override + public Slice slice(final long offset, final long length, final boolean isDeflatedZipEntry, + final long inflatedLengthHint) { + if (this.isDeflatedZipEntry) { + throw new IllegalArgumentException("Cannot slice a deflated zip entry"); + } + return new ArraySlice(this, offset, length, isDeflatedZipEntry, inflatedLengthHint, nestedJarHandler); + } + + @Override + public byte[] load() throws IOException { + if (isDeflatedZipEntry) { + // Deflate into RAM if necessary + try (InputStream inputStream = open()) { + return NestedJarHandler.readAllBytesAsArray(inputStream, inflatedLengthHint); + } + } else if (sliceStartPos == 0L && sliceLength == arr.length) { + // Fast path -- return whole array, if the array is the whole slice and is not deflated + return arr; + } else { + // Copy range of array, if it is a slice and it is not deflated + return Arrays.copyOfRange(arr, (int) sliceStartPos, (int) (sliceStartPos + sliceLength)); + } + } + + @Override + public RandomAccessReader randomAccessReader() { + return new RandomAccessArrayReader(arr, (int) sliceStartPos, (int) sliceLength); + } +} \ No newline at end of file diff --git a/src/main/java/nonapi/io/github/classgraph/fileslice/FileSlice.java b/src/main/java/nonapi/io/github/classgraph/fileslice/FileSlice.java new file mode 100644 index 000000000..11d8f0dd4 --- /dev/null +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/FileSlice.java @@ -0,0 +1,126 @@ +/* + * This file is part of ClassGraph. + * + * Author: Luke Hutchison + * + * Hosted at: https://github.com/classgraph/classgraph + * + * -- + * + * The MIT License (MIT) + * + * Copyright (c) 2020 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.fileslice; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.RandomAccessFile; +import java.nio.channels.FileChannel; + +import nonapi.io.github.classgraph.fastzipfilereader.NestedJarHandler; +import nonapi.io.github.classgraph.fileslice.reader.RandomAccessFileReader; +import nonapi.io.github.classgraph.fileslice.reader.RandomAccessReader; +import nonapi.io.github.classgraph.utils.FileUtils; + +/** A {@link File} slice. */ +public class FileSlice extends Slice { + /** The {@link File}. */ + public final File file; + + /** The {@link RandomAccessFile} opened on the {@link File}. */ + public final RandomAccessFile raf; + + private final FileChannel fileChannel; + private final long fileLength; + + /** Constructor. */ + private FileSlice(final FileSlice parentSlice, final long offset, final long length, + final boolean isDeflatedZipEntry, final long inflatedLengthHint, + final NestedJarHandler nestedJarHandler) { + super(parentSlice, offset, length, isDeflatedZipEntry, inflatedLengthHint, nestedJarHandler); + this.file = parentSlice.file; + this.raf = parentSlice.raf; + this.fileChannel = parentSlice.fileChannel; + this.fileLength = parentSlice.fileLength; + } + + /** + * Constructor. + * + * @throws IOException + * if the file cannot be opened. + */ + public FileSlice(final File file, final boolean isDeflatedZipEntry, final long inflatedLengthHint, + final NestedJarHandler nestedJarHandler) throws IOException { + super(file.length(), isDeflatedZipEntry, inflatedLengthHint, nestedJarHandler); + this.file = file; + this.raf = nestedJarHandler.openFile(file); + this.fileChannel = raf.getChannel(); + this.fileLength = file.length(); + } + + /** + * Constructor. + * + * @throws IOException + * if the file cannot be opened. + */ + public FileSlice(final File file, final NestedJarHandler nestedJarHandler) throws IOException { + this(file, /* isDeflatedZipEntry = */ false, /* inflatedSizeHint = */ 0L, nestedJarHandler); + } + + @Override + public Slice slice(final long offset, final long length, final boolean isDeflatedZipEntry, + final long inflatedLengthHint) { + if (this.isDeflatedZipEntry) { + throw new IllegalArgumentException("Cannot slice a deflated zip entry"); + } + return new FileSlice(this, offset, length, isDeflatedZipEntry, inflatedLengthHint, nestedJarHandler); + } + + /** Read directly from FileChannel (slow path, but handles >2GB). */ + @Override + public RandomAccessReader randomAccessReader() { + return new RandomAccessFileReader(fileChannel, sliceStartPos, sliceLength); + } + + @Override + public byte[] load() throws IOException { + if (isDeflatedZipEntry) { + // Deflate into RAM if necessary + try (InputStream inputStream = open()) { + return NestedJarHandler.readAllBytesAsArray(inputStream, inflatedLengthHint); + } + } else { + if (sliceLength > FileUtils.MAX_BUFFER_SIZE) { + throw new IOException("File is larger than 2GB"); + } + final RandomAccessReader reader = randomAccessReader(); + final byte[] content = new byte[(int) sliceLength]; + if (reader.read(sliceStartPos, content, 0, content.length) < content.length) { + // Should not happen + throw new IOException("File is truncated"); + } + return content; + } + } + + // TODO: could make load() and read() mmap the file to MappedByteBuffers +} diff --git a/src/main/java/nonapi/io/github/classgraph/fileslice/Slice.java b/src/main/java/nonapi/io/github/classgraph/fileslice/Slice.java new file mode 100644 index 000000000..72d6f64f9 --- /dev/null +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/Slice.java @@ -0,0 +1,258 @@ + +/* + * This file is part of ClassGraph. + * + * Author: Luke Hutchison + * + * Hosted at: https://github.com/classgraph/classgraph + * + * -- + * + * The MIT License (MIT) + * + * Copyright (c) 2020 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.fileslice; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +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; + +/** + * A slice of a {@link File}, {@link ByteBuffer} or {@link InputStream}. A single {@link Slice} instance should only + * be used by a single thread. + */ +public abstract class Slice { + /** The {@link NestedJarHandler}. */ + protected final NestedJarHandler nestedJarHandler; + + /** The parent slice. */ + protected final Slice parentSlice; + + /** The start position of the slice. */ + public final long sliceStartPos; + + /** The length of the slice, or -1L if unknown (for {@link InputStream}). */ + public final long sliceLength; + + /** If true, the slice is a deflated zip entry, and needs to be inflated to access the content. */ + public final boolean isDeflatedZipEntry; + + /** If the slice is a deflated zip entry, this is the expected uncompressed length, or -1L if unknown. */ + public final long inflatedLengthHint; + + /** The cached hashCode. */ + private int hashCode; + + /** Constructor. */ + protected Slice(final Slice parentSlice, final long offset, final long length, final boolean isDeflatedZipEntry, + final long inflatedLengthHint, final NestedJarHandler nestedJarHandler) { + this.parentSlice = parentSlice; + final long parentSliceStartPos = parentSlice == null ? 0L : parentSlice.sliceStartPos; + this.sliceStartPos = parentSliceStartPos + offset; + this.sliceLength = length; + this.isDeflatedZipEntry = isDeflatedZipEntry; + this.inflatedLengthHint = inflatedLengthHint; + this.nestedJarHandler = nestedJarHandler; + + if (sliceStartPos < 0L) { + throw new IllegalArgumentException("Invalid startPos"); + } + if (length < 0L) { + throw new IllegalArgumentException("Invalid length"); + } + if (parentSlice != null && (sliceStartPos < parentSliceStartPos + || sliceStartPos + length > parentSliceStartPos + parentSlice.sliceLength)) { + throw new IllegalArgumentException("Child slice is not completely contained within parent slice"); + } + } + + /** Constructor. */ + protected Slice(final long length, final boolean isDeflatedZipEntry, final long inflatedLengthHint, + final NestedJarHandler nestedJarHandler) { + this(/* parentSlice = */ null, 0L, length, isDeflatedZipEntry, inflatedLengthHint, nestedJarHandler); + } + + /** + * Get a child {@link Slice} from this parent {@link Slice}. The child slice must be smaller than the parent + * slice, and completely contained within it. + * + * @param offset + * The offset to start slicing from, relative to this parent slice's start position. + * @param length + * The length of the slice. + * @param isDeflatedZipEntry + * True if the slice is a deflated zip entry. + * @param inflatedLengthHint + * If this is a deflated zip entry, the expected length of the inflated content, or -1L if unknown. + * If this is not a deflated zip entry, 0L. + * @return The child slice. + */ + public abstract Slice slice(long offset, long length, boolean isDeflatedZipEntry, + final long inflatedLengthHint); + + /** + * Open this {@link Slice} as an {@link InputStream}. + * + * @throws IOException + * if an inflater cannot be created for this {@link Slice}. + */ + public InputStream open() throws IOException { + final InputStream rawInputStream = new InputStream() { + RandomAccessReader randomAccessReader = randomAccessReader(); + private long currOff; + private long markOff; + private final byte[] byteBuf = new byte[1]; + private final AtomicBoolean closed = new AtomicBoolean(); + + @Override + public int read() throws IOException { + if (closed.get()) { + throw new IOException("Already closed"); + } + return read(byteBuf, 0, 1); + } + + // InputStream's default implementation of this method is very slow -- it calls read() + // for every byte. This method reads the maximum number of bytes possible in one call. + @Override + public int read(final byte buf[], final int off, final int len) throws IOException { + if (closed.get()) { + throw new IOException("Already closed"); + } else if (len == 0) { + return 0; + } + final int numBytesToRead = Math.min(len, available()); + if (numBytesToRead < 1) { + return -1; + } + final int numBytesRead = randomAccessReader.read(currOff, buf, off, numBytesToRead); + if (numBytesRead > 0) { + currOff += numBytesRead; + } + return numBytesRead; + } + + @Override + public long skip(final long n) throws IOException { + if (closed.get()) { + throw new IOException("Already closed"); + } + final long newOff = Math.min(currOff + n, sliceLength); + final long skipped = newOff - currOff; + currOff = newOff; + return skipped; + } + + @Override + public int available() { + return (int) Math.min(Math.max(sliceLength - currOff, 0L), FileUtils.MAX_BUFFER_SIZE); + } + + @Override + public void mark(final int readlimit) { + // Ignore readlimit + markOff = currOff; + } + + @Override + public void reset() { + currOff = markOff; + } + + @Override + public boolean markSupported() { + return true; + } + + @Override + public void close() { + closed.getAndSet(true); + // Nothing to close + } + }; + return isDeflatedZipEntry ? nestedJarHandler.openInflaterInputStream(rawInputStream) : rawInputStream; + } + + /** Create a new {@link RandomAccessReader} for this {@link Slice}. */ + 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. + */ + 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); + } + + /** Load this {@link Slice} into a byte array. */ + public abstract byte[] load() throws IOException; + + /** + * Read this {@link Slice} as a {@link String}. + * + * @throws IOException + * if slice cannot be read. + */ + public String loadAsString() throws IOException { + return new String(load(), StandardCharsets.UTF_8); + } + + /** Read this {@link Slice} into a {@link ByteBuffer}. */ + public ByteBuffer read() throws IOException { + return ByteBuffer.wrap(load()); + } + + @Override + public int hashCode() { + if (hashCode == 0) { + hashCode = (parentSlice == null ? 1 : parentSlice.hashCode) ^ ((int) sliceStartPos * 7) + ^ ((int) sliceLength * 15); + if (hashCode == 0) { + hashCode = 1; + } + } + return hashCode; + } + + @Override + public boolean equals(final Object o) { + if (o == this) { + return true; + } else if (!(o instanceof Slice)) { + return false; + } else { + final Slice other = (Slice) o; + return this.parentSlice == other.parentSlice && this.sliceStartPos == other.sliceStartPos + && this.sliceLength == other.sliceLength; + } + } +} 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 new file mode 100644 index 000000000..a005c31b4 --- /dev/null +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/reader/ClassfileReader.java @@ -0,0 +1,430 @@ + +/* + * This file is part of ClassGraph. + * + * Author: Luke Hutchison + * + * Hosted at: https://github.com/classgraph/classgraph + * + * -- + * + * The MIT License (MIT) + * + * Copyright (c) 2020 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.fileslice.reader; + +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.nio.ReadOnlyBufferException; +import java.util.Arrays; + +import nonapi.io.github.classgraph.fileslice.ArraySlice; +import nonapi.io.github.classgraph.fileslice.FileSlice; +import nonapi.io.github.classgraph.fileslice.Slice; +import nonapi.io.github.classgraph.utils.FileUtils; +import nonapi.io.github.classgraph.utils.StringUtils; + +/** + * A {@link Slice} reader that works as either a {@link RandomAccessReader} or a {@link SequentialReader}. The file + * is buffered up to the point it has been read so far. Reads in big endian order, as required by the + * classfile format. + */ +public class ClassfileReader implements RandomAccessReader, SequentialReader, Closeable { + /** If slice is deflated, a wrapper for {@link InflateInputStream}. */ + private InputStream inflaterInputStream; + + /** + * If slice is not deflated, a {@link RandomAccessReader} for either the {@link ArraySlice} or {@link FileSlice} + * concrete subclass. + */ + private RandomAccessReader randomAccessReader; + + /** Buffer. */ + private byte[] arr; + + /** The number of bytes used in arr. */ + private int arrUsed; + + /** The current read index within the slice. */ + private int currIdx; + + /** + * The length of the classfile if known (because it is not deflated), or -1 if unknown (because it is deflated). + */ + private int classfileLengthHint = -1; + + /** + * Initial buffer size. For most classfiles, only the first 16-64kb needs to be read (we don't read the + * bytecodes). + */ + private static final int INITIAL_BUF_SIZE = 16384; + + /** + * Read this many bytes each time there is a buffer underrun. This is smaller than 8k by 8 bytes to prevent the + * doubling of the array size when the last chunk doesn't quite fit within the 16kb of INITIAL_BUF_SIZE, since + * the number of bytes that can be requested is up to 8 (for longs). Otherwise we could request to read to (8kb + * * 2 + 8), which would double the size of the buffer to 32kb, but if we only need to read between 8kb and + * 16kb, then we unnecessarily copied the buffer content one extra time. + */ + private static final int BUF_CHUNK_SIZE = 8192 - 8; + + /** + * Constructor. + * + * @param slice + * the {@link Slice} to read. + * @throws IOException + * If an inflater cannot be opened on the {@link Slice}. + */ + public ClassfileReader(final Slice slice) throws IOException { + this.classfileLengthHint = (int) slice.sliceLength; + if (slice.isDeflatedZipEntry) { + // If this is a deflated slice, need to read from an InflaterInputStream to fill buffer + inflaterInputStream = slice.open(); + arr = new byte[INITIAL_BUF_SIZE]; + classfileLengthHint = (int) Math.min(slice.inflatedLengthHint, FileUtils.MAX_BUFFER_SIZE); + } else { + if (slice instanceof ArraySlice) { + // If slice is an ArraySlice, avoid copying by simply reusing the wrapped byte array + // in place of the buffer array, and mark it as fully loaded + final ArraySlice arraySlice = (ArraySlice) slice; + if (arraySlice.sliceStartPos == 0 && arraySlice.sliceLength == arraySlice.arr.length) { + // ArraySlice is the whole array + arr = arraySlice.arr; + } else { + // ArraySlice covers only a partial array, and this class doesn't support a starting + // offset, so copy the sliced part of the array to a new buffer + arr = Arrays.copyOfRange(arraySlice.arr, (int) arraySlice.sliceStartPos, + (int) (arraySlice.sliceStartPos + arraySlice.sliceLength)); + } + arrUsed = arr.length; + classfileLengthHint = arr.length; + } else { + // Otherwise this is a FileSlice -- need to fetch chunks of bytes using a random access reader + randomAccessReader = slice.randomAccessReader(); + arr = new byte[INITIAL_BUF_SIZE]; + classfileLengthHint = (int) Math.min(slice.sliceLength, FileUtils.MAX_BUFFER_SIZE); + } + } + } + + /** + * Constructor for reader of module {@link InputStream} (which is not deflated). + * + * @param inputStream + * the {@link InputStream} to read from. + * @throws IOException + * If an inflater cannot be opened on the {@link Slice}. + */ + public ClassfileReader(final InputStream inputStream) throws IOException { + inflaterInputStream = inputStream; + arr = new byte[INITIAL_BUF_SIZE]; + } + + /** @return the current read position. */ + public int currPos() { + return currIdx; + } + + /** @return the buffer. */ + public byte[] buf() { + return arr; + } + + /** + * Called when there is a buffer underrun to ensure there are sufficient bytes available in the array to read + * the given number of bytes at the given start index. + */ + private void readTo(final int targetArrUsed) throws IOException { + // Array does not need to grow larger than the length hint (if the uncompressed size of the zip entry + // is an underestimate, classfile will be truncated). If -1, assume 2GB is the max size. + final int maxArrLen = classfileLengthHint == -1 ? FileUtils.MAX_BUFFER_SIZE : classfileLengthHint; + if (inflaterInputStream == null && randomAccessReader == null) { + // If neither inflaterInputStream nor randomAccessReader is set, then slice is an ArraySlice, + // and array is already "fully loaded" (the ArraySlice's backing array is used as the buffer). + throw new IOException("Tried to read past end of fixed array buffer"); + } + if (targetArrUsed > FileUtils.MAX_BUFFER_SIZE || targetArrUsed < 0 || arrUsed == maxArrLen) { + throw new IOException("Hit 2GB limit while trying to grow buffer array"); + } + + // Need to read at least BUF_CHUNK_SIZE (but don't overshoot past 2GB limit) + final int maxNewArrUsed = (int) Math.min(Math.max(targetArrUsed, (long) (arrUsed + BUF_CHUNK_SIZE)), + maxArrLen); + + // Double the size of the array if it's too small to contain the new chunk of bytes + if (arr.length < maxNewArrUsed) { + arr = Arrays.copyOf(arr, (int) Math.min(arr.length * 2L, maxArrLen)); + } + + // Figure out the maximum number of bytes that can be read into the array (which is the minimum + // of the number of requested bytes, and the space left in the array) + final int maxBytesToRead = Math.min(maxNewArrUsed - arrUsed, arr.length - arrUsed); + + // Read a new chunk into the buffer, starting at position arrUsed + if (inflaterInputStream != null) { + // Read from inflater input stream + final int numRead = inflaterInputStream.read(arr, arrUsed, maxBytesToRead); + if (numRead > 0) { + arrUsed += numRead; + } + } else /* randomAccessReader == null, so this is a (non-deflated) FileSlice */ { + // Don't read past end of slice + final int bytesToRead = Math.min(maxBytesToRead, maxArrLen - arrUsed); + // Read bytes from FileSlice into arr + final int numBytesRead = randomAccessReader.read(/* srcOffset = */ arrUsed, /* dstArr = */ arr, + /* dstArrStart = */ arrUsed, /* numBytes = */ bytesToRead); + if (numBytesRead > 0) { + arrUsed += numBytesRead; + } + } + + // Check the buffer was able to be filled to the requested position + if (arrUsed < targetArrUsed) { + throw new IOException("Buffer underflow"); + } + } + + /** + * Ensure that the given number of bytes have been read into the buffer from the beginning of the slice. + * + * @throws IOException + * on EOF or if the bytes could not be read. + */ + public void bufferTo(final int numBytes) throws IOException { + if (numBytes > arrUsed) { + readTo(numBytes); + } + } + + @Override + public int read(final long srcOffset, final byte[] dstArr, final int dstArrStart, final int numBytes) + throws IOException { + if (numBytes == 0) { + return 0; + } + final int idx = (int) srcOffset; + if (idx + numBytes > arrUsed) { + readTo(idx + numBytes); + } + final int numBytesToRead = Math.max(Math.min(numBytes, dstArr.length - dstArrStart), 0); + if (numBytesToRead == 0) { + return -1; + } + try { + System.arraycopy(arr, idx, dstArr, dstArrStart, numBytesToRead); + return numBytesToRead; + } catch (final IndexOutOfBoundsException e) { + throw new IOException("Read index out of bounds"); + } + } + + @Override + public int read(final long srcOffset, final ByteBuffer dstBuf, final int dstBufStart, final int numBytes) + throws IOException { + if (numBytes == 0) { + return 0; + } + final int idx = (int) srcOffset; + if (idx + numBytes > arrUsed) { + readTo(idx + numBytes); + } + final int numBytesToRead = Math.max(Math.min(numBytes, dstBuf.capacity() - dstBufStart), 0); + if (numBytesToRead == 0) { + return -1; + } + try { + dstBuf.position(dstBufStart); + dstBuf.limit(dstBufStart + numBytesToRead); + dstBuf.put(arr, idx, numBytesToRead); + return numBytesToRead; + } catch (BufferUnderflowException | IndexOutOfBoundsException | ReadOnlyBufferException e) { + throw new IOException("Read index out of bounds"); + } + } + + @Override + public byte readByte(final long offset) throws IOException { + final int idx = (int) offset; + if (idx + 1 > arrUsed) { + readTo(idx + 1); + } + return arr[idx]; + } + + @Override + public int readUnsignedByte(final long offset) throws IOException { + final int idx = (int) offset; + if (idx + 1 > arrUsed) { + readTo(idx + 1); + } + return arr[idx] & 0xff; + } + + @Override + public short readShort(final long offset) throws IOException { + return (short) readUnsignedShort(offset); + } + + @Override + public int readUnsignedShort(final long offset) throws IOException { + final int idx = (int) offset; + if (idx + 2 > arrUsed) { + readTo(idx + 2); + } + return ((arr[idx] & 0xff) << 8) // + | (arr[idx + 1] & 0xff); + } + + @Override + public int readInt(final long offset) throws IOException { + final int idx = (int) offset; + if (idx + 4 > arrUsed) { + readTo(idx + 4); + } + return ((arr[idx] & 0xff) << 24) // + | ((arr[idx + 1] & 0xff) << 16) // + | ((arr[idx + 2] & 0xff) << 8) // + | (arr[idx + 3] & 0xff); + } + + @Override + public long readUnsignedInt(final long offset) throws IOException { + return readInt(offset) & 0xffffffffL; + } + + @Override + public long readLong(final long offset) throws IOException { + final int idx = (int) offset; + if (idx + 8 > arrUsed) { + readTo(idx + 8); + } + return ((arr[idx] & 0xffL) << 56) // + | ((arr[idx + 1] & 0xffL) << 48) // + | ((arr[idx + 2] & 0xffL) << 40) // + | ((arr[idx + 3] & 0xffL) << 32) // + | ((arr[idx + 4] & 0xffL) << 24) // + | ((arr[idx + 5] & 0xffL) << 16) // + | ((arr[idx + 6] & 0xffL) << 8) // + | (arr[idx + 7] & 0xffL); + } + + @Override + public byte readByte() throws IOException { + final byte val = readByte(currIdx); + currIdx++; + return val; + } + + @Override + public int readUnsignedByte() throws IOException { + final int val = readUnsignedByte(currIdx); + currIdx++; + return val; + } + + @Override + public short readShort() throws IOException { + final short val = readShort(currIdx); + currIdx += 2; + return val; + } + + @Override + public int readUnsignedShort() throws IOException { + final int val = readUnsignedShort(currIdx); + currIdx += 2; + return val; + } + + @Override + public int readInt() throws IOException { + final int val = readInt(currIdx); + currIdx += 4; + return val; + } + + @Override + public long readUnsignedInt() throws IOException { + final long val = readUnsignedInt(currIdx); + currIdx += 4; + return val; + } + + @Override + public long readLong() throws IOException { + final long val = readLong(currIdx); + currIdx += 8; + return val; + } + + @Override + public void skip(final int bytesToSkip) throws IOException { + if (bytesToSkip < 0) { + throw new IllegalArgumentException("Tried to skip a negative number of bytes"); + } + final int idx = currIdx; + if (idx + bytesToSkip > arrUsed) { + readTo(idx + bytesToSkip); + } + currIdx += bytesToSkip; + } + + @Override + public String readString(final long offset, final int numBytes, final boolean replaceSlashWithDot, + final boolean stripLSemicolon) throws IOException { + final int idx = (int) offset; + if (idx + numBytes > arrUsed) { + readTo(idx + numBytes); + } + return StringUtils.readString(arr, idx, numBytes, replaceSlashWithDot, stripLSemicolon); + } + + @Override + public String readString(final int numBytes, final boolean replaceSlashWithDot, final boolean stripLSemicolon) + throws IOException { + final String val = StringUtils.readString(arr, currIdx, numBytes, replaceSlashWithDot, stripLSemicolon); + currIdx += numBytes; + return val; + } + + @Override + public String readString(final long offset, final int numBytes) throws IOException { + return readString(offset, numBytes, false, false); + } + + @Override + public String readString(final int numBytes) throws IOException { + return readString(numBytes, false, false); + } + + @Override + public void close() { + try { + if (inflaterInputStream != null) { + inflaterInputStream.close(); + } + } catch (final Exception e) { + // Ignore + } + } +} diff --git a/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessArrayReader.java b/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessArrayReader.java new file mode 100644 index 000000000..ef714307d --- /dev/null +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessArrayReader.java @@ -0,0 +1,162 @@ +/* + * This file is part of ClassGraph. + * + * Author: Luke Hutchison + * + * Hosted at: https://github.com/classgraph/classgraph + * + * -- + * + * The MIT License (MIT) + * + * Copyright (c) 2020 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.fileslice.reader; + +import java.io.IOException; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.nio.ReadOnlyBufferException; + +import nonapi.io.github.classgraph.utils.StringUtils; + +/** + * {@link RandomAccessReader} backed by a byte array. Reads in little endian order, as required by the + * zipfile format. + */ +public class RandomAccessArrayReader implements RandomAccessReader { + private final byte[] arr; + private final int sliceStartPos; + private final int sliceLength; + + /** Constructor. */ + public RandomAccessArrayReader(final byte[] arr, final int sliceStartPos, final int length) { + this.arr = arr; + this.sliceStartPos = sliceStartPos; + this.sliceLength = length; + } + + @Override + public int read(final long srcOffset, final byte[] dstArr, final int dstArrStart, final int numBytes) + throws IOException { + if (numBytes == 0) { + return 0; + } + if (srcOffset < 0L || numBytes < 0 || numBytes > sliceLength) { + throw new IOException("Read index out of bounds"); + } + try { + final int numBytesToRead = Math.max(Math.min(numBytes, dstArr.length - dstArrStart), 0); + if (numBytesToRead == 0) { + return -1; + } + final int srcStart = (int) (sliceStartPos + srcOffset); + System.arraycopy(arr, srcStart, dstArr, dstArrStart, numBytesToRead); + return numBytesToRead; + } catch (final IndexOutOfBoundsException e) { + throw new IOException("Read index out of bounds"); + } + } + + @Override + public int read(final long srcOffset, final ByteBuffer dstBuf, final int dstBufStart, final int numBytes) + throws IOException { + if (numBytes == 0) { + return 0; + } + if (srcOffset < 0L || numBytes < 0 || numBytes > sliceLength) { + throw new IOException("Read index out of bounds"); + } + try { + final int numBytesToRead = Math.max(Math.min(numBytes, dstBuf.capacity() - dstBufStart), 0); + if (numBytesToRead == 0) { + return -1; + } + final int srcStart = (int) (sliceStartPos + srcOffset); + dstBuf.position(dstBufStart); + dstBuf.limit(dstBufStart + numBytesToRead); + dstBuf.put(arr, srcStart, numBytesToRead); + return numBytesToRead; + } catch (BufferUnderflowException | IndexOutOfBoundsException | ReadOnlyBufferException e) { + throw new IOException("Read index out of bounds"); + } + } + + @Override + public byte readByte(final long offset) throws IOException { + final int idx = sliceStartPos + (int) offset; + return arr[idx]; + } + + @Override + public int readUnsignedByte(final long offset) throws IOException { + final int idx = sliceStartPos + (int) offset; + return arr[idx] & 0xff; + } + + @Override + public short readShort(final long offset) throws IOException { + return (short) readUnsignedShort(offset); + } + + @Override + public int readUnsignedShort(final long offset) throws IOException { + final int idx = sliceStartPos + (int) offset; + return ((arr[idx + 1] & 0xff) << 8) // + | (arr[idx] & 0xff); + } + + @Override + public int readInt(final long offset) throws IOException { + final int idx = sliceStartPos + (int) offset; + return ((arr[idx + 3] & 0xff) << 24) // + | ((arr[idx + 2] & 0xff) << 16) // + | ((arr[idx + 1] & 0xff) << 8) // + | (arr[idx] & 0xff); + } + + @Override + public long readUnsignedInt(final long offset) throws IOException { + return readInt(offset) & 0xffffffffL; + } + + @Override + public long readLong(final long offset) throws IOException { + final int idx = sliceStartPos + (int) offset; + return ((arr[idx + 7] & 0xffL) << 56) // + | ((arr[idx + 6] & 0xffL) << 48) // + | ((arr[idx + 5] & 0xffL) << 40) // + | ((arr[idx + 4] & 0xffL) << 32) // + | ((arr[idx + 3] & 0xffL) << 24) // + | ((arr[idx + 2] & 0xffL) << 16) // + | ((arr[idx + 1] & 0xffL) << 8) // + | (arr[idx] & 0xffL); + } + + @Override + public String readString(final long offset, final int numBytes, final boolean replaceSlashWithDot, + final boolean stripLSemicolon) throws IOException { + final int idx = sliceStartPos + (int) offset; + return StringUtils.readString(arr, idx, numBytes, replaceSlashWithDot, stripLSemicolon); + } + + @Override + public String readString(final long offset, final int numBytes) throws IOException { + return readString(offset, numBytes, false, false); + } +} diff --git a/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessFileReader.java b/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessFileReader.java new file mode 100644 index 000000000..3d7a14d8b --- /dev/null +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessFileReader.java @@ -0,0 +1,177 @@ +/* + * This file is part of ClassGraph. + * + * Author: Luke Hutchison + * + * Hosted at: https://github.com/classgraph/classgraph + * + * -- + * + * The MIT License (MIT) + * + * Copyright (c) 2020 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.fileslice.reader; + +import java.io.File; +import java.io.IOException; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; + +import nonapi.io.github.classgraph.utils.StringUtils; + +/** + * {@link RandomAccessReader} for a {@link File}. Reads in little endian order, as required by the zipfile + * format. + */ +public class RandomAccessFileReader implements RandomAccessReader { + private final FileChannel fileChannel; + private final long sliceStartPos; + private final long sliceLength; + private ByteBuffer reusableByteBuffer; + private final byte[] scratchArr = new byte[8]; + private final ByteBuffer scratchByteBuf = ByteBuffer.wrap(scratchArr); + private byte[] utf8Bytes; + + /** Constructor. */ + public RandomAccessFileReader(final FileChannel fileChannel, final long sliceStartPos, final long sliceLength) { + this.fileChannel = fileChannel; + this.sliceStartPos = sliceStartPos; + this.sliceLength = sliceLength; + } + + @Override + public int read(final long srcOffset, final ByteBuffer dstBuf, final int dstBufStart, final int numBytes) + throws IOException { + if (numBytes == 0) { + return 0; + } + try { + if (srcOffset < 0L || numBytes < 0 || numBytes > sliceLength) { + throw new IOException("Read index out of bounds"); + } + final long srcStart = sliceStartPos + srcOffset; + dstBuf.position(dstBufStart); + dstBuf.limit(dstBufStart + numBytes); + return fileChannel.read(dstBuf, srcStart); + + } catch (BufferUnderflowException | IndexOutOfBoundsException e) { + throw new IOException("Read index out of bounds"); + } + } + + @Override + public int read(final long srcOffset, final byte[] dstArr, final int dstArrStart, final int numBytes) + throws IOException { + if (numBytes == 0) { + return 0; + } + try { + if (reusableByteBuffer == null || reusableByteBuffer.array() != dstArr) { + // If reusableByteBuffer is not set, or wraps a different array from a previous operation, + // wrap dstArr with a new ByteBuffer + reusableByteBuffer = ByteBuffer.wrap(dstArr); + } + // Read into reusableByteBuffer, which is backed with dstArr + return read(srcOffset, reusableByteBuffer, dstArrStart, numBytes); + + } catch (BufferUnderflowException | IndexOutOfBoundsException e) { + throw new IOException("Read index out of bounds"); + } + } + + @Override + public byte readByte(final long offset) throws IOException { + if (read(offset, scratchByteBuf, 0, 1) < 1) { + throw new IOException("Premature EOF"); + } + return scratchArr[0]; + } + + @Override + public int readUnsignedByte(final long offset) throws IOException { + if (read(offset, scratchByteBuf, 0, 1) < 1) { + throw new IOException("Premature EOF"); + } + return scratchArr[0] & 0xff; + } + + @Override + public short readShort(final long offset) throws IOException { + return (short) readUnsignedShort(offset); + } + + @Override + public int readUnsignedShort(final long offset) throws IOException { + if (read(offset, scratchByteBuf, 0, 2) < 2) { + throw new IOException("Premature EOF"); + } + return ((scratchArr[1] & 0xff) << 8) // + | (scratchArr[0] & 0xff); + } + + @Override + public int readInt(final long offset) throws IOException { + if (read(offset, scratchByteBuf, 0, 4) < 4) { + throw new IOException("Premature EOF"); + } + return ((scratchArr[3] & 0xff) << 24) // + | ((scratchArr[2] & 0xff) << 16) // + | ((scratchArr[1] & 0xff) << 8) // + | (scratchArr[0] & 0xff); + } + + @Override + public long readUnsignedInt(final long offset) throws IOException { + return readInt(offset) & 0xffffffffL; + } + + @Override + public long readLong(final long offset) throws IOException { + if (read(offset, scratchByteBuf, 0, 8) < 8) { + throw new IOException("Premature EOF"); + } + return ((scratchArr[7] & 0xffL) << 56) // + | ((scratchArr[6] & 0xffL) << 48) // + | ((scratchArr[5] & 0xffL) << 40) // + | ((scratchArr[4] & 0xffL) << 32) // + | ((scratchArr[3] & 0xffL) << 24) // + | ((scratchArr[2] & 0xffL) << 16) // + | ((scratchArr[1] & 0xffL) << 8) // + | (scratchArr[0] & 0xffL); + } + + @Override + public String readString(final long offset, final int numBytes, final boolean replaceSlashWithDot, + final boolean stripLSemicolon) throws IOException { + // Reuse UTF8 buffer array if it's non-null from a previous call, and if it's big enough + if (utf8Bytes == null || utf8Bytes.length < numBytes) { + utf8Bytes = new byte[numBytes]; + } + if (read(offset, utf8Bytes, 0, numBytes) < numBytes) { + throw new IOException("Premature EOF"); + } + return StringUtils.readString(utf8Bytes, 0, numBytes, replaceSlashWithDot, stripLSemicolon); + } + + @Override + public String readString(final long offset, final int numBytes) throws IOException { + return readString(offset, numBytes, false, false); + } +} 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 new file mode 100644 index 000000000..fa7232984 --- /dev/null +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessReader.java @@ -0,0 +1,180 @@ +/* + * This file is part of ClassGraph. + * + * Author: Luke Hutchison + * + * Hosted at: https://github.com/classgraph/classgraph + * + * -- + * + * The MIT License (MIT) + * + * Copyright (c) 2020 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.fileslice.reader; + +import java.io.IOException; +import java.nio.ByteBuffer; + +/** Interface for random access to values in byte order. */ +public interface RandomAccessReader { + /** + * Read bytes into a {@link ByteBuffer}. + * + * @param srcOffset + * The offset to start reading from. + * @param dstBuf + * The {@link ByteBuffer} to write into. + * @param dstBufStart + * The offset within the destination buffer to start writing at. + * @param numBytes + * The number of bytes to read. + * @return The number of bytes actually read, or -1 if no more bytes could be read. + * @throws IOException + * If there was an exception while reading. + */ + public int read(long srcOffset, ByteBuffer dstBuf, int dstBufStart, int numBytes) throws IOException; + + /** + * Read bytes into a byte array. + * + * @param srcOffset + * The offset to start reading from. + * @param dstArr + * The byte array to write into. + * @param dstArrStart + * The offset within the destination array to start writing at. + * @param numBytes + * The number of bytes to read. + * @return The number of bytes actually read, or -1 if no more bytes could be read. + * @throws IOException + * If there was an exception while reading. + */ + public 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). + * + * @param offset + * The buffer offset to read from. + * @return The byte at the offset. + * @throws IOException + * If there was an exception while reading. + */ + public byte readByte(final long offset) throws IOException; + + /** + * Read an unsigned byte at a specific offset (without changing the current cursor offset). + * + * @param offset + * The buffer offset to read from. + * @return The unsigned byte at the offset. + * @throws IOException + * If there was an exception while reading. + */ + public int readUnsignedByte(final long offset) throws IOException; + + /** + * Read a short at a specific offset (without changing the current cursor offset). + * + * @param offset + * The buffer offset to read from. + * @return The short at the offset. + * @throws IOException + * If there was an exception while reading. + */ + public short readShort(final long offset) throws IOException; + + /** + * Read a unsigned short at a specific offset (without changing the current cursor offset). + * + * @param offset + * The buffer offset to read from. + * @return The unsigned short at the offset. + * @throws IOException + * If there was an exception while reading. + */ + public int readUnsignedShort(final long offset) throws IOException; + + /** + * Read a int at a specific offset (without changing the current cursor offset). + * + * @param offset + * The buffer offset to read from. + * @return The int at the offset. + * @throws IOException + * If there was an exception while reading. + */ + public int readInt(final long offset) throws IOException; + + /** + * Read a unsigned int at a specific offset (without changing the current cursor offset). + * + * @param offset + * The buffer offset to read from. + * @return The int at the offset, as a long. + * @throws IOException + * If there was an exception while reading. + */ + public long readUnsignedInt(final long offset) throws IOException; + + /** + * Read a long at a specific offset (without changing the current cursor offset). + * + * @param offset + * The buffer offset to read from. + * @return The long at the offset. + * @throws IOException + * If there was an exception while reading. + */ + public long readLong(final long offset) throws IOException; + + /** + * Reads the "modified UTF8" format defined in the Java classfile spec, optionally replacing '/' with '.', and + * optionally removing the prefix "L" and the suffix ";". + * + * @param offset + * The start offset of the string. + * @param numBytes + * The number of bytes of the UTF8 encoding of the string, or if -1, read the length of the string as + * a short in the first two bytes at offset. + * @param replaceSlashWithDot + * If true, replace '/' with '.'. + * @param stripLSemicolon + * If true, string final ';' character. + * @return The string. + * @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; + + /** + * Reads the "modified UTF8" format defined in the Java classfile spec. + * + * @param offset + * The start offset of the string. + * @param numBytes + * The number of bytes of the UTF8 encoding of the string, or if -1, read the length of the string as + * a short in the first two bytes at offset. + * @return The string. + * @throws IOException + * If an I/O exception occurs. + */ + public 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 new file mode 100644 index 000000000..62459ea10 --- /dev/null +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/reader/SequentialReader.java @@ -0,0 +1,137 @@ +/* + * This file is part of ClassGraph. + * + * Author: Luke Hutchison + * + * Hosted at: https://github.com/classgraph/classgraph + * + * -- + * + * The MIT License (MIT) + * + * Copyright (c) 2020 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.fileslice.reader; + +import java.io.IOException; + +/** Interface for sequentially reading values in byte order. */ +public interface SequentialReader { + /** + * Read a byte at the current cursor position. + * + * @return The byte at the current cursor position. + * @throws IOException + * If there was an exception while reading. + */ + public byte readByte() throws IOException; + + /** + * Read an unsigned byte at the current cursor position. + * + * @return The unsigned byte at the current cursor position. + * @throws IOException + * If there was an exception while reading. + */ + public int readUnsignedByte() throws IOException; + + /** + * Read a short at the current cursor position. + * + * @return The short at the current cursor position. + * @throws IOException + * If there was an exception while reading. + */ + public short readShort() throws IOException; + + /** + * Read a unsigned short at the current cursor position. + * + * @return The unsigned shortat the current cursor position. + * @throws IOException + * If there was an exception while reading. + */ + public int readUnsignedShort() throws IOException; + + /** + * Read a int at the current cursor position. + * + * @return The int at the current cursor position. + * @throws IOException + * If there was an exception while reading. + */ + public int readInt() throws IOException; + + /** + * Read a unsigned int at the current cursor position. + * + * @return The int at the current cursor position, as a long. + * @throws IOException + * If there was an exception while reading. + */ + public long readUnsignedInt() throws IOException; + + /** + * Read a long at the current cursor position. + * + * @return The long at the current cursor position. + * @throws IOException + * If there was an exception while reading. + */ + public long readLong() throws IOException; + + /** + * Skip the given number of bytes. + * + * @param bytesToSkip + * The number of bytes to skip. + * @throws IOException + * If there was an exception while reading. + */ + public void skip(final int bytesToSkip) throws IOException; + + /** + * Reads the "modified UTF8" format defined in the Java classfile spec, optionally replacing '/' with '.', and + * optionally removing the prefix "L" and the suffix ";". + * + * @param numBytes + * The number of bytes of the UTF8 encoding of the string, or if -1, read the length of the string as + * a short in the first two bytes at offset. + * @param replaceSlashWithDot + * If true, replace '/' with '.'. + * @param stripLSemicolon + * If true, string final ';' character. + * @return The string. + * @throws IOException + * If an I/O exception occurs. + */ + public String readString(final int numBytes, final boolean replaceSlashWithDot, final boolean stripLSemicolon) + throws IOException; + + /** + * Reads the "modified UTF8" format defined in the Java classfile spec. + * + * @param numBytes + * The number of bytes of the UTF8 encoding of the string, or if -1, read the length of the string as + * a short in the first two bytes at offset. + * @return The string. + * @throws IOException + * If an I/O exception occurs. + */ + public String readString(final int numBytes) throws IOException; +} 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 aee38f5ff..148f0a7eb 100644 --- a/src/main/java/nonapi/io/github/classgraph/recycler/Recycler.java +++ b/src/main/java/nonapi/io/github/classgraph/recycler/Recycler.java @@ -112,11 +112,15 @@ public RecycleOnClose acquireRecycleOnClose() throws E { */ public final void recycle(final T instance) { if (instance != null) { - usedInstances.remove(instance); + if (!usedInstances.remove(instance)) { + throw new IllegalArgumentException("Tried to recycle an instance that was not in use"); + } if (instance instanceof Resettable) { ((Resettable) instance).reset(); } - unusedInstances.add(instance); + if (!unusedInstances.add(instance)) { + throw new IllegalArgumentException("Tried to recycle an instance twice"); + } } } 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 9b4a5b292..abb03e6c8 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -31,19 +31,16 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; -import java.io.InputStream; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; -import java.nio.charset.StandardCharsets; import java.nio.file.LinkOption; 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.Arrays; import java.util.List; import io.github.classgraph.ClassGraphException; @@ -62,33 +59,18 @@ public final class FileUtils { /** The Unsafe object. */ private static Object theUnsafe; - /** - * The minimum filesize at which it becomes more efficient to read a file with a memory-mapped file channel - * rather than an InputStream. Based on benchmark testing using the following benchmark, averaged over three - * separate runs, then plotted as a speedup curve for 1, 2, 4 and 8 concurrent threads: - * - * https://github.com/lukehutch/FileReadingBenchmark - */ - public static final int FILECHANNEL_FILE_SIZE_THRESHOLD; - /** * 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; - /** The default size of a file buffer. */ - private static final int DEFAULT_BUFFER_SIZE = 16384; - /** * The maximum size of a file buffer array. Eight bytes smaller than {@link Integer#MAX_VALUE}, since some VMs * reserve header words in arrays. */ public static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8; - /** The maximum initial buffer size. */ - private static final int MAX_INITIAL_BUFFER_SIZE = 16 * 1024 * 1024; - // ------------------------------------------------------------------------------------------------------------- /** @@ -120,182 +102,6 @@ private FileUtils() { // ------------------------------------------------------------------------------------------------------------- - static { - switch (VersionFinder.OS) { - case Linux: - // On Linux, FileChannel is more efficient once file sizes are larger than 16kb, - // and the speedup increases superlinearly, reaching 1.5-3x for a filesize of 1MB - // (and the performance increase does not level off at 1MB either -- that is as - // far as this was benchmarked). - case MacOSX: - // On older/slower Mac OS X machines, FileChannel is always 10-20% slower than InputStream, - // except for very large files (>1MB), and only for single-threaded reading. - // But on newer/faster Mac OS X machines, you get a 10-20% speedup between 16kB and 128kB, - // then a much larger speedup for files larger than 128kb (topping out at about 2.5x speedup). - // It's probably worth setting the threshold to 16kB to get the 10-20% speedup for files - // larger than 16kB in size on modern machines. - case Solaris: - case BSD: - case Unix: - // No testing has been performed yet on the other unices, so just pick the same val as MacOSX and Linux - FILECHANNEL_FILE_SIZE_THRESHOLD = 16384; - break; - - case Windows: - // Windows is always 10-20% faster with FileChannel than with InputStream, even for small files. - FILECHANNEL_FILE_SIZE_THRESHOLD = -1; - break; - - case Unknown: - // For any other operating system - default: - FILECHANNEL_FILE_SIZE_THRESHOLD = 16384; - break; - } - } - - // ------------------------------------------------------------------------------------------------------------- - - /** - * Read all the bytes in an {@link InputStream}. - * - * @param inputStream - * The {@link InputStream}. - * @param fileSizeHint - * The file size, if known, otherwise -1L. - * @return The contents of the {@link InputStream} as a byte array. - * @throws IOException - * If the contents could not be read. - */ - private static byte[] readAllBytes(final InputStream inputStream, final long fileSizeHint) throws IOException { - if (fileSizeHint > MAX_BUFFER_SIZE) { - throw new IOException("InputStream is too large to read"); - } - final int bufferSize = fileSizeHint < 1L - // 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 - // lengths do not become a memory allocation attack vector - : Math.min((int) fileSizeHint, MAX_INITIAL_BUFFER_SIZE); - byte[] buf = new byte[bufferSize]; - int totBytesRead = 0; - for (int bytesRead;;) { - while ((bytesRead = inputStream.read(buf, totBytesRead, buf.length - totBytesRead)) > 0) { - // Fill buffer until nothing more can be read - totBytesRead += bytesRead; - } - if (bytesRead < 0) { - // Reached end of stream - break; - } - // bytesRead == 0 => grow buffer (avoid integer overflow in next line) - if (buf.length <= MAX_BUFFER_SIZE - buf.length) { - buf = Arrays.copyOf(buf, buf.length * 2); - } else { - if (buf.length == MAX_BUFFER_SIZE) { - // Try reading one more byte, just in case the stream is exactly MAX_BUFFER_SIZE in length - if (inputStream.read() == -1) { - break; - } else { - throw new IOException("InputStream too large to read into array"); - } - } - // Can't double the size of the buffer, but increase it to max size - buf = Arrays.copyOf(buf, MAX_BUFFER_SIZE); - } - } - // Return buffer and number of bytes read - return totBytesRead == buf.length ? buf : Arrays.copyOf(buf, totBytesRead); - } - - /** - * Read all the bytes in an {@link InputStream} as a byte array. - * - * @param inputStream - * The {@link InputStream}. - * @param fileSizeHint - * The file size, if known, otherwise -1L. - * @return The contents of the {@link InputStream} as a byte array. - * @throws IOException - * If the contents could not be read. - */ - public static byte[] readAllBytesAsArray(final InputStream inputStream, final long fileSizeHint) - throws IOException { - return readAllBytes(inputStream, fileSizeHint); - } - - /** - * Read all the bytes in an {@link InputStream} as a {@link ByteBuffer}. - * - * @param inputStream - * The {@link InputStream}. - * @param fileSizeHint - * The file size, if known, otherwise -1L. - * @return The contents of the {@link InputStream} as a {@link ByteBuffer}. - * @throws IOException - * If the contents could not be read. - */ - public static ByteBuffer readAllBytesAsByteBuffer(final InputStream inputStream, final long fileSizeHint) - throws IOException { - final byte[] buf = readAllBytes(inputStream, fileSizeHint); - return ByteBuffer.wrap(buf, 0, buf.length); - } - - /** - * Read all the bytes in an {@link InputStream} as a String. - * - * @param inputStream - * The {@link InputStream}. - * @param fileSizeHint - * The file size, if known, otherwise -1L. - * @return The contents of the {@link InputStream} as a String. - * @throws IOException - * If the contents could not be read. - */ - public static String readAllBytesAsString(final InputStream inputStream, final long fileSizeHint) - throws IOException { - final byte[] buf = readAllBytes(inputStream, fileSizeHint); - return new String(buf, 0, buf.length, StandardCharsets.UTF_8); - } - - // ------------------------------------------------------------------------------------------------------------- - - /** - * Produce an {@link InputStream} that is able to read from a {@link ByteBuffer}. - * - * @param byteBuffer - * The {@link ByteBuffer}. - * @return An {@link InputStream} that reads from the {@link ByteBuffer}. - */ - public static InputStream byteBufferToInputStream(final ByteBuffer byteBuffer) { - // https://stackoverflow.com/questions/4332264/wrapping-a-bytebuffer-with-an-inputstream/6603018#6603018 - return new InputStream() { - /** The intermediate buffer. */ - final ByteBuffer buf = byteBuffer; - - @Override - public int read() { - if (!buf.hasRemaining()) { - return -1; - } - return buf.get() & 0xFF; - } - - @Override - public int read(final byte[] bytes, final int off, final int len) { - if (!buf.hasRemaining()) { - return -1; - } - - final int bytesRead = Math.min(len, buf.remaining()); - buf.get(bytes, off, bytesRead); - return bytesRead; - } - }; - } - - // ------------------------------------------------------------------------------------------------------------- - /** * Sanitize relative paths against "zip slip" vulnerability, by removing path segments if ".." is found in the * URL, but without allowing navigation above the path hierarchy root. Treats each "!" character as a new path diff --git a/src/main/java/nonapi/io/github/classgraph/utils/InputStreamOrByteBufferAdapter.java b/src/main/java/nonapi/io/github/classgraph/utils/InputStreamOrByteBufferAdapter.java deleted file mode 100644 index 537b31620..000000000 --- a/src/main/java/nonapi/io/github/classgraph/utils/InputStreamOrByteBufferAdapter.java +++ /dev/null @@ -1,454 +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.io.IOException; -import java.io.InputStream; -import java.nio.BufferUnderflowException; -import java.nio.ByteBuffer; -import java.util.Arrays; - -import nonapi.io.github.classgraph.fastzipfilereader.ByteBufferWrapper; - -/** Buffer class that can wrap either an InputStream or a ByteBuffer, depending on which is available. */ -public class InputStreamOrByteBufferAdapter implements AutoCloseable { - /** - * Buffer size for initial read. We can save some time by reading most of the classfile header in a single read - * at the beginning of the scan. - * - *

- * (If chunk sizes are too small, significant overhead is expended in refilling the buffer. If they are too - * large, significant overhead is expended in decompressing more of the classfile header than is needed. Testing - * on a large classpath indicates that the defaults are reasonably optimal.) - */ - private static final int INITIAL_BUFFER_CHUNK_SIZE = 16384; - - /** Buffer size for classfile reader. */ - private static final int SUBSEQUENT_BUFFER_CHUNK_SIZE = 4096; - - /** The InputStream, if applicable. */ - private InputStream inputStream; - - /** The {@link ByteBufferWrapper}, if applicable. */ - private ByteBufferWrapper byteBufferWrapper; - - /** - * Bytes read from the beginning of the classfile. Only access an index in this array directly if at least that - * many bytes have already been read from the classfile (i.e. can be used to read backwards but not forwards in - * the classfile). - */ - public byte[] buf; - - /** - * - * /** The current position in the buffer. - */ - public int curr; - - /** Bytes used in the buffer. */ - private int bufBytesFilled; - - /** - * Create an {@link InputStreamOrByteBufferAdapter} from an {@link InputStream}. - * - * @param inputStream - * the input stream - */ - public InputStreamOrByteBufferAdapter(final InputStream inputStream) { - this.inputStream = inputStream; - this.buf = new byte[INITIAL_BUFFER_CHUNK_SIZE]; - } - - /** - * Create an {@link InputStreamOrByteBufferAdapter} from an {@link InputStream}. - * - * @param byteBufferWrapper - * the byte buffer wrapper - */ - public InputStreamOrByteBufferAdapter(final ByteBufferWrapper byteBufferWrapper) { - final ByteBuffer byteBuffer = byteBufferWrapper.getByteBuffer(); - if (byteBuffer != null && byteBuffer.hasArray()) { - // Just use the array behind the buffer as the input buffer - this.buf = byteBuffer.array(); - this.bufBytesFilled = this.buf.length; - } else { - this.byteBufferWrapper = byteBufferWrapper; - this.buf = new byte[INITIAL_BUFFER_CHUNK_SIZE]; - } - } - - /** - * Copy up to len bytes into buf, starting at the given offset. - * - * @param off - * The start index for the copy. - * @param len - * The maximum number of bytes to copy. - * @return The number of bytes actually copied. - * @throws IOException - * If the file content could not be read. - */ - private int read(final int off, final int len) throws IOException { - if (len == 0) { - return 0; - } - if (inputStream != null) { - // Wrapped InputStream - return inputStream.read(buf, off, len); - } else { - // Wrapped ByteBuffer - final int bytesRemainingInBuf = byteBufferWrapper != null ? byteBufferWrapper.remaining() - : buf.length - off; - final int bytesRead = Math.max(0, Math.min(len, bytesRemainingInBuf)); - if (bytesRead == 0) { - // Return -1, as per InputStream#read() contract - return -1; - } - if (byteBufferWrapper != null) { - // Copy from the ByteBuffer into the byte array - final int byteBufPositionBefore = byteBufferWrapper.position(); - try { - byteBufferWrapper.get(buf, off, bytesRead); - } catch (final BufferUnderflowException e) { - // Should not happen - throw new IOException("Buffer underflow", e); - } - return byteBufferWrapper.position() - byteBufPositionBefore; - } else { - // Nothing to read, since ByteBuffer is backed with an array - return bytesRead; - } - } - } - - /** - * Read another chunk of from the InputStream or ByteBuffer. - * - * @param bytesRequired - * the number of bytes to read - * @throws IOException - * If an I/O exception occurs. - */ - private void readMore(final int bytesRequired) throws IOException { - if ((long) bufBytesFilled + (long) bytesRequired > FileUtils.MAX_BUFFER_SIZE) { - // Since buf is an array, we're limited to reading 2GB per file - throw new IOException("File is larger than 2GB, cannot read it"); - } - // Read INITIAL_BUFFER_CHUNK_SIZE for first chunk, or SUBSEQUENT_BUFFER_CHUNK_SIZE for subsequent chunks, - // but don't try to read past 2GB limit - final int targetReadSize = Math.max(bytesRequired, // - bufBytesFilled == 0 ? INITIAL_BUFFER_CHUNK_SIZE : SUBSEQUENT_BUFFER_CHUNK_SIZE); - // Calculate number of bytes to read, based on the target read size, handling integer overflow - final int maxNewUsed = (int) Math.min((long) bufBytesFilled + (long) targetReadSize, - FileUtils.MAX_BUFFER_SIZE); - final int bytesToRead = maxNewUsed - bufBytesFilled; - if (maxNewUsed > buf.length) { - // Ran out of space, need to increase the size of the buffer - long newBufLen = buf.length; - while (newBufLen < maxNewUsed) { - newBufLen <<= 1; - } - buf = Arrays.copyOf(buf, (int) Math.min(newBufLen, FileUtils.MAX_BUFFER_SIZE)); - } - int extraBytesStillNotRead = bytesToRead; - int totBytesRead = 0; - while (extraBytesStillNotRead > 0) { - final int bytesRead = read(bufBytesFilled, extraBytesStillNotRead); - if (bytesRead > 0) { - bufBytesFilled += bytesRead; - totBytesRead += bytesRead; - extraBytesStillNotRead -= bytesRead; - } else { - // EOF - break; - } - } - if (totBytesRead < bytesRequired) { - throw new IOException("Premature EOF while reading classfile"); - } - } - - /** - * Read an unsigned byte from the buffer. - * - * @return The next unsigned byte in the buffer. - * @throws IOException - * If there was an exception while reading. - */ - public int readUnsignedByte() throws IOException { - final int val = readUnsignedByte(curr); - curr++; - return val; - } - - /** - * Read an unsigned byte at a specific offset (without changing the current read point). - * - * @param offset - * The buffer offset to read from. - * @return The unsigned byte at the buffer offset. - * @throws IOException - * If there was an exception while reading. - */ - public int readUnsignedByte(final int offset) throws IOException { - final int bytesToRead = Math.max(0, offset + 1 - bufBytesFilled); - if (bytesToRead > 0) { - readMore(bytesToRead); - } - return buf[offset] & 0xff; - } - - /** - * Read the next unsigned short. - * - * @return The next unsigned short in the buffer. - * @throws IOException - * If there was an exception while reading. - */ - public int readUnsignedShort() throws IOException { - final int val = readUnsignedShort(curr); - curr += 2; - return val; - } - - /** - * Read an unsigned short at a specific offset (without changing the current read point). - * - * @param offset - * The buffer offset to read from. - * @return The unsigned short at the buffer offset. - * @throws IOException - * If there was an exception while reading. - */ - public int readUnsignedShort(final int offset) throws IOException { - final int bytesToRead = Math.max(0, offset + 2 - bufBytesFilled); - if (bytesToRead > 0) { - readMore(bytesToRead); - } - return ((buf[offset] & 0xff) << 8) // - | (buf[offset + 1] & 0xff); - } - - /** - * Read the next int. - * - * @return The next int in the buffer. - * @throws IOException - * If there was an exception while reading. - */ - public int readInt() throws IOException { - final int val = readInt(curr); - curr += 4; - return val; - } - - /** - * Read an int at a specific offset (without changing the current read point). - * - * @param offset - * The buffer offset to read from. - * @return The int at the buffer offset. - * @throws IOException - * If there was an exception while reading. - */ - public int readInt(final int offset) throws IOException { - final int bytesToRead = Math.max(0, offset + 4 - bufBytesFilled); - if (bytesToRead > 0) { - readMore(bytesToRead); - } - return ((buf[offset] & 0xff) << 24) // - | ((buf[offset + 1] & 0xff) << 16) // - | ((buf[offset + 2] & 0xff) << 8) // - | (buf[offset + 3] & 0xff); - } - - /** - * Read the next long. - * - * @return The next long in the buffer. - * @throws IOException - * If there was an exception while reading. - */ - public long readLong() throws IOException { - final long val = readLong(curr); - curr += 8; - return val; - } - - /** - * Read a long at a specific offset (without changing the current read point). - * - * @param offset - * The buffer offset to read from. - * @return The long at the buffer offset. - * @throws IOException - * If there was an exception while reading. - */ - public long readLong(final int offset) throws IOException { - final int bytesToRead = Math.max(0, offset + 8 - bufBytesFilled); - if (bytesToRead > 0) { - readMore(bytesToRead); - } - return ((buf[offset] & 0xffL) << 56) // - | ((buf[offset + 1] & 0xffL) << 48) // - | ((buf[offset + 2] & 0xffL) << 40) // - | ((buf[offset + 3] & 0xffL) << 32) // - | ((buf[offset + 4] & 0xffL) << 24) // - | ((buf[offset + 5] & 0xffL) << 16) // - | ((buf[offset + 6] & 0xffL) << 8) // - | (buf[offset + 7] & 0xffL); - } - - /** - * Skip the given number of bytes. - * - * @param bytesToSkip - * The number of bytes to skip. - * @throws IOException - * If there was an exception while reading. - */ - public void skip(final int bytesToSkip) throws IOException { - final int bytesToRead = Math.max(0, curr + bytesToSkip - bufBytesFilled); - if (bytesToRead > 0) { - readMore(bytesToRead); - } - curr += bytesToSkip; - } - - /** - * Reads the "modified UTF8" format defined in the Java classfile spec, optionally replacing '/' with '.', and - * optionally removing the prefix "L" and the suffix ";". - * - * @param strStart - * The start index of the string. - * @param replaceSlashWithDot - * If true, replace '/' with '.'. - * @param stripLSemicolon - * If true, string final ';' character. - * @return The string. - * @throws IOException - * If an I/O exception occurs. - */ - public String readString(final int strStart, final boolean replaceSlashWithDot, final boolean stripLSemicolon) - throws IOException { - final int utfLen = readUnsignedShort(strStart); - final int utfStart = strStart + 2; - final int bufferUnderrunBytes = Math.max(0, utfStart + utfLen - bufBytesFilled); - if (bufferUnderrunBytes > 0) { - readMore(bufferUnderrunBytes); - } - final char[] chars = new char[utfLen]; - int byteIdx = 0; - int charIdx = 0; - for (; byteIdx < utfLen; byteIdx++) { - final int c = buf[utfStart + byteIdx] & 0xff; - if (c > 127) { - break; - } - chars[charIdx++] = (char) (replaceSlashWithDot && c == '/' ? '.' : c); - } - while (byteIdx < utfLen) { - final int c = buf[utfStart + byteIdx] & 0xff; - switch (c >> 4) { - case 0: - case 1: - case 2: - case 3: - case 4: - case 5: - case 6: - case 7: { - byteIdx++; - chars[charIdx++] = (char) (replaceSlashWithDot && c == '/' ? '.' : c); - break; - } - case 12: - case 13: { - byteIdx += 2; - if (byteIdx > utfLen) { - throw new IOException("Bad modified UTF8"); - } - final int c2 = buf[utfStart + byteIdx - 1]; - if ((c2 & 0xc0) != 0x80) { - throw new IOException("Bad modified UTF8"); - } - final int c3 = ((c & 0x1f) << 6) | (c2 & 0x3f); - chars[charIdx++] = (char) (replaceSlashWithDot && c3 == '/' ? '.' : c3); - break; - } - case 14: { - byteIdx += 3; - if (byteIdx > utfLen) { - throw new IOException("Bad modified UTF8"); - } - final int c2 = buf[utfStart + byteIdx - 2]; - final int c3 = buf[utfStart + byteIdx - 1]; - if ((c2 & 0xc0) != 0x80 || (c3 & 0xc0) != 0x80) { - throw new IOException("Bad modified UTF8"); - } - final int c4 = ((c & 0x0f) << 12) | ((c2 & 0x3f) << 6) | (c3 & 0x3f); - chars[charIdx++] = (char) (replaceSlashWithDot && c4 == '/' ? '.' : c4); - break; - } - default: - throw new IOException("Bad modified UTF8"); - } - } - if (charIdx == utfLen && !stripLSemicolon) { - return new String(chars); - } else { - if (stripLSemicolon) { - if (charIdx < 2 || chars[0] != 'L' || chars[charIdx - 1] != ';') { - throw new IOException("Expected string to start with 'L' and end with ';', got \"" - + new String(chars) + "\""); - } - return new String(chars, 1, charIdx - 2); - } else { - return new String(chars, 0, charIdx); - } - } - } - - /* (non-Javadoc) - * @see java.lang.AutoCloseable#close() - */ - @Override - public void close() { - if (this.inputStream != null) { - try { - this.inputStream.close(); - } catch (final IOException e) { - // Ignore - } - this.inputStream = null; - } - this.byteBufferWrapper = null; - this.buf = null; - } -} diff --git a/src/main/java/nonapi/io/github/classgraph/utils/StringUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/StringUtils.java new file mode 100644 index 000000000..f3605e705 --- /dev/null +++ b/src/main/java/nonapi/io/github/classgraph/utils/StringUtils.java @@ -0,0 +1,135 @@ +/* + * 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; + +/** + * File utilities. + */ +public final class StringUtils { + /** + * Constructor. + */ + private StringUtils() { + // Cannot be constructed + } + + /** + * Reads the "modified UTF8" format defined in the Java classfile spec, optionally replacing '/' with '.', and + * optionally removing the prefix "L" and the suffix ";". + * + * @param startOffset + * The start offset of the string. + * @param numBytes + * The number of bytes of the UTF8 encoding of the string. + * @param replaceSlashWithDot + * If true, replace '/' with '.'. + * @param stripLSemicolon + * If true, string final ';' character. + * @return The string. + * @throws IllegalArgumentException + * If string could not be parsed. + */ + public static String readString(final byte[] arr, final int startOffset, final int numBytes, + final boolean replaceSlashWithDot, final boolean stripLSemicolon) throws IllegalArgumentException { + if (startOffset < 0L || numBytes < 0 || startOffset + numBytes > arr.length) { + throw new IllegalArgumentException("offset or numBytes out of range"); + } + final char[] chars = new char[numBytes]; + int byteIdx = 0; + int charIdx = 0; + for (; byteIdx < numBytes; byteIdx++) { + final int c = arr[startOffset + byteIdx] & 0xff; + if (c > 127) { + break; + } + chars[charIdx++] = (char) (replaceSlashWithDot && c == '/' ? '.' : c); + } + while (byteIdx < numBytes) { + final int c = arr[startOffset + byteIdx] & 0xff; + switch (c >> 4) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: { + byteIdx++; + chars[charIdx++] = (char) (replaceSlashWithDot && c == '/' ? '.' : c); + break; + } + case 12: + case 13: { + byteIdx += 2; + if (byteIdx > numBytes) { + throw new IllegalArgumentException("Bad modified UTF8"); + } + final int c2 = arr[startOffset + byteIdx - 1]; + if ((c2 & 0xc0) != 0x80) { + throw new IllegalArgumentException("Bad modified UTF8"); + } + final int c3 = ((c & 0x1f) << 6) | (c2 & 0x3f); + chars[charIdx++] = (char) (replaceSlashWithDot && c3 == '/' ? '.' : c3); + break; + } + case 14: { + byteIdx += 3; + if (byteIdx > numBytes) { + throw new IllegalArgumentException("Bad modified UTF8"); + } + final int c2 = arr[startOffset + byteIdx - 2]; + final int c3 = arr[startOffset + byteIdx - 1]; + if ((c2 & 0xc0) != 0x80 || (c3 & 0xc0) != 0x80) { + throw new IllegalArgumentException("Bad modified UTF8"); + } + final int c4 = ((c & 0x0f) << 12) | ((c2 & 0x3f) << 6) | (c3 & 0x3f); + chars[charIdx++] = (char) (replaceSlashWithDot && c4 == '/' ? '.' : c4); + break; + } + default: + throw new IllegalArgumentException("Bad modified UTF8"); + } + } + if (charIdx == numBytes && !stripLSemicolon) { + return new String(chars); + } else { + if (stripLSemicolon) { + if (charIdx < 2 || chars[0] != 'L' || chars[charIdx - 1] != ';') { + throw new IllegalArgumentException("Expected string to start with 'L' and end with ';', got \"" + + new String(chars) + "\""); + } + return new String(chars, 1, charIdx - 2); + } else { + return new String(chars, 0, charIdx); + } + } + } + +} From 5574ddc407178db7c0cee0075209f1c463ee76a8 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 16 Feb 2020 03:09:44 -0700 Subject: [PATCH 0618/1778] Add mmap support back as an option (#400) --- .../java/io/github/classgraph/ClassGraph.java | 9 +- .../classgraph/ClassGraphClassLoader.java | 8 +- .../github/classgraph/ClasspathElement.java | 13 +- .../classgraph/ClasspathElementDir.java | 79 +++++---- .../classgraph/ClasspathElementModule.java | 53 +++--- .../classgraph/ClasspathElementZip.java | 47 ++--- .../java/io/github/classgraph/Resource.java | 23 +-- .../java/io/github/classgraph/Scanner.java | 21 +-- .../classgraph/concurrency/WorkQueue.java | 12 +- .../fastzipfilereader/LogicalZipFile.java | 4 + .../fastzipfilereader/NestedJarHandler.java | 65 +++---- .../fastzipfilereader/PhysicalZipFile.java | 2 +- .../classgraph/fileslice/FileSlice.java | 136 +++++++++++++-- .../io/github/classgraph/fileslice/Slice.java | 2 +- .../reader/RandomAccessArrayReader.java | 18 +- .../reader/RandomAccessByteBufferReader.java | 165 ++++++++++++++++++ .../reader/RandomAccessFileReader.java | 8 +- .../github/classgraph/scanspec/ScanSpec.java | 6 +- 18 files changed, 468 insertions(+), 203 deletions(-) create mode 100644 src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessByteBufferReader.java diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index 9358f39b6..d4340db51 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -1128,14 +1128,13 @@ public ClassGraph setMaxBufferedJarRAMSize(final int maxBufferedJarRAMSize) { } /** - * If true, use a {@link RandomAccessFile} rather than a {@link MappedByteBuffer} to open jarfiles, which is - * slower, but does not use up virtual memory space. You can call this method to disable memory mapping if you - * run into an {@link OutOfMemoryError} when scanning. + * If true, use a {@link MappedByteBuffer} rather than the {@link RandomAccessFile} API to open files, which may + * be faster, but uses up virtual memory space. * * @return this (for method chaining). */ - public ClassGraph disableMemoryMapping() { - scanSpec.disableMemoryMapping = true; + public ClassGraph enableMemoryMapping() { + scanSpec.enableMemoryMapping = true; return this; } diff --git a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java index 50b153bce..171cd08b4 100644 --- a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java +++ b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java @@ -208,8 +208,12 @@ protected Class findClass(final String className) // 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 - final ByteBuffer resourceByteBuffer = resource.read(); - return defineClass(className, resourceByteBuffer, null); + try { + final ByteBuffer resourceByteBuffer = resource.read(); + return defineClass(className, resourceByteBuffer, null); + } finally { + resource.close(); + } } catch (final IOException e) { throw new ClassNotFoundException("Could not load classfile for class " + className + " : " + e); } finally { diff --git a/src/main/java/io/github/classgraph/ClasspathElement.java b/src/main/java/io/github/classgraph/ClasspathElement.java index 3cef3bbc8..38a57cd4b 100644 --- a/src/main/java/io/github/classgraph/ClasspathElement.java +++ b/src/main/java/io/github/classgraph/ClasspathElement.java @@ -50,6 +50,9 @@ /** A classpath element (a directory or jarfile on the classpath). */ abstract class ClasspathElement { + /** The index of the classpath element within the classpath or module path. */ + int classpathElementIdx; + /** * If non-null, contains a list of resolved paths for any classpath element roots nested inside this classpath * element. (Scanning should stop at a nested classpath element root, otherwise that subtree will be scanned @@ -343,26 +346,22 @@ protected LogNode log(final int classpathElementIdx, final String msg, final Log * * @param workQueue * the work queue - * @param classpathElementIdx - * the index of the classpath element within the classpath or module path. * @param log * the log * @throws InterruptedException * if the thread was interrupted while trying to open the classpath element. */ - abstract void open(final WorkQueue workQueue, final int classpathElementIdx, - final LogNode log) throws InterruptedException; + abstract void open(final WorkQueue workQueue, final LogNode log) + throws InterruptedException; /** * Scan paths in the classpath element for whitelist/blacklist criteria, creating Resource objects for * whitelisted and non-blacklisted resources and classfiles. * - * @param classpathElementIdx - * the index of the classpath element within the classpath or module path. * @param log * the log */ - abstract void scanPaths(final int classpathElementIdx, final LogNode log); + abstract void scanPaths(final LogNode log); /** * Get the {@link Resource} for a given relative path. diff --git a/src/main/java/io/github/classgraph/ClasspathElementDir.java b/src/main/java/io/github/classgraph/ClasspathElementDir.java index ce46ba6e6..ad3303393 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementDir.java @@ -31,7 +31,6 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.io.RandomAccessFile; import java.net.URI; import java.nio.ByteBuffer; import java.nio.file.Files; @@ -93,7 +92,7 @@ class ClasspathElementDir extends ClasspathElement { * nonapi.io.github.classgraph.concurrency.WorkQueue, nonapi.io.github.classgraph.utils.LogNode) */ @Override - void open(final WorkQueue workQueue, final int classpathElementIdx, final LogNode log) { + void open(final WorkQueue workQueue, final LogNode log) { if (!scanSpec.scanDirs) { if (log != null) { log(classpathElementIdx, @@ -167,8 +166,8 @@ void open(final WorkQueue workQueue, final int classpath private Resource newResource(final String relativePath, final File resourceFile, final NestedJarHandler nestedJarHandler, final LogNode log) { return new Resource(this, resourceFile.length()) { - /** The {@link RandomAccessFile} opened on the file. */ - private RandomAccessFile raf; + /** The {@link FileSlice} opened on the file. */ + private FileSlice fileSlice; @Override public String getPath() { @@ -199,63 +198,81 @@ public Set getPosixFilePermissions() { } @Override - public synchronized ByteBuffer read() throws IOException { + public ByteBuffer read() throws IOException { if (skipClasspathElement) { // Shouldn't happen throw new IOException("Parent directory could not be opened"); } - final FileSlice fileSlice = new FileSlice(resourceFile, nestedJarHandler); - raf = fileSlice.raf; + 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 - synchronized ClassfileReader openClassfile() throws IOException { + ClassfileReader openClassfile() throws IOException { if (skipClasspathElement) { // Shouldn't happen throw new IOException("Parent directory could not be opened"); } - final FileSlice fileSlice = new FileSlice(resourceFile, nestedJarHandler); - raf = fileSlice.raf; + 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); } @Override - public synchronized InputStream open() throws IOException { + public InputStream open() throws IOException { if (skipClasspathElement) { // Shouldn't happen throw new IOException("Parent directory could not be opened"); } - final FileSlice fileSlice = new FileSlice(resourceFile, nestedJarHandler); - raf = fileSlice.raf; + 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(); + length = fileSlice.sliceLength; return inputStream; } @Override - public synchronized byte[] load() throws IOException { - if (skipClasspathElement) { - // Shouldn't happen - throw new IOException("Parent directory could not be opened"); + public byte[] load() throws IOException { + try { + read(); + fileSlice = new FileSlice(resourceFile, nestedJarHandler, /* log = */ null); + final byte[] bytes = fileSlice.load(); + length = bytes.length; + return bytes; + } finally { + close(); } - final FileSlice fileSlice = new FileSlice(resourceFile, nestedJarHandler); - raf = fileSlice.raf; - return fileSlice.load(); } @Override - public synchronized void close() { + public void close() { super.close(); // Close inputStream - if (byteBuffer != null) { - // All ByteBuffers should wrap arrays, so they don't need to be cleaned - byteBuffer = null; - } - if (raf != null) { - nestedJarHandler.closeOpenFile(raf); - raf = null; + if (isOpen.get()) { + 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.markFileSliceAsClosed(fileSlice); + fileSlice = null; + } + isOpen.getAndSet(false); } - markAsClosed(); } }; } @@ -431,13 +448,11 @@ private void scanDirRecursively(final File dir, final LogNode log) { /** * Hierarchically scan directory structure for classfiles and matching files. * - * @param classpathElementIdx - * the index of the classpath element within the classpath or module path. * @param log * the log */ @Override - void scanPaths(final int classpathElementIdx, final LogNode log) { + void scanPaths(final LogNode log) { if (skipClasspathElement) { return; } diff --git a/src/main/java/io/github/classgraph/ClasspathElementModule.java b/src/main/java/io/github/classgraph/ClasspathElementModule.java index 108a10378..1387bf766 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementModule.java +++ b/src/main/java/io/github/classgraph/ClasspathElementModule.java @@ -90,8 +90,8 @@ class ClasspathElementModule extends ClasspathElement { * nonapi.io.github.classgraph.concurrency.WorkQueue, nonapi.io.github.classgraph.utils.LogNode) */ @Override - void open(final WorkQueue workQueueIgnored, final int classpathElementIdx, - final LogNode log) throws InterruptedException { + void open(final WorkQueue workQueueIgnored, final LogNode log) + throws InterruptedException { if (!scanSpec.scanModules) { if (log != null) { log(classpathElementIdx, "Skipping module, since module scanning is disabled: " + getModuleName(), @@ -144,12 +144,15 @@ public Set getPosixFilePermissions() { } @Override - public synchronized ByteBuffer read() throws IOException { + public ByteBuffer read() throws IOException { if (skipClasspathElement) { // Shouldn't happen throw new IOException("Module could not be opened"); } - markAsOpen(); + if (isOpen.getAndSet(true)) { + throw new IOException( + "Resource is already open -- cannot open it again without first calling close()"); + } try { moduleReaderProxy = moduleReaderProxyRecycler.acquire(); // ModuleReader#read(String name) internally calls: @@ -165,21 +168,20 @@ public synchronized ByteBuffer read() throws IOException { } @Override - synchronized ClassfileReader openClassfile() throws IOException { - if (skipClasspathElement) { - // Shouldn't happen - throw new IOException("Module could not be opened"); - } + ClassfileReader openClassfile() throws IOException { return new ClassfileReader(open()); } @Override - public synchronized InputStream open() throws IOException { + public InputStream open() throws IOException { if (skipClasspathElement) { // Shouldn't happen throw new IOException("Module could not be opened"); } - markAsOpen(); + if (isOpen.getAndSet(true)) { + throw new IOException( + "Resource is already open -- cannot open it again without first calling close()"); + } try { moduleReaderProxy = moduleReaderProxyRecycler.acquire(); inputStream = moduleReaderProxy.open(resourcePath); @@ -194,7 +196,7 @@ public synchronized InputStream open() throws IOException { } @Override - public synchronized byte[] load() throws IOException { + public byte[] load() throws IOException { try { read(); final byte[] byteArray; @@ -213,21 +215,22 @@ public synchronized byte[] load() throws IOException { } @Override - public synchronized void close() { + public void close() { super.close(); // Close inputStream - if (moduleReaderProxy != null) { - if (byteBuffer != null) { - // Release any open ByteBuffer - moduleReaderProxy.release(byteBuffer); + if (isOpen.get()) { + if (moduleReaderProxy != null) { + if (byteBuffer != null) { + // Release any open ByteBuffer + moduleReaderProxy.release(byteBuffer); + } + // 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; } - markAsClosed(); } }; } @@ -254,7 +257,7 @@ Resource getResource(final String relativePath) { * the log */ @Override - void scanPaths(final int classpathElementIdx, final LogNode log) { + void scanPaths(final LogNode log) { if (skipClasspathElement) { return; } diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index ed06cf69e..df6b11e05 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -110,8 +110,7 @@ class ClasspathElementZip extends ClasspathElement { * nonapi.io.github.classgraph.concurrency.WorkQueue, nonapi.io.github.classgraph.utils.LogNode) */ @Override - void open(final WorkQueue workQueue, final int classpathElementIdx, final LogNode log) - throws InterruptedException { + void open(final WorkQueue workQueue, final LogNode log) throws InterruptedException { if (!scanSpec.scanJars) { if (log != null) { log(classpathElementIdx, "Skipping classpath element, since jar scanning is disabled: " + rawPath, @@ -349,12 +348,15 @@ public Set getPosixFilePermissions() { } @Override - public synchronized InputStream open() throws IOException { + public InputStream open() throws IOException { if (skipClasspathElement) { // Shouldn't happen throw new IOException("Jarfile could not be opened"); } - markAsOpen(); + if (isOpen.getAndSet(true)) { + throw new IOException( + "Resource is already open -- cannot open it again without first calling close()"); + } try { inputStream = zipEntry.getSlice().open(); length = zipEntry.uncompressedSize; @@ -367,20 +369,20 @@ public synchronized InputStream open() throws IOException { } @Override - synchronized ClassfileReader openClassfile() throws IOException { - if (skipClasspathElement) { - // Shouldn't happen - throw new IOException("Jarfile could not be opened"); - } + ClassfileReader openClassfile() throws IOException { return new ClassfileReader(open()); } @Override - public synchronized ByteBuffer read() throws IOException { + 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()"); + } try { byteBuffer = zipEntry.getSlice().read(); length = byteBuffer.remaining(); @@ -392,29 +394,34 @@ public synchronized ByteBuffer read() throws IOException { } @Override - public synchronized byte[] load() throws IOException { + 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()"); + } try { final byte[] byteArray = zipEntry.getSlice().load(); length = byteArray.length; return byteArray; - } catch (final IOException e) { + } finally { close(); - throw e; } } @Override - public synchronized void close() { + public void close() { super.close(); // Close inputStream - if (byteBuffer != null) { - // All ByteBuffers should wrap arrays, so they don't need to be cleaned - 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; + } } - markAsClosed(); } }; } @@ -435,13 +442,11 @@ Resource getResource(final String relativePath) { /** * Scan for path matches within jarfile, and record ZipEntry objects of matching files. * - * @param classpathElementIdx - * the index of the classpath element within the classpath or module path. * @param log * the log */ @Override - void scanPaths(final int classpathElementIdx, final LogNode log) { + void scanPaths(final LogNode log) { if (logicalZipFile == null) { skipClasspathElement = true; } diff --git a/src/main/java/io/github/classgraph/Resource.java b/src/main/java/io/github/classgraph/Resource.java index e525d1327..3acb317f7 100644 --- a/src/main/java/io/github/classgraph/Resource.java +++ b/src/main/java/io/github/classgraph/Resource.java @@ -40,6 +40,7 @@ import java.nio.charset.StandardCharsets; import java.nio.file.attribute.PosixFilePermission; import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.zip.ZipEntry; import nonapi.io.github.classgraph.fileslice.reader.ClassfileReader; @@ -64,7 +65,7 @@ public abstract class Resource implements Closeable, Comparable { protected long length; /** True if the resource is open. */ - private boolean isOpen; + protected AtomicBoolean isOpen = new AtomicBoolean(); /** The cached result of toString(). */ private String toString; @@ -92,26 +93,6 @@ public Resource(final ClasspathElement classpathElement, final long length) { // ------------------------------------------------------------------------------------------------------------- - /** - * Mark the resource as open. - * - * @throws IOException - * If the resource is already open. - */ - protected void markAsOpen() throws IOException { - if (isOpen) { - throw new IOException("Resource is already open -- cannot open it again without first calling close()"); - } - isOpen = true; - } - - /** Mark the resource as closed. */ - protected void markAsClosed() { - isOpen = false; - } - - // ------------------------------------------------------------------------------------------------------------- - /** * Convert a URI to URL, catching "jrt:" URIs as invalid. * diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index e7da4472b..aa9704836 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -196,8 +196,7 @@ class Scanner implements Callable { nestedJarHandler.moduleRefToModuleReaderProxyRecyclerMap, scanSpec); moduleOrder.add(classpathElementModule); // Open the ClasspathElementModule - classpathElementModule.open(/* ignored */ null, moduleOrder.size() - 1, - classpathFinderLog); + classpathElementModule.open(/* ignored */ null, classpathFinderLog); } else { if (classpathFinderLog != null) { classpathFinderLog.log( @@ -220,8 +219,7 @@ class Scanner implements Callable { nestedJarHandler.moduleRefToModuleReaderProxyRecyclerMap, scanSpec); moduleOrder.add(classpathElementModule); // Open the ClasspathElementModule - classpathElementModule.open(/* ignored */ null, moduleOrder.size() - 1, - classpathFinderLog); + classpathElementModule.open(/* ignored */ null, classpathFinderLog); } else { if (classpathFinderLog != null) { classpathFinderLog @@ -504,7 +502,7 @@ private WorkUnitProcessor newClasspathEntryWorkUnitProce final Queue> toplevelClasspathEltOrder) { return new WorkUnitProcessor() { @Override - public void processWorkUnit(final ClasspathEntryWorkUnit workUnit, final int workUnitIdx, + public void processWorkUnit(final ClasspathEntryWorkUnit workUnit, final WorkQueue workQueue, final LogNode log) throws InterruptedException { try { @@ -527,7 +525,7 @@ public void processWorkUnit(final ClasspathEntryWorkUnit workUnit, final int wor // 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, workUnitIdx, log); + classpathElt.open(workQueue, log); // Create a new tuple consisting of the order of the new classpath element // within its parent, and the new classpath element. @@ -638,7 +636,7 @@ public ClassfileScannerWorkUnitProcessor(final ScanSpec scanSpec, * java.lang.Object, nonapi.io.github.classgraph.concurrency.WorkQueue) */ @Override - public void processWorkUnit(final ClassfileScanWorkUnit workUnit, final int workUnitIdxIgnored, + public void processWorkUnit(final ClassfileScanWorkUnit workUnit, final WorkQueue workQueue, final LogNode log) throws InterruptedException { // Classfile scan log entries are listed inline below the entry that was added to the log // when the path of the corresponding resource was found, by using the LogNode stored in @@ -985,7 +983,9 @@ private ScanResult openClasspathElementsThenScan() throws InterruptedException, final int numElts = moduleOrder.size() + classpathEltOrder.size(); final List finalClasspathEltOrder = new ArrayList<>(numElts); final List finalClasspathEltOrderStrs = new ArrayList<>(numElts); + int classpathOrderIdx = 0; for (final ClasspathElementModule classpathElt : moduleOrder) { + classpathElt.classpathElementIdx = classpathOrderIdx++; finalClasspathEltOrder.add(classpathElt); finalClasspathEltOrderStrs.add(classpathElt.toString()); if (classpathOrderLog != null) { @@ -994,6 +994,7 @@ private ScanResult openClasspathElementsThenScan() throws InterruptedException, } } for (final ClasspathElement classpathElt : classpathEltOrder) { + classpathElt.classpathElementIdx = classpathOrderIdx++; finalClasspathEltOrder.add(classpathElt); finalClasspathEltOrderStrs.add(classpathElt.toString()); if (classpathOrderLog != null) { @@ -1007,10 +1008,10 @@ private ScanResult openClasspathElementsThenScan() throws InterruptedException, new WorkUnitProcessor() { @Override public void processWorkUnit(final ClasspathElement classpathElement, - final int classpathElementIdx, final WorkQueue workQueueIgnored, - final LogNode pathScanLog) throws InterruptedException { + final WorkQueue workQueueIgnored, final LogNode pathScanLog) + throws InterruptedException { // Scan the paths within the classpath element - classpathElement.scanPaths(classpathElementIdx, pathScanLog); + classpathElement.scanPaths(pathScanLog); } }); 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 0a2fbd90b..29d9aa99e 100644 --- a/src/main/java/nonapi/io/github/classgraph/concurrency/WorkQueue.java +++ b/src/main/java/nonapi/io/github/classgraph/concurrency/WorkQueue.java @@ -104,14 +104,11 @@ public WorkUnitWrapper(final T workUnit) { * The type of work unit to process. */ public interface WorkUnitProcessor { - /** * Process a work unit. * * @param workUnit * The work unit. - * @param workUnitIndex - * the index of the work unit from the original start of the work queue. * @param workQueue * The work queue. * @param log @@ -119,8 +116,7 @@ public interface WorkUnitProcessor { * @throws InterruptedException * If the worker thread is interrupted. */ - void processWorkUnit(T workUnit, int workUnitIndex, WorkQueue workQueue, LogNode log) - throws InterruptedException; + void processWorkUnit(T workUnit, WorkQueue workQueue, LogNode log) throws InterruptedException; } /** @@ -174,7 +170,7 @@ public static void runWorkQueue(final Collection elements, final Executor * @param workUnitProcessor * the work unit processor * @param numWorkers - * the num workers + * the number of workers * @param interruptionChecker * the interruption checker * @param log @@ -232,7 +228,7 @@ private void sendPoisonPills() { */ private void runWorkLoop() throws InterruptedException, ExecutionException { // Get next work unit from queue - for (int workUnitIdx = 0;; workUnitIdx++) { + for (;;) { // Check for interruption interruptionChecker.check(); @@ -247,7 +243,7 @@ private void runWorkLoop() throws InterruptedException, ExecutionException { // Process the work unit try { // Process the work unit (may throw InterruptedException) - workUnitProcessor.processWorkUnit(workUnitWrapper.workUnit, workUnitIdx, this, log); + workUnitProcessor.processWorkUnit(workUnitWrapper.workUnit, this, log); } catch (InterruptedException | OutOfMemoryError e) { // On InterruptedException or OutOfMemoryError, drain work queue, send poison pills, and re-throw 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 6f30e7ede..09e59370a 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java @@ -430,6 +430,10 @@ private void parseManifest(final FastZipEntry manifestZipEntry, final LogNode lo * if the thread was interrupted. */ private void readCentralDirectory(final LogNode log) throws IOException, InterruptedException { + if (slice.sliceLength < 22) { + throw new IOException("Zipfile too short to have a central directory"); + } + final RandomAccessReader reader = slice.randomAccessReader(); // Scan for End Of Central Directory (EOCD) signature. Final comment can be up to 64kB in length, 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 f03801c48..0504f19c1 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java @@ -355,9 +355,8 @@ public RecyclableInflater newInstance() throws RuntimeException { } }; - /** {@link RandomAccessFile} instances that are currently open (typically one per classpath element). */ - private Set openFiles = Collections - .newSetFromMap(new ConcurrentHashMap()); + /** {@link FileSlice} instances that are currently open. */ + private Set openFileSlices = Collections.newSetFromMap(new ConcurrentHashMap()); /** Any temporary files created while scanning. */ private Set tempFiles = Collections.newSetFromMap(new ConcurrentHashMap()); @@ -459,35 +458,23 @@ void removeTempFile(final File tempFile) throws IOException, SecurityException { } /** - * Open a file as a {@link RandomAccessFile}. + * Mark a {@link FileSlice} as open. * - * @param file - * the file to open. + * @param fileSlice + * the {@link FileSlice} that was just opened. */ - public RandomAccessFile openFile(final File file) throws IOException { - try { - final RandomAccessFile raf = new RandomAccessFile(file, "r"); - openFiles.add(raf); - return raf; - } catch (final SecurityException e) { - throw new IOException("Could not open file " + file + " : " + e.getMessage()); - } + public void markFileSliceAsOpen(final FileSlice fileSlice) throws IOException { + openFileSlices.add(fileSlice); } /** - * Close an open {@link RandomAccessFile}, and remove it from the list of files to close when - * {@link #close(LogNode)} is called. + * Mark a {@link FileSlice} as closed. * - * @param raf - * the {@link RandomAccessFile} to close. + * @param fileSlice + * the {@link FileSlice} to close. */ - public void closeOpenFile(final RandomAccessFile raf) { - openFiles.remove(raf); - try { - raf.close(); - } catch (final IOException e) { - // Ignore - } + public void markFileSliceAsClosed(final FileSlice fileSlice) { + openFileSlices.remove(fileSlice); } /** @@ -605,7 +592,7 @@ public int read(final byte outBuf[], final int off, final int len) throws IOExce return 0; } try { - // Keep fetching data from rawInputStream until + // 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, @@ -826,7 +813,7 @@ private FileSlice spillToDisk(final InputStream inputStream, final String tempFi } // Return a new FileSlice for the temporary file - return new FileSlice(tempFile, this); + return new FileSlice(tempFile, this, log); } /** @@ -842,10 +829,10 @@ private FileSlice spillToDisk(final InputStream inputStream, final String tempFi */ public static byte[] readAllBytesAsArray(final InputStream inputStream, final long uncompressedLengthHint) throws IOException { + if (uncompressedLengthHint > FileUtils.MAX_BUFFER_SIZE) { + throw new IOException("InputStream is too large to read"); + } try (InputStream inptStream = inputStream) { - if (uncompressedLengthHint > FileUtils.MAX_BUFFER_SIZE) { - throw new IOException("InputStream is too large to read"); - } final int bufferSize = uncompressedLengthHint < 1L // If fileSizeHint is zero or unknown, use default buffer size ? DEFAULT_BUFFER_SIZE @@ -937,19 +924,15 @@ public void close(final LogNode log) { fastZipEntryToZipFileSliceMap.clear(); fastZipEntryToZipFileSliceMap = null; } - if (openFiles != null) { - while (!openFiles.isEmpty()) { - for (final RandomAccessFile openFile : new ArrayList<>(openFiles)) { - try { - openFile.close(); - } catch (final IOException e) { - // Ignore - } - openFiles.remove(openFile); + if (openFileSlices != null) { + while (!openFileSlices.isEmpty()) { + for (final FileSlice fileSlice : new ArrayList<>(openFileSlices)) { + fileSlice.close(); + markFileSliceAsClosed(fileSlice); } } - openFiles.clear(); - openFiles = null; + openFileSlices.clear(); + openFileSlices = null; } if (inflaterRecycler != null) { inflaterRecycler.forceClose(); 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 c1ee0d028..b6073c9ca 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/PhysicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/PhysicalZipFile.java @@ -78,7 +78,7 @@ class PhysicalZipFile { this.file = file; this.nestedJarHandler = nestedJarHandler; this.path = FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, file.getPath()); - this.slice = new FileSlice(file, nestedJarHandler); + this.slice = new FileSlice(file, nestedJarHandler, log); } /** 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 11d8f0dd4..4686456a8 100644 --- a/src/main/java/nonapi/io/github/classgraph/fileslice/FileSlice.java +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/FileSlice.java @@ -28,29 +28,44 @@ */ package nonapi.io.github.classgraph.fileslice; +import java.io.Closeable; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; +import java.nio.ByteBuffer; import java.nio.channels.FileChannel; +import java.nio.channels.FileChannel.MapMode; +import java.util.concurrent.atomic.AtomicBoolean; import nonapi.io.github.classgraph.fastzipfilereader.NestedJarHandler; +import nonapi.io.github.classgraph.fileslice.reader.RandomAccessByteBufferReader; import nonapi.io.github.classgraph.fileslice.reader.RandomAccessFileReader; import nonapi.io.github.classgraph.fileslice.reader.RandomAccessReader; import nonapi.io.github.classgraph.utils.FileUtils; +import nonapi.io.github.classgraph.utils.LogNode; /** A {@link File} slice. */ -public class FileSlice extends Slice { +public class FileSlice extends Slice implements Closeable { /** The {@link File}. */ public final File file; /** The {@link RandomAccessFile} opened on the {@link File}. */ - public final RandomAccessFile raf; + public RandomAccessFile raf; - private final FileChannel fileChannel; private final long fileLength; - /** Constructor. */ + private FileChannel fileChannel; + + private ByteBuffer backingByteBuffer; + + private final boolean isTopLevelFileSlice; + + private final NestedJarHandler nestedJarHandler; + + private final AtomicBoolean isClosed = new AtomicBoolean(); + + /** Constructor for sub-slice of file. */ private FileSlice(final FileSlice parentSlice, final long offset, final long length, final boolean isDeflatedZipEntry, final long inflatedLengthHint, final NestedJarHandler nestedJarHandler) { @@ -59,33 +74,75 @@ private FileSlice(final FileSlice parentSlice, final long offset, final long len this.raf = parentSlice.raf; this.fileChannel = parentSlice.fileChannel; this.fileLength = parentSlice.fileLength; + this.nestedJarHandler = nestedJarHandler; + this.isTopLevelFileSlice = false; + + if (parentSlice.backingByteBuffer != null) { + // Duplicate and slice the backing byte buffer, if there is one + this.backingByteBuffer = parentSlice.backingByteBuffer.duplicate(); + this.backingByteBuffer.position((int) sliceStartPos); + this.backingByteBuffer.limit((int) (sliceStartPos + sliceLength)); + } + + // 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. + * Constructor for toplevel file slice. * * @throws IOException * if the file cannot be opened. */ public FileSlice(final File file, final boolean isDeflatedZipEntry, final long inflatedLengthHint, - final NestedJarHandler nestedJarHandler) throws IOException { + final NestedJarHandler nestedJarHandler, final LogNode log) throws IOException { super(file.length(), isDeflatedZipEntry, inflatedLengthHint, nestedJarHandler); + // Make sure the File is readable and is a regular file + FileUtils.checkCanReadAndIsFile(file); this.file = file; - this.raf = nestedJarHandler.openFile(file); + this.raf = new RandomAccessFile(file, "r"); this.fileChannel = raf.getChannel(); this.fileLength = file.length(); + this.isTopLevelFileSlice = true; + this.nestedJarHandler = nestedJarHandler; + + if (nestedJarHandler.scanSpec.enableMemoryMapping) { + try { + // Try mapping file (some operating systems throw OutOfMemoryError if file + // can't be mapped, some throw IOException) + backingByteBuffer = fileChannel.map(MapMode.READ_ONLY, 0L, fileLength); + } catch (IOException | OutOfMemoryError e) { + // Try running garbage collection then try mapping the file again + System.gc(); + System.runFinalization(); + try { + backingByteBuffer = fileChannel.map(MapMode.READ_ONLY, 0L, fileLength); + } catch (IOException | OutOfMemoryError e2) { + if (log != null) { + log.log("File " + file + " cannot be memory mapped: " + e2 + + " (using RandomAccessFile API instead)"); + } + // Fall through -- RandomAccessFile API will be used instead + } + } + } + + // Mark toplevel slice as open + nestedJarHandler.markFileSliceAsOpen(this); } /** - * Constructor. + * Constructor for toplevel file slice. * * @throws IOException * if the file cannot be opened. */ - public FileSlice(final File file, final NestedJarHandler nestedJarHandler) throws IOException { - this(file, /* isDeflatedZipEntry = */ false, /* inflatedSizeHint = */ 0L, nestedJarHandler); + public FileSlice(final File file, final NestedJarHandler nestedJarHandler, final LogNode log) + throws IOException { + this(file, /* isDeflatedZipEntry = */ false, /* inflatedSizeHint = */ 0L, nestedJarHandler, log); } + /** Slice the file. */ @Override public Slice slice(final long offset, final long length, final boolean isDeflatedZipEntry, final long inflatedLengthHint) { @@ -98,23 +155,33 @@ public Slice slice(final long offset, final long length, final boolean isDeflate /** Read directly from FileChannel (slow path, but handles >2GB). */ @Override public RandomAccessReader randomAccessReader() { - return new RandomAccessFileReader(fileChannel, sliceStartPos, sliceLength); + if (backingByteBuffer == null) { + // If file was not mmap'd, return a RandomAccessReader that uses the FileChannel + return new RandomAccessFileReader(fileChannel, sliceStartPos, sliceLength); + } else { + // If file was mmap'd, return a RandomAccessReader that uses the ByteBuffer + return new RandomAccessByteBufferReader(backingByteBuffer, sliceStartPos, sliceLength); + } } @Override public byte[] load() throws IOException { if (isDeflatedZipEntry) { - // Deflate into RAM if necessary + // Inflate into RAM if deflated + if (inflatedLengthHint > FileUtils.MAX_BUFFER_SIZE) { + throw new IOException("Uncompressed size is larger than 2GB"); + } try (InputStream inputStream = open()) { return NestedJarHandler.readAllBytesAsArray(inputStream, inflatedLengthHint); } } else { + // Copy from either RandomAccessFile or MappedByteBuffer to byte array if (sliceLength > FileUtils.MAX_BUFFER_SIZE) { throw new IOException("File is larger than 2GB"); } final RandomAccessReader reader = randomAccessReader(); final byte[] content = new byte[(int) sliceLength]; - if (reader.read(sliceStartPos, content, 0, content.length) < content.length) { + if (reader.read(0, content, 0, content.length) < content.length) { // Should not happen throw new IOException("File is truncated"); } @@ -122,5 +189,46 @@ public byte[] load() throws IOException { } } - // TODO: could make load() and read() mmap the file to MappedByteBuffers + @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) + if (inflatedLengthHint > FileUtils.MAX_BUFFER_SIZE) { + throw new IOException("Uncompressed size is larger than 2GB"); + } + return ByteBuffer.wrap(load()); + } else if (backingByteBuffer == null) { + // Copy from RandomAccessFile to byte array, then wrap in a ByteBuffer + if (sliceLength > FileUtils.MAX_BUFFER_SIZE) { + throw new IOException("File is larger than 2GB"); + } + return ByteBuffer.wrap(load()); + } else { + // FileSlice is backed with a MappedByteBuffer -- duplicate it and return it (low-cost operation) + return backingByteBuffer.duplicate(); + } + } + + // TOOD: close this in NestedJarHandler and ClasspathElementDir + @Override + public void close() { + if (!isClosed.getAndSet(true)) { + 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); + } + backingByteBuffer = null; + fileChannel = null; + try { + // Closing raf will also close the associated FileChannel + raf.close(); + } catch (final IOException e) { + // Ignore + } + raf = null; + nestedJarHandler.markFileSliceAsClosed(this); + } + } } 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 72d6f64f9..ba1a1f291 100644 --- a/src/main/java/nonapi/io/github/classgraph/fileslice/Slice.java +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/Slice.java @@ -234,7 +234,7 @@ public ByteBuffer read() throws IOException { @Override public int hashCode() { if (hashCode == 0) { - hashCode = (parentSlice == null ? 1 : parentSlice.hashCode) ^ ((int) sliceStartPos * 7) + hashCode = (parentSlice == null ? 1 : parentSlice.hashCode()) ^ ((int) sliceStartPos * 7) ^ ((int) sliceLength * 15); if (hashCode == 0) { hashCode = 1; diff --git a/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessArrayReader.java b/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessArrayReader.java index ef714307d..ee7aded1a 100644 --- a/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessArrayReader.java +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessArrayReader.java @@ -45,10 +45,10 @@ public class RandomAccessArrayReader implements RandomAccessReader { private final int sliceLength; /** Constructor. */ - public RandomAccessArrayReader(final byte[] arr, final int sliceStartPos, final int length) { + public RandomAccessArrayReader(final byte[] arr, final int sliceStartPos, final int sliceLength) { this.arr = arr; this.sliceStartPos = sliceStartPos; - this.sliceLength = length; + this.sliceLength = sliceLength; } @Override @@ -57,7 +57,7 @@ public int read(final long srcOffset, final byte[] dstArr, final int dstArrStart if (numBytes == 0) { return 0; } - if (srcOffset < 0L || numBytes < 0 || numBytes > sliceLength) { + if (srcOffset < 0L || numBytes < 0 || numBytes > sliceLength - srcOffset) { throw new IOException("Read index out of bounds"); } try { @@ -79,7 +79,7 @@ public int read(final long srcOffset, final ByteBuffer dstBuf, final int dstBufS if (numBytes == 0) { return 0; } - if (srcOffset < 0L || numBytes < 0 || numBytes > sliceLength) { + if (srcOffset < 0L || numBytes < 0 || numBytes > sliceLength - srcOffset) { throw new IOException("Read index out of bounds"); } try { @@ -104,14 +104,14 @@ public byte readByte(final long offset) throws IOException { } @Override - public int readUnsignedByte(final long offset) throws IOException { - final int idx = sliceStartPos + (int) offset; - return arr[idx] & 0xff; + public short readShort(final long offset) throws IOException { + return (short) readUnsignedShort(offset); } @Override - public short readShort(final long offset) throws IOException { - return (short) readUnsignedShort(offset); + public int readUnsignedByte(final long offset) throws IOException { + final int idx = sliceStartPos + (int) offset; + return arr[idx] & 0xff; } @Override diff --git a/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessByteBufferReader.java b/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessByteBufferReader.java new file mode 100644 index 000000000..d17a26c62 --- /dev/null +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessByteBufferReader.java @@ -0,0 +1,165 @@ +/* + * This file is part of ClassGraph. + * + * Author: Luke Hutchison + * + * Hosted at: https://github.com/classgraph/classgraph + * + * -- + * + * The MIT License (MIT) + * + * Copyright (c) 2020 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.fileslice.reader; + +import java.io.IOException; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.ReadOnlyBufferException; + +import nonapi.io.github.classgraph.utils.StringUtils; + +/** + * {@link RandomAccessReader} for a {@link ByteBuffer}. Reads in little endian order, as required by the + * zipfile format. + */ +public class RandomAccessByteBufferReader implements RandomAccessReader { + private final ByteBuffer byteBuffer; + private final int sliceStartPos; + private final int sliceLength; + + /** Constructor. */ + public RandomAccessByteBufferReader(final ByteBuffer byteBuffer, final long sliceStartPos, + final long sliceLength) { + this.byteBuffer = byteBuffer.duplicate(); + this.byteBuffer.order(ByteOrder.LITTLE_ENDIAN); + this.sliceStartPos = (int) sliceStartPos; + this.sliceLength = (int) sliceLength; + this.byteBuffer.position(this.sliceStartPos); + this.byteBuffer.limit(this.sliceStartPos + this.sliceLength); + } + + @Override + public int read(final long srcOffset, final byte[] dstArr, final int dstArrStart, final int numBytes) + throws IOException { + if (numBytes == 0) { + return 0; + } + if (srcOffset < 0L || numBytes < 0 || numBytes > sliceLength - srcOffset) { + throw new IOException("Read index out of bounds"); + } + try { + final int numBytesToRead = Math.max(Math.min(numBytes, dstArr.length - dstArrStart), 0); + if (numBytesToRead == 0) { + return -1; + } + final int srcStart = (int) srcOffset; + byteBuffer.position(sliceStartPos + srcStart); + byteBuffer.get(dstArr, dstArrStart, numBytesToRead); + byteBuffer.position(sliceStartPos); + return numBytesToRead; + } catch (final IndexOutOfBoundsException e) { + throw new IOException("Read index out of bounds"); + } + } + + @Override + public int read(final long srcOffset, final ByteBuffer dstBuf, final int dstBufStart, final int numBytes) + throws IOException { + if (numBytes == 0) { + return 0; + } + if (srcOffset < 0L || numBytes < 0 || numBytes > sliceLength - srcOffset) { + throw new IOException("Read index out of bounds"); + } + try { + final int numBytesToRead = Math.max(Math.min(numBytes, dstBuf.capacity() - dstBufStart), 0); + if (numBytesToRead == 0) { + return -1; + } + final int srcStart = (int) (sliceStartPos + srcOffset); + byteBuffer.position(srcStart); + dstBuf.position(dstBufStart); + dstBuf.limit(dstBufStart + numBytesToRead); + dstBuf.put(byteBuffer); + byteBuffer.limit(sliceStartPos + sliceLength); + byteBuffer.position(sliceStartPos); + return numBytesToRead; + } catch (BufferUnderflowException | IndexOutOfBoundsException | ReadOnlyBufferException e) { + throw new IOException("Read index out of bounds"); + } + } + + @Override + public byte readByte(final long offset) throws IOException { + final int idx = (int) (sliceStartPos + offset); + return byteBuffer.get(idx); + } + + @Override + public int readUnsignedByte(final long offset) throws IOException { + final int idx = (int) (sliceStartPos + offset); + return byteBuffer.get(idx) & 0xff; + } + + @Override + public int readUnsignedShort(final long offset) throws IOException { + final int idx = (int) (sliceStartPos + offset); + return byteBuffer.getShort(idx) & 0xff; + } + + @Override + public short readShort(final long offset) throws IOException { + return (short) readUnsignedShort(offset); + } + + @Override + public int readInt(final long offset) throws IOException { + final int idx = (int) (sliceStartPos + offset); + return byteBuffer.getInt(idx); + } + + @Override + public long readUnsignedInt(final long offset) throws IOException { + return readInt(offset) & 0xffffffffL; + } + + @Override + public long readLong(final long offset) throws IOException { + final int idx = (int) (sliceStartPos + offset); + return byteBuffer.getLong(idx); + } + + @Override + public String readString(final long offset, final int numBytes, final boolean replaceSlashWithDot, + final boolean stripLSemicolon) throws IOException { + final int idx = (int) (sliceStartPos + offset); + final byte[] arr = new byte[numBytes]; + if (read(offset, arr, 0, numBytes) < numBytes) { + throw new IOException("Premature EOF while reading string"); + } + return StringUtils.readString(arr, idx, numBytes, replaceSlashWithDot, stripLSemicolon); + } + + @Override + public String readString(final long offset, final int numBytes) throws IOException { + return readString(offset, numBytes, false, false); + } +} diff --git a/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessFileReader.java b/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessFileReader.java index 3d7a14d8b..5a963ec64 100644 --- a/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessFileReader.java +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessFileReader.java @@ -63,13 +63,14 @@ public int read(final long srcOffset, final ByteBuffer dstBuf, final int dstBufS return 0; } try { - if (srcOffset < 0L || numBytes < 0 || numBytes > sliceLength) { + if (srcOffset < 0L || numBytes < 0 || numBytes > sliceLength - srcOffset) { throw new IOException("Read index out of bounds"); } final long srcStart = sliceStartPos + srcOffset; dstBuf.position(dstBufStart); dstBuf.limit(dstBufStart + numBytes); - return fileChannel.read(dstBuf, srcStart); + final int numBytesRead = fileChannel.read(dstBuf, srcStart); + return numBytesRead == 0 ? -1 : numBytesRead; } catch (BufferUnderflowException | IndexOutOfBoundsException e) { throw new IOException("Read index out of bounds"); @@ -83,6 +84,9 @@ public int read(final long srcOffset, final byte[] dstArr, final int dstArrStart return 0; } try { + if (srcOffset < 0L || numBytes < 0 || numBytes > sliceLength - srcOffset) { + throw new IOException("Read index out of bounds"); + } if (reusableByteBuffer == null || reusableByteBuffer.array() != dstArr) { // If reusableByteBuffer is not set, or wraps a different array from a previous operation, // wrap dstArr with a new ByteBuffer 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 5b820aed8..49be638cd 100644 --- a/src/main/java/nonapi/io/github/classgraph/scanspec/ScanSpec.java +++ b/src/main/java/nonapi/io/github/classgraph/scanspec/ScanSpec.java @@ -256,11 +256,9 @@ public class ScanSpec { public int maxBufferedJarRAMSize = 64 * 1024 * 1024; /** - * If true, use a {@link RandomAccessFile} rather than a {@link MappedByteBuffer} to open jarfiles, which is - * slower, but does not use up virtual memory space. You can disable memory mapping if you get - * {@link OutOfMemoryError} when scanning. + * If true, use a {@link MappedByteBuffer} rather than the {@link RandomAccessFile} API to access file content. */ - public boolean disableMemoryMapping = false; + public boolean enableMemoryMapping; // ------------------------------------------------------------------------------------------------------------- From 91070f4246988e893889253141385605f94ea064 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 16 Feb 2020 04:00:08 -0700 Subject: [PATCH 0619/1778] Add Javadoc --- .../classgraph/ClasspathElementModule.java | 16 +++- .../java/io/github/classgraph/Resource.java | 17 ++++ .../java/io/github/classgraph/Scanner.java | 8 ++ .../classgraph/concurrency/SingletonMap.java | 4 +- .../fastzipfilereader/NestedJarHandler.java | 19 ++++- .../fastzipfilereader/PhysicalZipFile.java | 2 + .../classgraph/fileslice/ArraySlice.java | 58 ++++++++++++- .../classgraph/fileslice/FileSlice.java | 85 +++++++++++++++++-- .../io/github/classgraph/fileslice/Slice.java | 72 +++++++++++++--- .../fileslice/reader/ClassfileReader.java | 9 +- .../reader/RandomAccessArrayReader.java | 26 ++++-- .../reader/RandomAccessByteBufferReader.java | 16 +++- .../reader/RandomAccessFileReader.java | 26 +++++- .../fileslice/reader/RandomAccessReader.java | 6 +- .../fileslice/reader/SequentialReader.java | 6 +- .../classgraph/json/ReferenceEqualityKey.java | 16 ++++ .../github/classgraph/utils/StringUtils.java | 4 +- src/test/java/com/xyz/fig/SceneGraph.java | 5 ++ 18 files changed, 351 insertions(+), 44 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementModule.java b/src/main/java/io/github/classgraph/ClasspathElementModule.java index 1387bf766..ba3019986 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementModule.java +++ b/src/main/java/io/github/classgraph/ClasspathElementModule.java @@ -74,6 +74,8 @@ class ClasspathElementModule extends ClasspathElement { * the module ref * @param classLoader * the classloader + * @param moduleRefToModuleReaderProxyRecyclerMap + * the module ref to module reader proxy recycler map * @param scanSpec * the scan spec */ @@ -251,8 +253,6 @@ Resource getResource(final String relativePath) { /** * Scan for package matches within module. * - * @param classpathElementIdx - * the index of the classpath element within the classpath or module path. * @param log * the log */ @@ -449,6 +449,13 @@ public String toString() { return moduleRef.toString(); } + /** + * Equals. + * + * @param obj + * the obj + * @return true, if successful + */ /* (non-Javadoc) * @see java.lang.Object#equals(java.lang.Object) */ @@ -463,6 +470,11 @@ public boolean equals(final Object obj) { return this.getModuleNameOrEmpty().equals(other.getModuleNameOrEmpty()); } + /** + * Hash code. + * + * @return the int + */ /* (non-Javadoc) * @see java.lang.Object#hashCode() */ diff --git a/src/main/java/io/github/classgraph/Resource.java b/src/main/java/io/github/classgraph/Resource.java index 3acb317f7..ee7efc90c 100644 --- a/src/main/java/io/github/classgraph/Resource.java +++ b/src/main/java/io/github/classgraph/Resource.java @@ -337,6 +337,11 @@ public String toString() { } } + /** + * Hash code. + * + * @return the int + */ /* (non-Javadoc) * @see java.lang.Object#hashCode() */ @@ -345,6 +350,12 @@ public int hashCode() { return toString().hashCode(); } + /** + * Equals. + * + * @param obj the obj + * @return true, if successful + */ /* (non-Javadoc) * @see java.lang.Object#equals(java.lang.Object) */ @@ -358,6 +369,12 @@ public boolean equals(final Object obj) { return this.toString().equals(obj.toString()); } + /** + * Compare to. + * + * @param o the o + * @return the int + */ /* (non-Javadoc) * @see java.lang.Comparable#compareTo(java.lang.Object) */ diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index aa9704836..b637947af 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -631,6 +631,14 @@ public ClassfileScannerWorkUnitProcessor(final ScanSpec scanSpec, this.scannedClassfiles = scannedClassfiles; } + /** + * Process work unit. + * + * @param workUnit the work unit + * @param workQueue the work queue + * @param log the log + * @throws InterruptedException the interrupted exception + */ /* (non-Javadoc) * @see nonapi.io.github.classgraph.concurrency.WorkQueue.WorkUnitProcessor#processWorkUnit( * java.lang.Object, nonapi.io.github.classgraph.concurrency.WorkQueue) 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 071ab6ff1..3294337b8 100644 --- a/src/main/java/nonapi/io/github/classgraph/concurrency/SingletonMap.java +++ b/src/main/java/nonapi/io/github/classgraph/concurrency/SingletonMap.java @@ -249,7 +249,9 @@ public List> entries() throws InterruptedException { /** * Remove the singleton for a given key. - * + * + * @param key + * the key * @return the old singleton from the map, if one was present, otherwise null. * @throws InterruptedException * if interrupted. 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 0504f19c1..1ab61a083 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java @@ -459,9 +459,11 @@ void removeTempFile(final File tempFile) throws IOException, SecurityException { /** * Mark a {@link FileSlice} as open. - * + * * @param fileSlice * the {@link FileSlice} that was just opened. + * @throws IOException + * Signals that an I/O exception has occurred. */ public void markFileSliceAsOpen(final FileSlice fileSlice) throws IOException { openFileSlices.add(fileSlice); @@ -489,6 +491,8 @@ public void markFileSliceAsClosed(final FileSlice fileSlice) { * {@link PhysicalZipFile} instance. * @throws IOException * If the jar could not be downloaded, or the jar URL is malformed. + * @throws InterruptedException + * 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 @@ -557,7 +561,15 @@ private PhysicalZipFile downloadJarFromURL(final String jarURL, final LogNode lo // ------------------------------------------------------------------------------------------------------------- - /** 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 + * @return the inflater input stream + * @throws IOException + * 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) @@ -769,7 +781,7 @@ public Slice readAllBytesWithSpilloverToDisk(final InputStream inputStream, fina /** * Spill an {@link InputStream} to disk if the stream is too large to fit in RAM. - * + * * @param inputStream * The {@link InputStream}. * @param tempFileBaseName @@ -781,6 +793,7 @@ public Slice readAllBytesWithSpilloverToDisk(final InputStream inputStream, fina * nullity as buf.) * @param log * The log. + * @return the file slice * @throws IOException * If anything went wrong creating or writing to the temp file. */ 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 b6073c9ca..aa76fc307 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/PhysicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/PhysicalZipFile.java @@ -157,6 +157,8 @@ public String getPath() { /** * Get the length of the mapped file, or the initial remaining bytes in the wrapped ByteBuffer if a buffer was * wrapped. + * + * @return the length of the mapped file */ public long length() { return slice.sliceLength; diff --git a/src/main/java/nonapi/io/github/classgraph/fileslice/ArraySlice.java b/src/main/java/nonapi/io/github/classgraph/fileslice/ArraySlice.java index 2bdead992..829e8dad8 100644 --- a/src/main/java/nonapi/io/github/classgraph/fileslice/ArraySlice.java +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/ArraySlice.java @@ -41,7 +41,23 @@ public class ArraySlice extends Slice { /** The wrapped byte array. */ public byte[] arr; - /** Constructor. */ + /** + * Constructor for treating a range of an array as a slice. + * + * @param parentSlice + * the parent slice + * @param offset + * the offset of the sub-slice within the parent slice + * @param length + * the length of the sub-slice + * @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 + */ private ArraySlice(final ArraySlice parentSlice, final long offset, final long length, final boolean isDeflatedZipEntry, final long inflatedLengthHint, final NestedJarHandler nestedJarHandler) { @@ -49,13 +65,39 @@ private ArraySlice(final ArraySlice parentSlice, final long offset, final long l this.arr = parentSlice.arr; } - /** Constructor. */ + /** + * Constructor for treating a whole array as a slice. + * + * @param arr + * the array containing the slice. + * @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 + */ public ArraySlice(final byte[] arr, final boolean isDeflatedZipEntry, final long inflatedLengthHint, final NestedJarHandler nestedJarHandler) { super(arr.length, isDeflatedZipEntry, inflatedLengthHint, nestedJarHandler); this.arr = arr; } + /** + * Slice this slice to form a sub-slice. + * + * @param offset + * the offset relative to the start of this slice to use as the start of the sub-slice. + * @param length + * the length of the sub-slice. + * @param isDeflatedZipEntry + * the is 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. + * @return the slice + */ @Override public Slice slice(final long offset, final long length, final boolean isDeflatedZipEntry, final long inflatedLengthHint) { @@ -65,6 +107,13 @@ public Slice slice(final long offset, final long length, final boolean isDeflate return new ArraySlice(this, offset, length, isDeflatedZipEntry, inflatedLengthHint, nestedJarHandler); } + /** + * Load the slice as a byte array. + * + * @return the byte[] + * @throws IOException + * Signals that an I/O exception has occurred. + */ @Override public byte[] load() throws IOException { if (isDeflatedZipEntry) { @@ -81,6 +130,11 @@ public byte[] load() throws IOException { } } + /** + * Return a new random access reader. + * + * @return the random access reader + */ @Override public RandomAccessReader randomAccessReader() { return new RandomAccessArrayReader(arr, (int) sliceStartPos, (int) sliceLength); 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 4686456a8..a18b2a955 100644 --- a/src/main/java/nonapi/io/github/classgraph/fileslice/FileSlice.java +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/FileSlice.java @@ -34,10 +34,12 @@ import java.io.InputStream; import java.io.RandomAccessFile; import java.nio.ByteBuffer; +import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; import java.util.concurrent.atomic.AtomicBoolean; +import io.github.classgraph.ClassGraph; import nonapi.io.github.classgraph.fastzipfilereader.NestedJarHandler; import nonapi.io.github.classgraph.fileslice.reader.RandomAccessByteBufferReader; import nonapi.io.github.classgraph.fileslice.reader.RandomAccessFileReader; @@ -53,19 +55,41 @@ public class FileSlice extends Slice implements Closeable { /** The {@link RandomAccessFile} opened on the {@link File}. */ public RandomAccessFile raf; + /** The file length. */ private final long fileLength; + /** The file channel. */ private FileChannel fileChannel; + /** The backing byte buffer, if any. */ private ByteBuffer backingByteBuffer; + /** True if this is a top level file slice. */ private final boolean isTopLevelFileSlice; + /** The nested jar handler. */ private final NestedJarHandler nestedJarHandler; + /** True if {@link #close} has been called. */ private final AtomicBoolean isClosed = new AtomicBoolean(); - /** Constructor for sub-slice of file. */ + /** + * Constructor for treating a range of a file as a slice. + * + * @param parentSlice + * the parent slice + * @param offset + * the offset of the sub-slice within the parent slice + * @param length + * the length of the sub-slice + * @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 + */ private FileSlice(final FileSlice parentSlice, final long offset, final long length, final boolean isDeflatedZipEntry, final long inflatedLengthHint, final NestedJarHandler nestedJarHandler) { @@ -90,7 +114,18 @@ private FileSlice(final FileSlice parentSlice, final long offset, final long len /** * Constructor for toplevel file slice. - * + * + * @param file + * the file + * @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 log + * the log * @throws IOException * if the file cannot be opened. */ @@ -133,7 +168,13 @@ public FileSlice(final File file, final boolean isDeflatedZipEntry, final long i /** * Constructor for toplevel file slice. - * + * + * @param file + * the file + * @param nestedJarHandler + * the nested jar handler + * @param log + * the log * @throws IOException * if the file cannot be opened. */ @@ -142,7 +183,20 @@ public FileSlice(final File file, final NestedJarHandler nestedJarHandler, final this(file, /* isDeflatedZipEntry = */ false, /* inflatedSizeHint = */ 0L, nestedJarHandler, log); } - /** Slice the file. */ + /** + * Slice the file. + * + * @param offset + * the offset of the sub-slice within the parent slice + * @param length + * the length of the sub-slice + * @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. + * @return the slice + */ @Override public Slice slice(final long offset, final long length, final boolean isDeflatedZipEntry, final long inflatedLengthHint) { @@ -152,7 +206,11 @@ public Slice slice(final long offset, final long length, final boolean isDeflate return new FileSlice(this, offset, length, isDeflatedZipEntry, inflatedLengthHint, nestedJarHandler); } - /** Read directly from FileChannel (slow path, but handles >2GB). */ + /** + * Read directly from FileChannel (slow path, but handles >2GB). + * + * @return the random access reader + */ @Override public RandomAccessReader randomAccessReader() { if (backingByteBuffer == null) { @@ -164,6 +222,13 @@ public RandomAccessReader randomAccessReader() { } } + /** + * Load the slice as a byte array. + * + * @return the byte[] + * @throws IOException + * Signals that an I/O exception has occurred. + */ @Override public byte[] load() throws IOException { if (isDeflatedZipEntry) { @@ -189,6 +254,14 @@ public byte[] load() throws IOException { } } + /** + * 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. + */ @Override public ByteBuffer read() throws IOException { if (isDeflatedZipEntry) { @@ -210,7 +283,7 @@ public ByteBuffer read() throws IOException { } } - // TOOD: close this in NestedJarHandler and ClasspathElementDir + /** Close the slice. Unmaps any backing {@link MappedByteBuffer}. */ @Override public void close() { if (!isClosed.getAndSet(true)) { 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 ba1a1f291..4fe0d25d2 100644 --- a/src/main/java/nonapi/io/github/classgraph/fileslice/Slice.java +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/Slice.java @@ -68,7 +68,23 @@ public abstract class Slice { /** The cached hashCode. */ private int hashCode; - /** Constructor. */ + /** + * Constructor for treating a range of a slice as a sub-slice. + * + * @param parentSlice + * the parent slice + * @param offset + * the offset of the sub-slice within the parent slice + * @param length + * the length of the sub-slice + * @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 + */ protected Slice(final Slice parentSlice, final long offset, final long length, final boolean isDeflatedZipEntry, final long inflatedLengthHint, final NestedJarHandler nestedJarHandler) { this.parentSlice = parentSlice; @@ -91,7 +107,19 @@ protected Slice(final Slice parentSlice, final long offset, final long length, f } } - /** Constructor. */ + /** + * Constructor. + * + * @param length + * the length + * @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 + */ protected Slice(final long length, final boolean isDeflatedZipEntry, final long inflatedLengthHint, final NestedJarHandler nestedJarHandler) { this(/* parentSlice = */ null, 0L, length, isDeflatedZipEntry, inflatedLengthHint, nestedJarHandler); @@ -106,10 +134,10 @@ protected Slice(final long length, final boolean isDeflatedZipEntry, final long * @param length * The length of the slice. * @param isDeflatedZipEntry - * True if the slice is a deflated zip entry. + * true if this is a deflated zip entry * @param inflatedLengthHint - * If this is a deflated zip entry, the expected length of the inflated content, or -1L if unknown. - * If this is not a deflated zip entry, 0L. + * the uncompressed size of a deflated zip entry, or -1 if unknown, or 0 of this is not a deflated + * zip entry. * @return The child slice. */ public abstract Slice slice(long offset, long length, boolean isDeflatedZipEntry, @@ -117,7 +145,8 @@ public abstract Slice slice(long offset, long length, boolean isDeflatedZipEntry /** * Open this {@link Slice} as an {@link InputStream}. - * + * + * @return the input stream * @throws IOException * if an inflater cannot be created for this {@link Slice}. */ @@ -198,12 +227,20 @@ public void close() { return isDeflatedZipEntry ? nestedJarHandler.openInflaterInputStream(rawInputStream) : rawInputStream; } - /** Create a new {@link RandomAccessReader} for this {@link Slice}. */ + /** + * Create a new {@link RandomAccessReader} for this {@link Slice}. + * + * @return the random access reader + */ 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) { @@ -213,12 +250,19 @@ public ClassfileReader openClassfileReader() throws IOException { return new ClassfileReader(this); } - /** Load this {@link Slice} into a byte array. */ + /** + * Load the slice as a byte array. + * + * @return the byte[] + * @throws IOException + * Signals that an I/O exception has occurred. + */ public abstract byte[] load() throws IOException; /** - * Read this {@link Slice} as a {@link String}. - * + * Load the slice as a string. + * + * @return the string * @throws IOException * if slice cannot be read. */ @@ -226,7 +270,13 @@ public String loadAsString() throws IOException { return new String(load(), StandardCharsets.UTF_8); } - /** Read this {@link Slice} into a {@link ByteBuffer}. */ + /** + * Read the slice into a {@link ByteBuffer}. + * + * @return the byte buffer + * @throws IOException + * Signals that an I/O exception has occurred. + */ public ByteBuffer read() throws IOException { return ByteBuffer.wrap(load()); } 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 a005c31b4..b3974611a 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 @@ -153,6 +153,11 @@ public byte[] buf() { /** * Called when there is a buffer underrun to ensure there are sufficient bytes available in the array to read * the given number of bytes at the given start index. + * + * @param targetArrUsed + * the target value for {@link #arrUsed} (i.e. the number of bytes that must be filled in the array) + * @throws IOException + * Signals that an I/O exception has occurred. */ private void readTo(final int targetArrUsed) throws IOException { // Array does not need to grow larger than the length hint (if the uncompressed size of the zip entry @@ -206,7 +211,9 @@ private void readTo(final int targetArrUsed) throws IOException { /** * Ensure that the given number of bytes have been read into the buffer from the beginning of the slice. - * + * + * @param numBytes + * the number of bytes to ensure have been buffered * @throws IOException * on EOF or if the bytes could not be read. */ diff --git a/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessArrayReader.java b/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessArrayReader.java index ee7aded1a..3001c4d8a 100644 --- a/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessArrayReader.java +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessArrayReader.java @@ -40,11 +40,25 @@ * zipfile format. */ public class RandomAccessArrayReader implements RandomAccessReader { + /** The array. */ private final byte[] arr; + + /** The start index of the slice within the array. */ private final int sliceStartPos; + + /** The length of the slice within the array. */ private final int sliceLength; - /** Constructor. */ + /** + * Constructor for slicing an array. + * + * @param arr + * the array to slice. + * @param sliceStartPos + * the start index of the slice within the array. + * @param sliceLength + * the length of the slice within the array. + */ public RandomAccessArrayReader(final byte[] arr, final int sliceStartPos, final int sliceLength) { this.arr = arr; this.sliceStartPos = sliceStartPos; @@ -103,17 +117,17 @@ public byte readByte(final long offset) throws IOException { return arr[idx]; } - @Override - public short readShort(final long offset) throws IOException { - return (short) readUnsignedShort(offset); - } - @Override public int readUnsignedByte(final long offset) throws IOException { final int idx = sliceStartPos + (int) offset; return arr[idx] & 0xff; } + @Override + public short readShort(final long offset) throws IOException { + return (short) readUnsignedShort(offset); + } + @Override public int readUnsignedShort(final long offset) throws IOException { final int idx = sliceStartPos + (int) offset; diff --git a/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessByteBufferReader.java b/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessByteBufferReader.java index d17a26c62..50459e2d5 100644 --- a/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessByteBufferReader.java +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessByteBufferReader.java @@ -41,11 +41,25 @@ * zipfile format. */ public class RandomAccessByteBufferReader implements RandomAccessReader { + /** The byte buffer. */ private final ByteBuffer byteBuffer; + + /** The slice start pos. */ private final int sliceStartPos; + + /** The slice length. */ private final int sliceLength; - /** Constructor. */ + /** + * Constructor. + * + * @param byteBuffer + * the byte buffer + * @param sliceStartPos + * the slice start pos + * @param sliceLength + * the slice length + */ public RandomAccessByteBufferReader(final ByteBuffer byteBuffer, final long sliceStartPos, final long sliceLength) { this.byteBuffer = byteBuffer.duplicate(); diff --git a/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessFileReader.java b/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessFileReader.java index 5a963ec64..6e53c4488 100644 --- a/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessFileReader.java +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessFileReader.java @@ -41,15 +41,38 @@ * format. */ public class RandomAccessFileReader implements RandomAccessReader { + + /** The file channel. */ private final FileChannel fileChannel; + + /** The slice start pos. */ private final long sliceStartPos; + + /** The slice length. */ private final long sliceLength; + + /** The reusable byte buffer. */ private ByteBuffer reusableByteBuffer; + + /** The scratch arr. */ private final byte[] scratchArr = new byte[8]; + + /** The scratch byte buf. */ private final ByteBuffer scratchByteBuf = ByteBuffer.wrap(scratchArr); + + /** The utf 8 bytes. */ private byte[] utf8Bytes; - /** Constructor. */ + /** + * Constructor. + * + * @param fileChannel + * the file channel + * @param sliceStartPos + * the slice start pos + * @param sliceLength + * the slice length + */ public RandomAccessFileReader(final FileChannel fileChannel, final long sliceStartPos, final long sliceLength) { this.fileChannel = fileChannel; this.sliceStartPos = sliceStartPos; @@ -100,7 +123,6 @@ public int read(final long srcOffset, final byte[] dstArr, final int dstArrStart } } - @Override public byte readByte(final long offset) throws IOException { if (read(offset, scratchByteBuf, 0, 1) < 1) { throw new IOException("Premature EOF"); 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 fa7232984..4d9f770fa 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 @@ -151,8 +151,7 @@ public interface RandomAccessReader { * @param offset * The start offset of the string. * @param numBytes - * The number of bytes of the UTF8 encoding of the string, or if -1, read the length of the string as - * a short in the first two bytes at offset. + * The number of bytes of the UTF8 encoding of the string. * @param replaceSlashWithDot * If true, replace '/' with '.'. * @param stripLSemicolon @@ -170,8 +169,7 @@ public String readString(final long offset, final int numBytes, final boolean re * @param offset * The start offset of the string. * @param numBytes - * The number of bytes of the UTF8 encoding of the string, or if -1, read the length of the string as - * a short in the first two bytes at offset. + * The number of bytes of the UTF8 encoding of the string. * @return The string. * @throws IOException * If an I/O exception occurs. 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 62459ea10..5cf4b6835 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 @@ -110,8 +110,7 @@ public interface SequentialReader { * optionally removing the prefix "L" and the suffix ";". * * @param numBytes - * The number of bytes of the UTF8 encoding of the string, or if -1, read the length of the string as - * a short in the first two bytes at offset. + * The number of bytes of the UTF8 encoding of the string. * @param replaceSlashWithDot * If true, replace '/' with '.'. * @param stripLSemicolon @@ -127,8 +126,7 @@ public String readString(final int numBytes, final boolean replaceSlashWithDot, * Reads the "modified UTF8" format defined in the Java classfile spec. * * @param numBytes - * The number of bytes of the UTF8 encoding of the string, or if -1, read the length of the string as - * a short in the first two bytes at offset. + * The number of bytes of the UTF8 encoding of the string. * @return The string. * @throws IOException * If an I/O exception occurs. diff --git a/src/main/java/nonapi/io/github/classgraph/json/ReferenceEqualityKey.java b/src/main/java/nonapi/io/github/classgraph/json/ReferenceEqualityKey.java index b5e12a43c..8a371139c 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/ReferenceEqualityKey.java +++ b/src/main/java/nonapi/io/github/classgraph/json/ReferenceEqualityKey.java @@ -59,6 +59,11 @@ public K get() { return wrappedKey; } + /** + * Hash code. + * + * @return the int + */ /* (non-Javadoc) * @see java.lang.Object#hashCode() */ @@ -70,6 +75,12 @@ public int hashCode() { return key == null ? 0 : System.identityHashCode(key); } + /** + * Equals. + * + * @param obj the obj + * @return true, if successful + */ /* (non-Javadoc) * @see java.lang.Object#equals(java.lang.Object) */ @@ -83,6 +94,11 @@ public boolean equals(final Object obj) { return wrappedKey == ((ReferenceEqualityKey) obj).wrappedKey; } + /** + * To string. + * + * @return the string + */ /* (non-Javadoc) * @see java.lang.Object#toString() */ diff --git a/src/main/java/nonapi/io/github/classgraph/utils/StringUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/StringUtils.java index f3605e705..68e28cc02 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/StringUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/StringUtils.java @@ -43,8 +43,10 @@ private StringUtils() { * Reads the "modified UTF8" format defined in the Java classfile spec, optionally replacing '/' with '.', and * optionally removing the prefix "L" and the suffix ";". * + * @param arr + * the array to read the string from * @param startOffset - * The start offset of the string. + * The start offset of the string within the array. * @param numBytes * The number of bytes of the UTF8 encoding of the string. * @param replaceSlashWithDot diff --git a/src/test/java/com/xyz/fig/SceneGraph.java b/src/test/java/com/xyz/fig/SceneGraph.java index f19136dbe..9b4705a2e 100644 --- a/src/test/java/com/xyz/fig/SceneGraph.java +++ b/src/test/java/com/xyz/fig/SceneGraph.java @@ -22,6 +22,11 @@ public void addShape(final Shape shape) { shapes.add(shape); } + /** + * Draw. + * + * @param g the g + */ /* (non-Javadoc) * @see com.xyz.fig.Drawable#draw(java.awt.Graphics2D) */ From 6fd0dff4da2ed9c49b3aec699ddbcb88628b1a2f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 16 Feb 2020 04:04:02 -0700 Subject: [PATCH 0620/1778] Refactoring --- .../fastzipfilereader/NestedJarHandler.java | 37 +++++++++++ .../fastzipfilereader/RecyclableInflater.java | 64 ------------------- 2 files changed, 37 insertions(+), 64 deletions(-) delete mode 100644 src/main/java/nonapi/io/github/classgraph/fastzipfilereader/RecyclableInflater.java 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 1ab61a083..617f9bfe2 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java @@ -66,6 +66,7 @@ import nonapi.io.github.classgraph.fileslice.FileSlice; 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.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.FastPathResolver; import nonapi.io.github.classgraph.utils.FileUtils; @@ -561,6 +562,40 @@ private PhysicalZipFile downloadJarFromURL(final String jarURL, final LogNode lo // ------------------------------------------------------------------------------------------------------------- + /** + * 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). + */ + private final Inflater inflater = new Inflater(/* nowrap = */ true); + + /** + * Get the {@link Inflater} instance. + * + * @return the {@link Inflater} instance. + */ + public Inflater getInflater() { + return inflater; + } + + /** + * 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. */ + @Override + public void close() { + inflater.end(); + } + } + /** * Wrap an {@link InputStream} with an {@link InflaterInputStream}, recycling the {@link Inflater} instance. * @@ -709,6 +744,8 @@ public void close() { }; } + // ------------------------------------------------------------------------------------------------------------- + /** * Read all the bytes in an {@link InputStream}, with spillover to a temporary file on disk if a maximum buffer * size is exceeded. diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/RecyclableInflater.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/RecyclableInflater.java deleted file mode 100644 index b56b995fd..000000000 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/RecyclableInflater.java +++ /dev/null @@ -1,64 +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.fastzipfilereader; - -import java.util.zip.Inflater; - -import nonapi.io.github.classgraph.recycler.Recycler; -import nonapi.io.github.classgraph.recycler.Resettable; - -/** - * Wrapper class that allows an {@link Inflater} instance to be reset for reuse and then recycled by a - * {@link Recycler}. - */ -public class RecyclableInflater implements Resettable, AutoCloseable { - /** Create a new {@link Inflater} instance with the "nowrap" option (which is needed for zipfile entries). */ - private final Inflater inflater = new Inflater(/* nowrap = */ true); - - /** - * Get the {@link Inflater} instance. - * - * @return the {@link Inflater} instance. - */ - public Inflater getInflater() { - return inflater; - } - - /** 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. */ - @Override - public void close() { - inflater.end(); - } -} \ No newline at end of file From a0e40281b10ba73574abb2bcf62237e2c759d003 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 16 Feb 2020 04:04:22 -0700 Subject: [PATCH 0621/1778] Source > Cleanup --- src/main/java/io/github/classgraph/Resource.java | 6 ++++-- src/main/java/io/github/classgraph/Scanner.java | 12 ++++++++---- .../classgraph/fastzipfilereader/LogicalZipFile.java | 2 +- .../fileslice/reader/RandomAccessFileReader.java | 1 + .../github/classgraph/json/ReferenceEqualityKey.java | 3 ++- src/test/java/com/xyz/fig/SceneGraph.java | 3 ++- 6 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/main/java/io/github/classgraph/Resource.java b/src/main/java/io/github/classgraph/Resource.java index ee7efc90c..cd2fcf064 100644 --- a/src/main/java/io/github/classgraph/Resource.java +++ b/src/main/java/io/github/classgraph/Resource.java @@ -353,7 +353,8 @@ public int hashCode() { /** * Equals. * - * @param obj the obj + * @param obj + * the obj * @return true, if successful */ /* (non-Javadoc) @@ -372,7 +373,8 @@ public boolean equals(final Object obj) { /** * Compare to. * - * @param o the o + * @param o + * the o * @return the int */ /* (non-Javadoc) diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index b637947af..b14f71b99 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -634,10 +634,14 @@ public ClassfileScannerWorkUnitProcessor(final ScanSpec scanSpec, /** * Process work unit. * - * @param workUnit the work unit - * @param workQueue the work queue - * @param log the log - * @throws InterruptedException the interrupted exception + * @param workUnit + * the work unit + * @param workQueue + * the work queue + * @param log + * the log + * @throws InterruptedException + * the interrupted exception */ /* (non-Javadoc) * @see nonapi.io.github.classgraph.concurrency.WorkQueue.WorkUnitProcessor#processWorkUnit( 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 09e59370a..3b2ac6ef1 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java @@ -433,7 +433,7 @@ private void readCentralDirectory(final LogNode log) throws IOException, Interru if (slice.sliceLength < 22) { throw new IOException("Zipfile too short to have a central directory"); } - + final RandomAccessReader reader = slice.randomAccessReader(); // Scan for End Of Central Directory (EOCD) signature. Final comment can be up to 64kB in length, diff --git a/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessFileReader.java b/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessFileReader.java index 6e53c4488..0ce2bfa2f 100644 --- a/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessFileReader.java +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessFileReader.java @@ -123,6 +123,7 @@ public int read(final long srcOffset, final byte[] dstArr, final int dstArrStart } } + @Override public byte readByte(final long offset) throws IOException { if (read(offset, scratchByteBuf, 0, 1) < 1) { throw new IOException("Premature EOF"); diff --git a/src/main/java/nonapi/io/github/classgraph/json/ReferenceEqualityKey.java b/src/main/java/nonapi/io/github/classgraph/json/ReferenceEqualityKey.java index 8a371139c..4cd6aadeb 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/ReferenceEqualityKey.java +++ b/src/main/java/nonapi/io/github/classgraph/json/ReferenceEqualityKey.java @@ -78,7 +78,8 @@ public int hashCode() { /** * Equals. * - * @param obj the obj + * @param obj + * the obj * @return true, if successful */ /* (non-Javadoc) diff --git a/src/test/java/com/xyz/fig/SceneGraph.java b/src/test/java/com/xyz/fig/SceneGraph.java index 9b4705a2e..0159885be 100644 --- a/src/test/java/com/xyz/fig/SceneGraph.java +++ b/src/test/java/com/xyz/fig/SceneGraph.java @@ -25,7 +25,8 @@ public void addShape(final Shape shape) { /** * Draw. * - * @param g the g + * @param g + * the g */ /* (non-Javadoc) * @see com.xyz.fig.Drawable#draw(java.awt.Graphics2D) From ffd2e37980cb775ec04e75dd70fdd4b2a592044d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 16 Feb 2020 04:50:54 -0700 Subject: [PATCH 0622/1778] Small cleanups --- .../classgraph/ClasspathElementDir.java | 25 +++++++-------- .../classgraph/ClasspathElementModule.java | 32 +++++++++---------- .../classgraph/ClasspathElementZip.java | 18 +++++------ .../java/io/github/classgraph/Resource.java | 4 --- .../fastzipfilereader/LogicalZipFile.java | 30 +++++++++++++---- .../fastzipfilereader/NestedJarHandler.java | 25 ++++++--------- .../fastzipfilereader/ZipFileSlice.java | 2 +- .../classgraph/fileslice/ArraySlice.java | 10 ++++++ .../classgraph/fileslice/FileSlice.java | 10 ++++++ .../io/github/classgraph/fileslice/Slice.java | 2 +- 10 files changed, 90 insertions(+), 68 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementDir.java b/src/main/java/io/github/classgraph/ClasspathElementDir.java index ad3303393..8b71c5930 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementDir.java @@ -39,6 +39,7 @@ import java.util.Arrays; import java.util.HashSet; import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; import io.github.classgraph.Scanner.ClasspathEntryWorkUnit; import nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandlerRegistry; @@ -159,16 +160,17 @@ void open(final WorkQueue workQueue, final LogNode log) * the {@link File} for the resource * @param nestedJarHandler * the nested jar handler - * @param log - * the log * @return the resource */ private Resource newResource(final String relativePath, final File resourceFile, - final NestedJarHandler nestedJarHandler, final LogNode log) { + 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. */ + protected AtomicBoolean isOpen = new AtomicBoolean(); + @Override public String getPath() { return relativePath; @@ -247,21 +249,19 @@ public InputStream open() throws IOException { @Override public byte[] load() throws IOException { - try { - read(); + 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; - } finally { - close(); } } @Override public void close() { super.close(); // Close inputStream - if (isOpen.get()) { + if (isOpen.getAndSet(false)) { if (byteBuffer != null) { // Any ByteBuffer ref should be a duplicate, so it doesn't need to be cleaned byteBuffer = null; @@ -271,7 +271,6 @@ public void close() { nestedJarHandler.markFileSliceAsClosed(fileSlice); fileSlice = null; } - isOpen.getAndSet(false); } } }; @@ -288,8 +287,7 @@ public void close() { @Override Resource getResource(final String relativePath) { final File resourceFile = new File(classpathEltDir, relativePath); - return FileUtils.canReadAndIsFile(resourceFile) - ? newResource(relativePath, resourceFile, nestedJarHandler, /* log = */ null) + return FileUtils.canReadAndIsFile(resourceFile) ? newResource(relativePath, resourceFile, nestedJarHandler) : null; } @@ -400,8 +398,7 @@ private void scanDirRecursively(final File dir, final LogNode log) { || (parentMatchStatus == ScanSpecPathMatch.AT_WHITELISTED_CLASS_PACKAGE && scanSpec.classfileIsSpecificallyWhitelisted(fileInDirRelativePath))) { // Resource is whitelisted - final Resource resource = newResource(fileInDirRelativePath, fileInDir, nestedJarHandler, - subLog); + final Resource resource = newResource(fileInDirRelativePath, fileInDir, nestedJarHandler); addWhitelistedResource(resource, parentMatchStatus, /* isClassfileOnly = */ false, subLog); // Save last modified time @@ -417,7 +414,7 @@ private void scanDirRecursively(final File dir, final LogNode log) { // Always check for module descriptor in package root, even if package root isn't in whitelist for (final File fileInDir : filesInDir) { if (fileInDir.getName().equals("module-info.class") && fileInDir.isFile()) { - final Resource resource = newResource("module-info.class", fileInDir, nestedJarHandler, subLog); + final Resource resource = newResource("module-info.class", fileInDir, nestedJarHandler); addWhitelistedResource(resource, parentMatchStatus, /* isClassfileOnly = */ true, subLog); fileToLastModified.put(fileInDir, fileInDir.lastModified()); } diff --git a/src/main/java/io/github/classgraph/ClasspathElementModule.java b/src/main/java/io/github/classgraph/ClasspathElementModule.java index ba3019986..2e2520355 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementModule.java +++ b/src/main/java/io/github/classgraph/ClasspathElementModule.java @@ -37,6 +37,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; import io.github.classgraph.Scanner.ClasspathEntryWorkUnit; import nonapi.io.github.classgraph.concurrency.SingletonMap; @@ -125,6 +126,9 @@ private Resource newResource(final String resourcePath) { /** The module reader proxy. */ private ModuleReaderProxy moduleReaderProxy; + /** True if the resource is open. */ + protected AtomicBoolean isOpen = new AtomicBoolean(); + @Override public String getPath() { return resourcePath; @@ -199,8 +203,8 @@ public InputStream open() throws IOException { @Override public byte[] load() throws IOException { - try { - read(); + read(); + try (Resource res = this) { // Close this after use final byte[] byteArray; if (byteBuffer.hasArray() && byteBuffer.position() == 0 && byteBuffer.limit() == byteBuffer.capacity()) { @@ -211,27 +215,23 @@ public byte[] load() throws IOException { } length = byteArray.length; return byteArray; - } finally { - close(); } } @Override public void close() { super.close(); // Close inputStream - if (isOpen.get()) { - if (moduleReaderProxy != null) { - if (byteBuffer != null) { - // Release any open ByteBuffer - moduleReaderProxy.release(byteBuffer); - } - // 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; + if (isOpen.getAndSet(false) && moduleReaderProxy != null) { + if (byteBuffer != null) { + // Release any open ByteBuffer + moduleReaderProxy.release(byteBuffer); } + // 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; } } }; diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index df6b11e05..eb464e14f 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -40,6 +40,7 @@ import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; import io.github.classgraph.Scanner.ClasspathEntryWorkUnit; import nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandlerRegistry; @@ -289,6 +290,9 @@ 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(); + /** * Path with package root prefix and/or any Spring Boot prefix ("BOOT-INF/classes/" or * "WEB-INF/classes/") removed. @@ -403,24 +407,20 @@ public byte[] load() throws IOException { throw new IOException( "Resource is already open -- cannot open it again without first calling close()"); } - try { + try (Resource res = this) { // Close this after use final byte[] byteArray = zipEntry.getSlice().load(); length = byteArray.length; return byteArray; - } finally { - close(); } } @Override public void close() { super.close(); // Close inputStream - 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; - } + 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; } } }; diff --git a/src/main/java/io/github/classgraph/Resource.java b/src/main/java/io/github/classgraph/Resource.java index cd2fcf064..ec4db36ee 100644 --- a/src/main/java/io/github/classgraph/Resource.java +++ b/src/main/java/io/github/classgraph/Resource.java @@ -40,7 +40,6 @@ import java.nio.charset.StandardCharsets; import java.nio.file.attribute.PosixFilePermission; import java.util.Set; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.zip.ZipEntry; import nonapi.io.github.classgraph.fileslice.reader.ClassfileReader; @@ -64,9 +63,6 @@ public abstract class Resource implements Closeable, Comparable { /** The length, or -1L for unknown. */ protected long length; - /** True if the resource is open. */ - protected AtomicBoolean isOpen = new AtomicBoolean(); - /** The cached result of toString(). */ private String toString; 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 3b2ac6ef1..d29848020 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java @@ -140,6 +140,8 @@ public class LogicalZipFile extends ZipFileSlice { * * @param zipFileSlice * the zipfile slice + * @param nestedJarHandler + * the nested jar handler * @param log * the log * @throws IOException @@ -147,9 +149,10 @@ public class LogicalZipFile extends ZipFileSlice { * @throws InterruptedException * if the thread was interrupted. */ - LogicalZipFile(final ZipFileSlice zipFileSlice, final LogNode log) throws IOException, InterruptedException { + LogicalZipFile(final ZipFileSlice zipFileSlice, final NestedJarHandler nestedJarHandler, final LogNode log) + throws IOException, InterruptedException { super(zipFileSlice); - readCentralDirectory(log); + readCentralDirectory(nestedJarHandler, log); } // ------------------------------------------------------------------------------------------------------------- @@ -421,7 +424,9 @@ private void parseManifest(final FastZipEntry manifestZipEntry, final LogNode lo /** * Read the central directory of the zipfile. - * + * + * @param nestedJarHandler + * the nested jar handler * @param log * the log * @throws IOException @@ -429,7 +434,8 @@ private void parseManifest(final FastZipEntry manifestZipEntry, final LogNode lo * @throws InterruptedException * if the thread was interrupted. */ - private void readCentralDirectory(final LogNode log) throws IOException, InterruptedException { + private void readCentralDirectory(final NestedJarHandler nestedJarHandler, final LogNode log) + throws IOException, InterruptedException { if (slice.sliceLength < 22) { throw new IOException("Zipfile too short to have a central directory"); } @@ -449,7 +455,7 @@ private void readCentralDirectory(final LogNode log) throws IOException, Interru if (eocdPos < 0) { // If EOCD signature was not found, read the last 64kB of file to RAM in a single chunk // so that we can scan back through it at higher speed to locate the EOCD signature - final int bytesToRead = (int) Math.min(slice.sliceLength, 22 + (1 << 16)); + final int bytesToRead = (int) Math.min(slice.sliceLength, 22L + (1 << 16)); final byte[] eocdBytes = new byte[bytesToRead]; final long readStartOff = slice.sliceLength - bytesToRead; if (reader.read(readStartOff, eocdBytes, 0, bytesToRead) < bytesToRead) { @@ -457,7 +463,7 @@ private void readCentralDirectory(final LogNode log) throws IOException, Interru throw new IOException("Zipfile is truncated"); } final RandomAccessReader eocdReader = new ArraySlice(eocdBytes, /* isDeflatedZipEntry = */ false, - /* inflatedLengthHint = */ 0L, physicalZipFile.nestedJarHandler).randomAccessReader(); + /* inflatedLengthHint = */ 0L, nestedJarHandler).randomAccessReader(); for (long i = eocdBytes.length - 22; i >= 0; --i) { if (eocdReader.readInt(i) == 0x06054b50) { eocdPos = i + readStartOff; @@ -550,7 +556,7 @@ private void readCentralDirectory(final LogNode log) throws IOException, Interru throw new IOException("Zipfile is truncated"); } cenReader = new ArraySlice(entryBytes, /* isDeflatedZipEntry = */ false, /* inflatedSizeHint = */ 0L, - physicalZipFile.nestedJarHandler).randomAccessReader(); + nestedJarHandler).randomAccessReader(); } if (numEnt == -1L) { @@ -823,6 +829,16 @@ private void readCentralDirectory(final LogNode log) throws IOException, Interru // ------------------------------------------------------------------------------------------------------------- + @Override + public boolean equals(final Object o) { + return super.equals(o); + } + + @Override + public int hashCode() { + return super.hashCode(); + } + /* (non-Javadoc) * @see nonapi.io.github.classgraph.fastzipfilereader.ZipFileSlice#toString() */ 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 617f9bfe2..e9fbabc10 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java @@ -137,7 +137,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, log); + return new LogicalZipFile(zipFileSlice, NestedJarHandler.this, log); } }; @@ -630,7 +630,7 @@ public int read() throws IOException { } @Override - public int read(final byte outBuf[], final int off, final int len) throws IOException { + public int read(final byte[] outBuf, final int off, final int len) throws IOException { if (closed.get()) { throw new IOException("Already closed"); } else if (len < 0) { @@ -901,20 +901,13 @@ public static byte[] readAllBytesAsArray(final InputStream inputStream, final lo break; } - // Reached end of stream, and buf is full - final int extraByte; - try { - // 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. - extraByte = inptStream.read(); - if (extraByte == -1) { - // Reached end of stream - break; - } - } catch (final ZipException e) { - // FIXME temp - throw new RuntimeException(e); + // 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) { + // Reached end of stream + break; } // Haven't reached end of stream yet. Need to grow the buffer (double its size), and append diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSlice.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSlice.java index d533a19f3..09c49548b 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSlice.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSlice.java @@ -40,7 +40,7 @@ public class ZipFileSlice { /** The parent slice, or null if this is the toplevel slice (the whole zipfile). */ private final ZipFileSlice parentZipFileSlice; /** The underlying physical zipfile. */ - public final PhysicalZipFile physicalZipFile; + protected final PhysicalZipFile physicalZipFile; /** For the toplevel zipfile slice, the zipfile path; For nested slices, the name/path of the zipfile entry. */ private final String pathWithinParentZipFileSlice; /** The {@link Slice} containing the zipfile. */ diff --git a/src/main/java/nonapi/io/github/classgraph/fileslice/ArraySlice.java b/src/main/java/nonapi/io/github/classgraph/fileslice/ArraySlice.java index 829e8dad8..7937de6c7 100644 --- a/src/main/java/nonapi/io/github/classgraph/fileslice/ArraySlice.java +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/ArraySlice.java @@ -139,4 +139,14 @@ public byte[] load() throws IOException { public RandomAccessReader randomAccessReader() { return new RandomAccessArrayReader(arr, (int) sliceStartPos, (int) sliceLength); } + + @Override + public boolean equals(final Object o) { + return super.equals(o); + } + + @Override + public int hashCode() { + return super.hashCode(); + } } \ No newline at end of file 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 a18b2a955..bcd7560ea 100644 --- a/src/main/java/nonapi/io/github/classgraph/fileslice/FileSlice.java +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/FileSlice.java @@ -283,6 +283,16 @@ public ByteBuffer read() throws IOException { } } + @Override + public boolean equals(final Object o) { + return super.equals(o); + } + + @Override + public int hashCode() { + return super.hashCode(); + } + /** Close the slice. Unmaps any backing {@link MappedByteBuffer}. */ @Override public void 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 4fe0d25d2..60e0802f3 100644 --- a/src/main/java/nonapi/io/github/classgraph/fileslice/Slice.java +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/Slice.java @@ -169,7 +169,7 @@ public int read() throws IOException { // InputStream's default implementation of this method is very slow -- it calls read() // for every byte. This method reads the maximum number of bytes possible in one call. @Override - public int read(final byte buf[], final int off, final int len) throws IOException { + public int read(final byte[] buf, final int off, final int len) throws IOException { if (closed.get()) { throw new IOException("Already closed"); } else if (len == 0) { From 0c6bf3ebf82fb1a2a912e1a9700ed5afb3585ec0 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 16 Feb 2020 04:56:58 -0700 Subject: [PATCH 0623/1778] [maven-release-plugin] prepare release classgraph-4.8.63 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 120ddfd16..90394b54c 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.63-SNAPSHOT + 4.8.63 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.56 + classgraph-4.8.63 From 655f96d67256b3c2c6bbbb5b9e949ced1e79bd6c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 16 Feb 2020 04:57:05 -0700 Subject: [PATCH 0624/1778] [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 90394b54c..852624045 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.63 + 4.8.64-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.63 + classgraph-4.8.56 From 6e4f9a1f2ded8d57de51c27b897c07380123c3f6 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 16 Feb 2020 05:36:42 -0700 Subject: [PATCH 0625/1778] Update github stars URL --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9d71287ac..9fdcef83c 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ ClassGraph is an uber-fast, ultra-lightweight, parallelized classpath scanner an [![License: MIT](https://img.shields.io/badge/license-MIT-lightgrey.svg)](https://github.com/classgraph/classgraph/blob/master/LICENSE) [![Dependencies: none](https://img.shields.io/badge/dependencies-none-lightgrey.svg)](#)
-[![GitHub stars chart](https://img.shields.io/badge/github%20stars-chart-yellow.svg)](https://seladb.github.io/StarTrack-js/?u=classgraph&r=classgraph) +[![GitHub stars chart](https://img.shields.io/badge/github%20stars-chart-yellow.svg)](https://seladb.github.io/StarTrack-js/#/preload?r=classgraph,classgraph) [![Gitter chat](https://img.shields.io/badge/gitter-join%20chat-yellow.svg)](https://gitter.im/classgraph/Lobby) | ClassGraph is now fully stable. This project adheres to the **[Zero Bugs Commitment](https://github.com/classgraph/classgraph/blob/master/Zero-Bugs-Commitment.md)**. | From 1f1dd2c30739ce0c674a9aef239c6d6c0db4b9c1 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 16 Feb 2020 05:39:30 -0700 Subject: [PATCH 0626/1778] Update Javadoc --- src/main/java/io/github/classgraph/ClassGraph.java | 6 +++--- .../java/nonapi/io/github/classgraph/scanspec/ScanSpec.java | 6 ++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index d4340db51..52ebe01eb 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -30,11 +30,11 @@ import java.io.File; import java.io.InputStream; -import java.io.RandomAccessFile; import java.net.URI; import java.net.URL; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; @@ -1128,8 +1128,8 @@ public ClassGraph setMaxBufferedJarRAMSize(final int maxBufferedJarRAMSize) { } /** - * If true, use a {@link MappedByteBuffer} rather than the {@link RandomAccessFile} API to open files, which may - * be faster, but uses up virtual memory space. + * If true, use a {@link MappedByteBuffer} rather than the {@link FileChannel} API to open files, which may be + * faster, but uses up virtual memory space. * * @return this (for method chaining). */ 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 49be638cd..d91be8172 100644 --- a/src/main/java/nonapi/io/github/classgraph/scanspec/ScanSpec.java +++ b/src/main/java/nonapi/io/github/classgraph/scanspec/ScanSpec.java @@ -29,12 +29,12 @@ package nonapi.io.github.classgraph.scanspec; import java.io.InputStream; -import java.io.RandomAccessFile; import java.lang.reflect.Field; import java.net.URI; import java.net.URL; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; @@ -255,9 +255,7 @@ public class ScanSpec { */ public int maxBufferedJarRAMSize = 64 * 1024 * 1024; - /** - * If true, use a {@link MappedByteBuffer} rather than the {@link RandomAccessFile} API to access file content. - */ + /** If true, use a {@link MappedByteBuffer} rather than the {@link FileChannel} API to access file content. */ public boolean enableMemoryMapping; // ------------------------------------------------------------------------------------------------------------- From f6edf37ec41ba594736f7dc3739634180fb2449c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 16 Feb 2020 05:40:12 -0700 Subject: [PATCH 0627/1778] Update Javadoc --- 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 52ebe01eb..44f5eaca0 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -1129,7 +1129,7 @@ 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, but uses up virtual memory space. + * faster for large classpaths consisting of many large jarfiles, but uses up virtual memory space. * * @return this (for method chaining). */ From f10087a65b2154b5362183ad5690e8233480cbaf Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 16 Feb 2020 13:39:41 -0700 Subject: [PATCH 0628/1778] Fix JDK 7/8 compatibility (#403) --- .../classgraph/fileslice/FileSlice.java | 5 +++-- .../fileslice/reader/ClassfileReader.java | 5 +++-- .../reader/RandomAccessArrayReader.java | 5 +++-- .../reader/RandomAccessByteBufferReader.java | 19 ++++++++++--------- .../reader/RandomAccessFileReader.java | 5 +++-- 5 files changed, 22 insertions(+), 17 deletions(-) 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 bcd7560ea..708a72c37 100644 --- a/src/main/java/nonapi/io/github/classgraph/fileslice/FileSlice.java +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/FileSlice.java @@ -33,6 +33,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; +import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; @@ -104,8 +105,8 @@ private FileSlice(final FileSlice parentSlice, final long offset, final long len if (parentSlice.backingByteBuffer != null) { // Duplicate and slice the backing byte buffer, if there is one this.backingByteBuffer = parentSlice.backingByteBuffer.duplicate(); - this.backingByteBuffer.position((int) sliceStartPos); - this.backingByteBuffer.limit((int) (sliceStartPos + sliceLength)); + ((Buffer) this.backingByteBuffer).position((int) sliceStartPos); + ((Buffer) this.backingByteBuffer).limit((int) (sliceStartPos + sliceLength)); } // Only mark toplevel file slices as open (sub slices don't need to be marked as open since 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 b3974611a..ac005134d 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 @@ -32,6 +32,7 @@ import java.io.Closeable; import java.io.IOException; import java.io.InputStream; +import java.nio.Buffer; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.nio.ReadOnlyBufferException; @@ -260,8 +261,8 @@ public int read(final long srcOffset, final ByteBuffer dstBuf, final int dstBufS return -1; } try { - dstBuf.position(dstBufStart); - dstBuf.limit(dstBufStart + numBytesToRead); + ((Buffer) dstBuf).position(dstBufStart); + ((Buffer) dstBuf).limit(dstBufStart + numBytesToRead); dstBuf.put(arr, idx, numBytesToRead); return numBytesToRead; } catch (BufferUnderflowException | IndexOutOfBoundsException | ReadOnlyBufferException e) { diff --git a/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessArrayReader.java b/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessArrayReader.java index 3001c4d8a..87fafbbe3 100644 --- a/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessArrayReader.java +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessArrayReader.java @@ -29,6 +29,7 @@ package nonapi.io.github.classgraph.fileslice.reader; import java.io.IOException; +import java.nio.Buffer; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.nio.ReadOnlyBufferException; @@ -102,8 +103,8 @@ public int read(final long srcOffset, final ByteBuffer dstBuf, final int dstBufS return -1; } final int srcStart = (int) (sliceStartPos + srcOffset); - dstBuf.position(dstBufStart); - dstBuf.limit(dstBufStart + numBytesToRead); + ((Buffer) dstBuf).position(dstBufStart); + ((Buffer) dstBuf).limit(dstBufStart + numBytesToRead); dstBuf.put(arr, srcStart, numBytesToRead); return numBytesToRead; } catch (BufferUnderflowException | IndexOutOfBoundsException | ReadOnlyBufferException e) { diff --git a/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessByteBufferReader.java b/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessByteBufferReader.java index 50459e2d5..a981dfa69 100644 --- a/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessByteBufferReader.java +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessByteBufferReader.java @@ -29,6 +29,7 @@ package nonapi.io.github.classgraph.fileslice.reader; import java.io.IOException; +import java.nio.Buffer; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -66,8 +67,8 @@ public RandomAccessByteBufferReader(final ByteBuffer byteBuffer, final long slic this.byteBuffer.order(ByteOrder.LITTLE_ENDIAN); this.sliceStartPos = (int) sliceStartPos; this.sliceLength = (int) sliceLength; - this.byteBuffer.position(this.sliceStartPos); - this.byteBuffer.limit(this.sliceStartPos + this.sliceLength); + ((Buffer) this.byteBuffer).position(this.sliceStartPos); + ((Buffer) this.byteBuffer).limit(this.sliceStartPos + this.sliceLength); } @Override @@ -85,9 +86,9 @@ public int read(final long srcOffset, final byte[] dstArr, final int dstArrStart return -1; } final int srcStart = (int) srcOffset; - byteBuffer.position(sliceStartPos + srcStart); + ((Buffer) byteBuffer).position(sliceStartPos + srcStart); byteBuffer.get(dstArr, dstArrStart, numBytesToRead); - byteBuffer.position(sliceStartPos); + ((Buffer) byteBuffer).position(sliceStartPos); return numBytesToRead; } catch (final IndexOutOfBoundsException e) { throw new IOException("Read index out of bounds"); @@ -109,12 +110,12 @@ public int read(final long srcOffset, final ByteBuffer dstBuf, final int dstBufS return -1; } final int srcStart = (int) (sliceStartPos + srcOffset); - byteBuffer.position(srcStart); - dstBuf.position(dstBufStart); - dstBuf.limit(dstBufStart + numBytesToRead); + ((Buffer) byteBuffer).position(srcStart); + ((Buffer) dstBuf).position(dstBufStart); + ((Buffer) dstBuf).limit(dstBufStart + numBytesToRead); dstBuf.put(byteBuffer); - byteBuffer.limit(sliceStartPos + sliceLength); - byteBuffer.position(sliceStartPos); + ((Buffer) byteBuffer).limit(sliceStartPos + sliceLength); + ((Buffer) byteBuffer).position(sliceStartPos); return numBytesToRead; } catch (BufferUnderflowException | IndexOutOfBoundsException | ReadOnlyBufferException e) { throw new IOException("Read index out of bounds"); diff --git a/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessFileReader.java b/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessFileReader.java index 0ce2bfa2f..7621a4eb6 100644 --- a/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessFileReader.java +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessFileReader.java @@ -30,6 +30,7 @@ import java.io.File; import java.io.IOException; +import java.nio.Buffer; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; @@ -90,8 +91,8 @@ public int read(final long srcOffset, final ByteBuffer dstBuf, final int dstBufS throw new IOException("Read index out of bounds"); } final long srcStart = sliceStartPos + srcOffset; - dstBuf.position(dstBufStart); - dstBuf.limit(dstBufStart + numBytes); + ((Buffer) dstBuf).position(dstBufStart); + ((Buffer) dstBuf).limit(dstBufStart + numBytes); final int numBytesRead = fileChannel.read(dstBuf, srcStart); return numBytesRead == 0 ? -1 : numBytesRead; From cc942fb35fac837a654ebc3902133c350c426e35 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 16 Feb 2020 13:40:22 -0700 Subject: [PATCH 0629/1778] [maven-release-plugin] prepare release classgraph-4.8.64 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 852624045..50258e0ff 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.64-SNAPSHOT + 4.8.64 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.56 + classgraph-4.8.64 From db30fb54a93e3c4f98bc64ac5255c4758299f2e3 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 16 Feb 2020 13:40:29 -0700 Subject: [PATCH 0630/1778] [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 50258e0ff..111d81b52 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.64 + 4.8.65-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.64 + classgraph-4.8.56 From cc1701162b57899c895422bb85a11f688e43789f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 16 Feb 2020 16:40:52 -0700 Subject: [PATCH 0631/1778] Update plugin and dependency versions to latest --- pom.xml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pom.xml b/pom.xml index 111d81b52..f3b59ef77 100644 --- a/pom.xml +++ b/pom.xml @@ -69,25 +69,25 @@ org.junit.jupiter junit-jupiter - 5.6.0-M1 + 5.6.0 test org.openjdk.jmh jmh-core - 1.22 + 1.23 test org.openjdk.jmh jmh-generator-annprocess - 1.22 + 1.23 test org.assertj assertj-core - 3.14.0 + 3.15.0 test @@ -130,7 +130,7 @@ org.eclipse.jdt org.eclipse.jdt.annotation - 2.2.300 + 2.2.400 provided @@ -178,7 +178,7 @@ org.apache.maven.plugins maven-source-plugin - 3.2.0 + 3.2.1 org.apache.maven.plugins @@ -198,7 +198,7 @@ org.apache.maven.plugins maven-release-plugin - 2.5.3 + 3.0.0-M1 From b0a196216ddadeacfd47dd487706af3a39a7eb12 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 16 Feb 2020 16:42:45 -0700 Subject: [PATCH 0632/1778] Remove warning override --- pom.xml | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index f3b59ef77..94ab7bf66 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,7 @@ - + 4.0.0 io.github.classgraph @@ -198,7 +201,7 @@ org.apache.maven.plugins maven-release-plugin - 3.0.0-M1 + 3.0.0-M1 @@ -261,7 +264,8 @@ - + org.codehaus.mojo.signature @@ -340,8 +344,13 @@ - - + + From 461fe4abff5e39c7e773809cbcf29d7552b45936 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 16 Feb 2020 16:46:08 -0700 Subject: [PATCH 0633/1778] List JDK version number when warning that test is skipped --- .../github/classgraph/features/MultiReleaseJarTest.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/test/java/io/github/classgraph/features/MultiReleaseJarTest.java b/src/test/java/io/github/classgraph/features/MultiReleaseJarTest.java index 39837714a..c410b22af 100644 --- a/src/test/java/io/github/classgraph/features/MultiReleaseJarTest.java +++ b/src/test/java/io/github/classgraph/features/MultiReleaseJarTest.java @@ -35,7 +35,8 @@ public class MultiReleaseJarTest { public void multiReleaseJar() throws Exception { if (VersionFinder.JAVA_MAJOR_VERSION < 9) { // Multi-release jar sections are ignored by ClassGraph if JDK<9 - System.out.println("Skipping test, as JDK version is less than 9"); + System.err.println("Skipping multi-release jar test, as JDK version " + VersionFinder.JAVA_VERSION + + " is less than 9"); } else { try (ScanResult scanResult = new ClassGraph() .overrideClassLoaders(new URLClassLoader(new URL[] { jarURL })).enableAllInfo().scan()) { @@ -74,10 +75,8 @@ public void accept(final Resource resource, final byte[] byteArray) { */ @Test public void multiReleaseVersioningOfResources() throws Exception { - if (VersionFinder.JAVA_MAJOR_VERSION < 9) { - // Multi-release jar sections are ignored by ClassGraph if JDK<9 - System.out.println("Skipping test, as JDK version is less than 9"); - } else { + // 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 })) .whitelistPaths("nonexistent_path").scan()) { From 549db7455e52754db4d8f27750af2afa6a437e05 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 16 Feb 2020 16:46:47 -0700 Subject: [PATCH 0634/1778] Report warning on stdout, not stderr, so that it is shown --- .../java/io/github/classgraph/features/MultiReleaseJarTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/io/github/classgraph/features/MultiReleaseJarTest.java b/src/test/java/io/github/classgraph/features/MultiReleaseJarTest.java index c410b22af..55e5c3fd4 100644 --- a/src/test/java/io/github/classgraph/features/MultiReleaseJarTest.java +++ b/src/test/java/io/github/classgraph/features/MultiReleaseJarTest.java @@ -35,7 +35,7 @@ public class MultiReleaseJarTest { public void multiReleaseJar() throws Exception { if (VersionFinder.JAVA_MAJOR_VERSION < 9) { // Multi-release jar sections are ignored by ClassGraph if JDK<9 - System.err.println("Skipping multi-release jar test, as JDK version " + VersionFinder.JAVA_VERSION + System.out.println("Skipping multi-release jar test, as JDK version " + VersionFinder.JAVA_VERSION + " is less than 9"); } else { try (ScanResult scanResult = new ClassGraph() From b820f38314c51f2d47a1e2dca1107d879fb28529 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 19 Feb 2020 03:00:01 -0700 Subject: [PATCH 0635/1778] Keep static analyzers happy --- .../io/github/classgraph/MappableInfoList.java | 6 ++---- .../java/io/github/classgraph/ScanResult.java | 16 ++++++---------- .../fastzipfilereader/NestedJarHandler.java | 4 ++-- .../io/github/classgraph/fileslice/Slice.java | 4 ++-- 4 files changed, 12 insertions(+), 18 deletions(-) diff --git a/src/main/java/io/github/classgraph/MappableInfoList.java b/src/main/java/io/github/classgraph/MappableInfoList.java index 346c992db..89a52ab70 100644 --- a/src/main/java/io/github/classgraph/MappableInfoList.java +++ b/src/main/java/io/github/classgraph/MappableInfoList.java @@ -92,10 +92,8 @@ public Map asMap() { */ public boolean containsName(final String name) { for (final T i : this) { - if (i != null) { - if (i.getName().equals(name)) { - return true; - } + if (i != null && i.getName().equals(name)) { + return true; } } return false; diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index 5f295472f..d420f936f 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -462,9 +462,7 @@ public ResourceList getAllResources() { // Index Resource objects by path final ResourceList whitelistedResourcesList = new ResourceList(); for (final ClasspathElement classpathElt : classpathOrder) { - if (classpathElt.whitelistedResources != null) { - whitelistedResourcesList.addAll(classpathElt.whitelistedResources); - } + whitelistedResourcesList.addAll(classpathElt.whitelistedResources); } // Set atomically for thread safety allWhitelistedResourcesCached = whitelistedResourcesList; @@ -516,14 +514,12 @@ public ResourceList getResourcesWithPath(final String resourcePath) { // If just a few calls are made, directly search for resource with the requested path ResourceList matchingResources = null; for (final ClasspathElement classpathElt : classpathOrder) { - if (classpathElt.whitelistedResources != null) { - for (final Resource res : classpathElt.whitelistedResources) { - if (res.getPath().equals(path)) { - if (matchingResources == null) { - matchingResources = new ResourceList(); - } - matchingResources.add(res); + for (final Resource res : classpathElt.whitelistedResources) { + if (res.getPath().equals(path)) { + if (matchingResources == null) { + matchingResources = new ResourceList(); } + matchingResources.add(res); } } } 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 e9fbabc10..1a1713fee 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java @@ -714,12 +714,12 @@ public int available() throws IOException { } @Override - public void mark(final int readlimit) { + public synchronized void mark(final int readlimit) { throw new IllegalArgumentException("Not supported"); } @Override - public void reset() throws IOException { + public synchronized void reset() throws IOException { throw new IllegalArgumentException("Not supported"); } 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 60e0802f3..0831e8384 100644 --- a/src/main/java/nonapi/io/github/classgraph/fileslice/Slice.java +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/Slice.java @@ -203,13 +203,13 @@ public int available() { } @Override - public void mark(final int readlimit) { + public synchronized void mark(final int readlimit) { // Ignore readlimit markOff = currOff; } @Override - public void reset() { + public synchronized void reset() { currOff = markOff; } From d4b1639d4833ed84ed0606b52b71888a47ffa8c1 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 20 Feb 2020 01:42:29 -0700 Subject: [PATCH 0636/1778] Call java.lang.management.ManagementFactory via reflection (#404) --- .../io/github/classgraph/ModulePathInfo.java | 48 ++++++++++++------- .../classgraph/utils/ReflectionUtils.java | 2 +- 2 files changed, 32 insertions(+), 18 deletions(-) diff --git a/src/main/java/io/github/classgraph/ModulePathInfo.java b/src/main/java/io/github/classgraph/ModulePathInfo.java index 436be5e47..50bb8743d 100644 --- a/src/main/java/io/github/classgraph/ModulePathInfo.java +++ b/src/main/java/io/github/classgraph/ModulePathInfo.java @@ -29,7 +29,6 @@ package io.github.classgraph; import java.io.File; -import java.lang.management.ManagementFactory; import java.util.Arrays; import java.util.LinkedHashSet; import java.util.List; @@ -37,6 +36,7 @@ import nonapi.io.github.classgraph.utils.JarUtils; import nonapi.io.github.classgraph.utils.Join; +import nonapi.io.github.classgraph.utils.ReflectionUtils; /** * Information on the module path. Note that this will only include module system parameters actually listed in @@ -128,22 +128,36 @@ public class ModulePathInfo { /** Construct a {@link ModulePathInfo}. */ public ModulePathInfo() { - final List commandlineArguments = ManagementFactory.getRuntimeMXBean().getInputArguments(); - 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 - for (final String argPart : JarUtils.smartPathSplit(argParam, sepChar, - /* scanSpec = */ null)) { - argField.add(argPart); + // 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(managementFactory, "getRuntimeMXBean", + /* throwException = */ false); + @SuppressWarnings("unchecked") + final List commandlineArguments = runtimeMXBean == null ? null + : (List) ReflectionUtils.invokeStaticMethod(runtimeMXBean.getClass(), "getInputArguments", + /* throwException = */ false); + 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 + for (final String argPart : JarUtils.smartPathSplit(argParam, sepChar, + /* scanSpec = */ null)) { + argField.add(argPart); + } } } } 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 2a92dbe83..9e283f045 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/ReflectionUtils.java @@ -258,7 +258,7 @@ private static Object invokeMethod(final Class cls, final Object obj, final S } try { return oneArg ? method.invoke(obj, param) : method.invoke(obj); - } catch (final IllegalAccessException e) { + } catch (final IllegalAccessException | SecurityException e) { if (throwException) { throw new IllegalArgumentException( "Can't call " + (obj == null ? "static " : "") + "method \"" + methodName + "\": " + e); From 9180fb9b400e835e36b25bfa74fca0991c1f19f3 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 20 Feb 2020 01:46:39 -0700 Subject: [PATCH 0637/1778] Fix previous commit --- 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 50bb8743d..101dbc3f0 100644 --- a/src/main/java/io/github/classgraph/ModulePathInfo.java +++ b/src/main/java/io/github/classgraph/ModulePathInfo.java @@ -139,7 +139,7 @@ public ModulePathInfo() { /* throwException = */ false); @SuppressWarnings("unchecked") final List commandlineArguments = runtimeMXBean == null ? null - : (List) ReflectionUtils.invokeStaticMethod(runtimeMXBean.getClass(), "getInputArguments", + : (List) ReflectionUtils.invokeMethod(runtimeMXBean, "getInputArguments", /* throwException = */ false); if (commandlineArguments != null) { for (final String arg : commandlineArguments) { From df037e4f3ac323ce25e619d7d2836f06283d71f3 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 20 Feb 2020 01:47:19 -0700 Subject: [PATCH 0638/1778] [maven-release-plugin] prepare release classgraph-4.8.65 --- pom.xml | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/pom.xml b/pom.xml index 94ab7bf66..e17daa0c7 100644 --- a/pom.xml +++ b/pom.xml @@ -1,12 +1,9 @@ - + 4.0.0 io.github.classgraph classgraph - 4.8.65-SNAPSHOT + 4.8.65 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.56 + classgraph-4.8.65 @@ -264,8 +261,7 @@ - + org.codehaus.mojo.signature @@ -344,13 +340,8 @@ - - + + From 7b013b6dd575d7407bee5bb20a024c006e85fb35 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 20 Feb 2020 01:47:26 -0700 Subject: [PATCH 0639/1778] [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 e17daa0c7..58c95ec90 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.65 + 4.8.66-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 04da342ac8312f671659e1f653cde1a0b804fec6 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 20 Feb 2020 01:54:18 -0700 Subject: [PATCH 0640/1778] Bump version back down to attempt release again --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 58c95ec90..46dbf5d98 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.66-SNAPSHOT + 4.8.65-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 66c28120a0a3439e9ae50e59af166d847dfb86c1 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 20 Feb 2020 01:55:07 -0700 Subject: [PATCH 0641/1778] [maven-release-plugin] prepare release classgraph-4.8.65 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 46dbf5d98..e17daa0c7 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.65-SNAPSHOT + 4.8.65 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 38a5085a9c936a5da5da72241683621b8f5d3d2b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 20 Feb 2020 01:55:14 -0700 Subject: [PATCH 0642/1778] [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 e17daa0c7..58c95ec90 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.65 + 4.8.66-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 6265eb4b9321b6bb9a7ff81629f6d3216c5c4899 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 20 Feb 2020 02:52:37 -0700 Subject: [PATCH 0643/1778] Fix numerous Maven build issues --- pom.xml | 112 ++++++++++++++++++++++---------------------------------- 1 file changed, 44 insertions(+), 68 deletions(-) diff --git a/pom.xml b/pom.xml index 58c95ec90..411c1fcf0 100644 --- a/pom.xml +++ b/pom.xml @@ -1,9 +1,12 @@ - + 4.0.0 io.github.classgraph classgraph - 4.8.66-SNAPSHOT + 4.8.65-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -241,6 +244,7 @@ enforce-versions + validate enforce @@ -261,7 +265,8 @@ - + org.codehaus.mojo.signature @@ -340,8 +345,13 @@ - - + + @@ -452,6 +462,7 @@ attach-sources + package jar-no-fork @@ -466,6 +477,7 @@ attach-javadocs + package jar @@ -480,6 +492,29 @@ + + + org.apache.maven.plugins + maven-gpg-plugin + + + --pinentry-mode + loopback + + ${gpg.keyname} + ${gpg.keyname} + + + + sign-artifacts + verify + + sign + + + + + @@ -500,9 +535,11 @@ maven-release-plugin true - false release - deploy + deploy + + deploy + @@ -524,67 +561,6 @@ - - - release - - - performRelease - true - - - - - - org.apache.maven.plugins - maven-source-plugin - - - attach-sources - - jar - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - - - attach-javadocs - - jar - - - - - - - org.apache.maven.plugins - maven-gpg-plugin - - - --pinentry-mode - loopback - - ${gpg.keyname} - ${gpg.keyname} - - - - sign-artifacts - verify - - sign - - - - - - - - From a4d43c47e3530f5d5ec966b6fdef0a018de932b8 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 20 Feb 2020 02:53:52 -0700 Subject: [PATCH 0644/1778] [maven-release-plugin] prepare release classgraph-4.8.65 --- pom.xml | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/pom.xml b/pom.xml index 411c1fcf0..b5932765c 100644 --- a/pom.xml +++ b/pom.xml @@ -1,12 +1,9 @@ - + 4.0.0 io.github.classgraph classgraph - 4.8.65-SNAPSHOT + 4.8.65 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -265,8 +262,7 @@ - + org.codehaus.mojo.signature @@ -345,13 +341,8 @@ - - + + From e8a1f975b5997a2eafc422f24863b02e8a5d9bfe Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 20 Feb 2020 02:53:59 -0700 Subject: [PATCH 0645/1778] [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 b5932765c..a7c2986d7 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.65 + 4.8.66-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 6420282f7d15d65cba3c8f48ccd62d31d1a9d93b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 20 Feb 2020 02:56:34 -0700 Subject: [PATCH 0646/1778] Fix maven-release-plugin issue --- pom.xml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index a7c2986d7..2a2e9a92a 100644 --- a/pom.xml +++ b/pom.xml @@ -528,9 +528,7 @@ true release deploy - - deploy - + deploy From b2b658dd695d53090e2e07bc6a96b1a51dc39428 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 20 Feb 2020 02:56:54 -0700 Subject: [PATCH 0647/1778] Drop version number down again --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2a2e9a92a..10714c858 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.66-SNAPSHOT + 4.8.65-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 07156dfbe549ccbd6898eaad3374d4cf5eddfccf Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 20 Feb 2020 02:57:39 -0700 Subject: [PATCH 0648/1778] [maven-release-plugin] prepare release classgraph-4.8.65 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 10714c858..f5b8b2a3b 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.65-SNAPSHOT + 4.8.65 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 2ff524b555fb8fb6f86f8d98621fc3662f3b65fe Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 20 Feb 2020 02:57:46 -0700 Subject: [PATCH 0649/1778] [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 f5b8b2a3b..2a2e9a92a 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.65 + 4.8.66-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From d5991dfaeda103cd4b52479c49be800cff9f71b7 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 20 Feb 2020 03:08:10 -0700 Subject: [PATCH 0650/1778] Keep static analyzer happy --- src/main/java/io/github/classgraph/Classfile.java | 6 +++--- .../github/classgraph/fastzipfilereader/LogicalZipFile.java | 2 +- .../nonapi/io/github/classgraph/fileslice/FileSlice.java | 5 ----- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index 15533d121..8685a88d8 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -662,7 +662,7 @@ private String getConstantPoolString(final int cpIdx, final boolean replaceSlash return ""; } return intern( - reader.readString(constantPoolStringOffset + 2, utfLen, replaceSlashWithDot, stripLSemicolon)); + reader.readString(constantPoolStringOffset + 2L, utfLen, replaceSlashWithDot, stripLSemicolon)); } /** @@ -689,7 +689,7 @@ private String getConstantPoolString(final int cpIdx, final int subFieldIdx) if (utfLen == 0) { return ""; } - return intern(reader.readString(constantPoolStringOffset + 2, utfLen, /* replaceSlashWithDot = */ false, + return intern(reader.readString(constantPoolStringOffset + 2L, utfLen, /* replaceSlashWithDot = */ false, /* stripLSemicolon = */ false)); } @@ -728,7 +728,7 @@ private byte getConstantPoolStringFirstByte(final int cpIdx) throws ClassfileFor if (utfLen == 0) { return '\0'; } - return reader.readByte(constantPoolStringOffset + 2); + return reader.readByte(constantPoolStringOffset + 2L); } /** 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 d29848020..2b6a492b4 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java @@ -464,7 +464,7 @@ private void readCentralDirectory(final NestedJarHandler nestedJarHandler, final } final RandomAccessReader eocdReader = new ArraySlice(eocdBytes, /* isDeflatedZipEntry = */ false, /* inflatedLengthHint = */ 0L, nestedJarHandler).randomAccessReader(); - for (long i = eocdBytes.length - 22; i >= 0; --i) { + for (long i = eocdBytes.length - 22L; i >= 0L; --i) { if (eocdReader.readInt(i) == 0x06054b50) { eocdPos = i + readStartOff; break; 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 708a72c37..08ad940ba 100644 --- a/src/main/java/nonapi/io/github/classgraph/fileslice/FileSlice.java +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/FileSlice.java @@ -68,9 +68,6 @@ public class FileSlice extends Slice implements Closeable { /** True if this is a top level file slice. */ private final boolean isTopLevelFileSlice; - /** The nested jar handler. */ - private final NestedJarHandler nestedJarHandler; - /** True if {@link #close} has been called. */ private final AtomicBoolean isClosed = new AtomicBoolean(); @@ -99,7 +96,6 @@ private FileSlice(final FileSlice parentSlice, final long offset, final long len this.raf = parentSlice.raf; this.fileChannel = parentSlice.fileChannel; this.fileLength = parentSlice.fileLength; - this.nestedJarHandler = nestedJarHandler; this.isTopLevelFileSlice = false; if (parentSlice.backingByteBuffer != null) { @@ -140,7 +136,6 @@ public FileSlice(final File file, final boolean isDeflatedZipEntry, final long i this.fileChannel = raf.getChannel(); this.fileLength = file.length(); this.isTopLevelFileSlice = true; - this.nestedJarHandler = nestedJarHandler; if (nestedJarHandler.scanSpec.enableMemoryMapping) { try { From 19b1c8cabb2b9d47813c418445c4963b0a905066 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 20 Feb 2020 03:18:53 -0700 Subject: [PATCH 0651/1778] Fix CI builds --- pom.xml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/pom.xml b/pom.xml index 2a2e9a92a..497ff0e6f 100644 --- a/pom.xml +++ b/pom.xml @@ -526,6 +526,7 @@ maven-release-plugin true + release deploy deploy @@ -550,6 +551,36 @@ + + + release + + + + org.apache.maven.plugins + maven-gpg-plugin + + + --pinentry-mode + loopback + + ${gpg.keyname} + ${gpg.keyname} + + + + sign-artifacts + verify + + sign + + + + + + + + From ae0913db688aa43318ea9f0bb1c24b5e5b15b819 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 20 Feb 2020 03:19:43 -0700 Subject: [PATCH 0652/1778] Fix CI builds --- pom.xml | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/pom.xml b/pom.xml index 497ff0e6f..6844b1ade 100644 --- a/pom.xml +++ b/pom.xml @@ -483,29 +483,6 @@ - - - org.apache.maven.plugins - maven-gpg-plugin - - - --pinentry-mode - loopback - - ${gpg.keyname} - ${gpg.keyname} - - - - sign-artifacts - verify - - sign - - - - - From e7c123cf8204f997c7664202bbe74cac78ee01e2 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 20 Feb 2020 03:20:44 -0700 Subject: [PATCH 0653/1778] [maven-release-plugin] prepare release classgraph-4.8.66 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 6844b1ade..7c2cc8c56 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.66-SNAPSHOT + 4.8.66 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.65 + classgraph-4.8.66 From b8eca272c9bac07fcd2f035aef5a9a56158dd868 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 20 Feb 2020 03:20:52 -0700 Subject: [PATCH 0654/1778] [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 7c2cc8c56..e55a6457a 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.66 + 4.8.67-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.66 + classgraph-4.8.65 From 0d77fbe62770ef7ef18c0d10364515c65ff44ce7 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 20 Feb 2020 03:25:48 -0700 Subject: [PATCH 0655/1778] Fix CI builds --- pom.xml | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/pom.xml b/pom.xml index e55a6457a..3eab394db 100644 --- a/pom.xml +++ b/pom.xml @@ -1,9 +1,12 @@ - + 4.0.0 io.github.classgraph classgraph - 4.8.67-SNAPSHOT + 4.8.66-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -262,7 +265,8 @@ - + org.codehaus.mojo.signature @@ -341,8 +345,13 @@ - - + + @@ -502,11 +511,13 @@ org.apache.maven.plugins maven-release-plugin + deploy + deploy true release - deploy - deploy + + -Prelease From 0b56b539a2ee0a8d56df915661827c574410518e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 20 Feb 2020 03:26:58 -0700 Subject: [PATCH 0656/1778] [maven-release-plugin] prepare release classgraph-4.8.66 --- pom.xml | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/pom.xml b/pom.xml index 3eab394db..0879cd450 100644 --- a/pom.xml +++ b/pom.xml @@ -1,12 +1,9 @@ - + 4.0.0 io.github.classgraph classgraph - 4.8.66-SNAPSHOT + 4.8.66 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.65 + classgraph-4.8.66 @@ -265,8 +262,7 @@ - + org.codehaus.mojo.signature @@ -345,13 +341,8 @@ - - + + From 6c592e5b71aca8d00ca82891bbd693b0d69f8fd0 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 20 Feb 2020 03:26:58 -0700 Subject: [PATCH 0657/1778] [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 0879cd450..86b4318aa 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.66 + 4.8.67-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.66 + classgraph-4.8.65 From 6f54683b6f3222636694101fccbb7ef65245748b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 20 Feb 2020 03:29:32 -0700 Subject: [PATCH 0658/1778] Bump version number back down --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 86b4318aa..4afeb8550 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.67-SNAPSHOT + 4.8.66-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From f31fa69275a50a591d1c17abe09c304ab67fbee5 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 20 Feb 2020 04:06:40 -0700 Subject: [PATCH 0659/1778] Update m2e lifecycle management to use new tag syntax --- pom.xml | 81 ++++++++++++++++++++------------------------------------- 1 file changed, 28 insertions(+), 53 deletions(-) diff --git a/pom.xml b/pom.xml index 4afeb8550..5b392e6ac 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,7 @@ - + 4.0.0 io.github.classgraph @@ -230,6 +233,9 @@ org.apache.maven.plugins maven-enforcer-plugin + + + org.codehaus.mojo @@ -238,7 +244,7 @@ - + enforce-versions validate @@ -251,8 +257,19 @@ [3.6.2,) + + + true + + + display-info + compile + + display-info + + check-signatures @@ -262,7 +279,8 @@ - + org.codehaus.mojo.signature @@ -341,8 +359,13 @@ - - + + @@ -559,53 +582,5 @@ - - - - - only-eclipse - - - - m2e.version - - - - - - - - - - org.eclipse.m2e - lifecycle-mapping - 1.0.0 - - - - - - - - - org.apache.maven.plugins - maven-enforcer-plugin - [1.0.0,) - - enforce - - - - - - - - - - - - - - From 438733906eb6993e23fd5f2785e17c8b8ffcdaaf Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 20 Feb 2020 05:48:59 -0700 Subject: [PATCH 0660/1778] Bump Maven version to 3.6.3 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5b392e6ac..b0d0f8f8d 100644 --- a/pom.xml +++ b/pom.xml @@ -254,7 +254,7 @@ - [3.6.2,) + [3.6.3,) From 82e3498dff3524cc723b986b78134960b687e0f2 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 20 Feb 2020 05:55:16 -0700 Subject: [PATCH 0661/1778] Update Maven to 3.6.3 and mvnw to 0.5.6 --- .mvn/wrapper/maven-wrapper.properties | 4 ++-- mvnw | 8 ++++---- mvnw.cmd | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index 7d59a01f2..642d572ce 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.2/apache-maven-3.6.2-bin.zip -wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar +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 diff --git a/mvnw b/mvnw index d2f0ea380..41c0f0c23 100755 --- a/mvnw +++ b/mvnw @@ -19,7 +19,7 @@ # ---------------------------------------------------------------------------- # ---------------------------------------------------------------------------- -# Maven2 Start Up Batch script +# Maven Start Up Batch script # # Required ENV vars: # ------------------ @@ -212,9 +212,9 @@ else 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.5/maven-wrapper-0.5.5.jar" + 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.5/maven-wrapper-0.5.5.jar" + 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 ;; @@ -246,7 +246,7 @@ else else curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f fi - + else if [ "$MVNW_VERBOSE" = true ]; then echo "Falling back to using Java to download" diff --git a/mvnw.cmd b/mvnw.cmd index b26ab24f0..86115719e 100644 --- a/mvnw.cmd +++ b/mvnw.cmd @@ -18,7 +18,7 @@ @REM ---------------------------------------------------------------------------- @REM ---------------------------------------------------------------------------- -@REM Maven2 Start Up Batch script +@REM Maven Start Up Batch script @REM @REM Required ENV vars: @REM JAVA_HOME - location of a JDK home dir @@ -26,7 +26,7 @@ @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 key stroke before ending +@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 @@ -120,7 +120,7 @@ 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.5/maven-wrapper-0.5.5.jar" +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 @@ -134,7 +134,7 @@ if exist %WRAPPER_JAR% ( ) ) else ( if not "%MVNW_REPOURL%" == "" ( - SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar" + 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 ... From 2870358dead2f082e9d7a552d08bdd04bf80b4dc Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 20 Feb 2020 06:00:33 -0700 Subject: [PATCH 0662/1778] Fix comment --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b0d0f8f8d..9b501e598 100644 --- a/pom.xml +++ b/pom.xml @@ -257,7 +257,7 @@ [3.6.3,) - + true From 60eab1d947b9d15b9a5f8da2ce349688464404b3 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 26 Feb 2020 14:24:29 -0700 Subject: [PATCH 0663/1778] Add comment --- src/main/java/io/github/classgraph/ClassGraphClassLoader.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java index 171cd08b4..c319151ea 100644 --- a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java +++ b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java @@ -210,6 +210,8 @@ protected Class findClass(final String className) // 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 + // if the following fails? See: https://bugs.openjdk.java.net/browse/JDK-8202999 return defineClass(className, resourceByteBuffer, null); } finally { resource.close(); From cd76aee1113c568927cbe3bdbaed1d0fdc13b386 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 26 Feb 2020 14:25:09 -0700 Subject: [PATCH 0664/1778] Update comment --- src/main/java/io/github/classgraph/ClassGraphClassLoader.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java index c319151ea..c4ab044f3 100644 --- a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java +++ b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java @@ -211,7 +211,8 @@ protected Class findClass(final String className) try { final ByteBuffer resourceByteBuffer = resource.read(); // TODO: is there any need to try java.lang.invoke.MethodHandles.Lookup.defineClass - // if the following fails? See: https://bugs.openjdk.java.net/browse/JDK-8202999 + // 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, null); } finally { resource.close(); From ede29c573755f08d4efc3aa82b74e0b6de99fd8e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 20 Mar 2020 11:18:51 -0600 Subject: [PATCH 0665/1778] Add QuarkusClassLoaderHandler (#405) --- .../ClassLoaderHandlerRegistry.java | 1 + .../QuarkusClassLoaderHandler.java | 109 ++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java 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 4b8acd8ef..08272a75d 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(SpringBootRestartClassLoaderHandler.class), new ClassLoaderHandlerRegistryEntry(TomcatWebappClassLoaderBaseHandler.class), new ClassLoaderHandlerRegistryEntry(PlexusClassWorldsClassRealmClassLoaderHandler.class), + new ClassLoaderHandlerRegistryEntry(QuarkusClassLoaderHandler.class), // For unit testing of PARENT_LAST delegation order new ClassLoaderHandlerRegistryEntry(ParentLastDelegationOrderTestClassLoaderHandler.class), diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java new file mode 100644 index 000000000..452b9ef7a --- /dev/null +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java @@ -0,0 +1,109 @@ +/* + * This file is part of ClassGraph. + * + * Author: @mcollovati + * + * Hosted at: https://github.com/classgraph/classgraph + * + * -- + * + * The MIT License (MIT) + * + * Copyright (c) 2019 @mcollovati, contributed to the ClassGraph project + * + * 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.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; + +/** + * Extract classpath entries from the Quarkus ClassLoader. + */ +class QuarkusClassLoaderHandler implements ClassLoaderHandler { + /** Class cannot be constructed. */ + private QuarkusClassLoaderHandler() { + } + + /** + * Can handle. + * + * @param classLoaderClass + * the classloader class + * @param log + * the log + * @return true, if classLoaderClass is the Quarkus RuntimeClassloader + */ + public static boolean canHandle(final Class classLoaderClass, final LogNode log) { + return "io.quarkus.runner.RuntimeClassLoader".equals(classLoaderClass.getName()); + } + + /** + * Find classloader order. + * + * @param classLoader + * the class loader + * @param classLoaderOrder + * the classloader order + * @param log + * the log + */ + public static void findClassLoaderOrder(final ClassLoader classLoader, final ClassLoaderOrder classLoaderOrder, + final LogNode log) { + 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) { + @SuppressWarnings("unchecked") + final Map applicationClasses = (Map) ReflectionUtils.getFieldVal(classLoader, + "applicationClasses", false); + if (applicationClasses != null) { + for (final Path path : applicationClasses.values()) { + classpathOrder.addClasspathEntry(path.toUri(), classLoader, scanSpec, log); + } + } + @SuppressWarnings("unchecked") + final Collection applicationClassDirectories = (Collection) ReflectionUtils + .getFieldVal(classLoader, "applicationClassDirectories", false); + if (applicationClassDirectories != null) { + for (final Path path : applicationClassDirectories) { + classpathOrder.addClasspathEntryObject(path.toUri(), classLoader, scanSpec, log); + } + } + } +} From 34330ba49d5ca8759c2abd430fdf7eea2cad7565 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 20 Mar 2020 11:20:17 -0600 Subject: [PATCH 0666/1778] [maven-release-plugin] prepare release classgraph-4.8.66 --- pom.xml | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/pom.xml b/pom.xml index 9b501e598..a0384d430 100644 --- a/pom.xml +++ b/pom.xml @@ -1,12 +1,9 @@ - + 4.0.0 io.github.classgraph classgraph - 4.8.66-SNAPSHOT + 4.8.66 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.65 + classgraph-4.8.66 @@ -279,8 +276,7 @@ - + org.codehaus.mojo.signature @@ -359,13 +355,8 @@ - - + + From 707dbd7c4454254814f77f8b54e9f216350e1b8a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 20 Mar 2020 11:20:24 -0600 Subject: [PATCH 0667/1778] [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 a0384d430..bfc990b54 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.66 + 4.8.67-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.66 + classgraph-4.8.65 From 9c35f2a505af154e1c0263ea39556a8b4e603e35 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 20 Mar 2020 14:07:54 -0600 Subject: [PATCH 0668/1778] Bump version number down; attempt re-release --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bfc990b54..24baa82ca 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.67-SNAPSHOT + 4.8.66-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 596ae432f969763521ceee3559ba51d1a391123e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 20 Mar 2020 14:08:48 -0600 Subject: [PATCH 0669/1778] [maven-release-plugin] prepare release classgraph-4.8.66 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 24baa82ca..a0384d430 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.66-SNAPSHOT + 4.8.66 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.65 + classgraph-4.8.66 From 1fcf6d36d3314340417cb89ae85a7a93b15c75ff Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 20 Mar 2020 14:08:55 -0600 Subject: [PATCH 0670/1778] [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 a0384d430..0bd924405 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.66 + 4.8.67-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From ac277a950e1754e1eb092b8321b01ed1832d3159 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 21 Mar 2020 00:19:37 -0600 Subject: [PATCH 0671/1778] Bump version down again --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0bd924405..b095a64bc 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.67-SNAPSHOT + 4.8.66-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From afa35507cd628b892259594583fefc420178c6fe Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 21 Mar 2020 00:20:21 -0600 Subject: [PATCH 0672/1778] [maven-release-plugin] prepare release classgraph-4.8.66 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b095a64bc..a0384d430 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.66-SNAPSHOT + 4.8.66 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From a0c9ec1668f9bfe354c830d21b2aeb4a4c911ea0 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 21 Mar 2020 00:20:28 -0600 Subject: [PATCH 0673/1778] [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 a0384d430..0bd924405 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.66 + 4.8.67-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 268a80371ffcf9c60c72c057418a00f039f1056e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 21 Mar 2020 12:17:15 -0600 Subject: [PATCH 0674/1778] Initial JDK14 record support --- .../java/io/github/classgraph/ClassInfo.java | 83 ++++++++++++++++-- .../io/github/classgraph/ClassInfoList.java | 14 +++ .../java/io/github/classgraph/Classfile.java | 19 +++- .../java/io/github/classgraph/ScanResult.java | 30 +++++++ .../features/MultiReleaseJarTest.java | 2 +- .../classgraph/features/RecordTest.java | 40 +++++++++ src/test/resources/record.jar | Bin 0 -> 1055 bytes 7 files changed, 177 insertions(+), 11 deletions(-) create mode 100644 src/test/java/io/github/classgraph/features/RecordTest.java create mode 100644 src/test/resources/record.jar diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index cef9d91af..a700f033c 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -64,6 +64,9 @@ public class ClassInfo extends ScanResultObject implements Comparable /** Class modifier flags, e.g. Modifier.PUBLIC */ private int modifiers; + /** True if the class is a record. */ + private boolean isRecord; + /** * This annotation has the {@link Inherited} meta-annotation, which means that any class that this annotation is * applied to also implicitly causes the annotation to annotate all subclasses too. @@ -365,7 +368,7 @@ void setIsInterface(final boolean isInterface) { } /** - * Set isInterface status. + * Set isAnnotation status. * * @param isAnnotation * true if this is an annotation @@ -376,6 +379,18 @@ void setIsAnnotation(final boolean isAnnotation) { } } + /** + * Set isRecord status. + * + * @param isRecord + * true if this is a record + */ + void setIsRecord(final boolean isRecord) { + if (isRecord) { + this.isRecord = isRecord; + } + } + // ------------------------------------------------------------------------------------------------------------- /** @@ -670,6 +685,10 @@ private enum ClassType { ANNOTATION, /** An interface or annotation (used since you can actually implement an annotation). */ INTERFACE_OR_ANNOTATION, + /** An enum. */ + ENUM, + /** A record type. */ + RECORD } /** @@ -694,6 +713,8 @@ private static Set filterClassInfo(final Collection classe boolean includeStandardClasses = false; boolean includeImplementedInterfaces = false; boolean includeAnnotations = false; + boolean includeEnums = false; + boolean includeRecords = false; for (final ClassType classType : classTypes) { switch (classType) { case ALL: @@ -711,6 +732,12 @@ private static Set filterClassInfo(final Collection classe case INTERFACE_OR_ANNOTATION: includeImplementedInterfaces = includeAnnotations = true; break; + case ENUM: + includeEnums = true; + break; + case RECORD: + includeRecords = true; + break; default: throw new IllegalArgumentException("Unknown ClassType: " + classType); } @@ -722,9 +749,11 @@ private static Set filterClassInfo(final Collection classe for (final ClassInfo classInfo : classes) { // Check class type against requested type(s) if ((includeAllTypes // - || includeStandardClasses && classInfo.isStandardClass() - || includeImplementedInterfaces && classInfo.isImplementedInterface() - || includeAnnotations && classInfo.isAnnotation()) // + || includeStandardClasses && classInfo.isStandardClass() // + || includeImplementedInterfaces && classInfo.isImplementedInterface() // + || includeAnnotations && classInfo.isAnnotation() // + || includeEnums && classInfo.isEnum() // + || includeRecords && classInfo.isRecord()) // // Always check blacklist && !scanSpec.classOrPackageIsBlacklisted(classInfo.name) // // Always return whitelisted classes, or external classes if enableExternalClasses is true @@ -873,6 +902,36 @@ static ClassInfoList getAllClasses(final Collection classes, final Sc /* sortByName = */ true); } + /** + * Get all {@link Enum} classes found during the scan. + * + * @param classes + * the classes + * @param scanSpec + * the scan spec + * @return A list of all {@link Enum} classes found during the scan, or the empty list if none. + */ + static ClassInfoList getAllEnums(final Collection classes, final ScanSpec scanSpec) { + return new ClassInfoList( + ClassInfo.filterClassInfo(classes, scanSpec, /* strictWhitelist = */ true, ClassType.ENUM), + /* sortByName = */ true); + } + + /** + * Get all {@code record} classes found during the scan. + * + * @param classes + * the classes + * @param scanSpec + * the scan spec + * @return A list of all {@code record} classes found during the scan, or the empty list if none. + */ + static ClassInfoList getAllRecords(final Collection classes, final ScanSpec scanSpec) { + return new ClassInfoList( + ClassInfo.filterClassInfo(classes, scanSpec, /* strictWhitelist = */ true, ClassType.RECORD), + /* sortByName = */ true); + } + /** * Get all standard classes found during the scan. * @@ -1112,6 +1171,15 @@ public boolean isEnum() { return (modifiers & 0x4000) != 0; } + /** + * Checks if is the class is a record (JDK 14+). + * + * @return true if this class is a record. + */ + public boolean isRecord() { + return isRecord; + } + /** * Checks if this class is a standard class. * @@ -2867,8 +2935,11 @@ protected String toString(final boolean typeNameOnly) { if (buf.length() > 0) { buf.append(' '); } - buf.append(isAnnotation() ? "@interface " - : isInterface() ? "interface " : (modifiers & 0x4000) != 0 ? "enum " : "class "); + buf.append(isRecord() ? "record " // + : isEnum() ? "enum " // + : isAnnotation() ? "@interface " // + : isInterface() ? "interface " // + : "class "); buf.append(name); final ClassInfo superclass = getSuperclass(); if (superclass != null && !superclass.getName().equals("java.lang.Object")) { diff --git a/src/main/java/io/github/classgraph/ClassInfoList.java b/src/main/java/io/github/classgraph/ClassInfoList.java index d7a0c1c75..1293436cb 100644 --- a/src/main/java/io/github/classgraph/ClassInfoList.java +++ b/src/main/java/io/github/classgraph/ClassInfoList.java @@ -492,6 +492,20 @@ public boolean accept(final ClassInfo ci) { }); } + /** + * Filter this {@link ClassInfoList} to include only {@code record} classes. + * + * @return The filtered list, containing only {@code record} classes. + */ + public ClassInfoList getRecords() { + return filter(new ClassInfoFilter() { + @Override + public boolean accept(final ClassInfo ci) { + return ci.isRecord(); + } + }); + } + /** * Filter this {@link ClassInfoList} to include only classes that are assignable to the requested class, * assignableToClass (i.e. where assignableToClass is a superclass or implemented interface of the list diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index 8685a88d8..625773b93 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -85,6 +85,9 @@ class Classfile { /** Whether this class is an interface. */ private boolean isInterface; + /** Whether this class is a record. */ + private boolean isRecord; + /** Whether this class is an annotation. */ private boolean isAnnotation; @@ -464,6 +467,7 @@ void link(final Map classNameToClassInfo, classInfo.setModifiers(classModifiers); classInfo.setIsInterface(isInterface); classInfo.setIsAnnotation(isAnnotation); + classInfo.setIsRecord(isRecord); if (superclassName != null) { classInfo.addSuperclass(superclassName, classNameToClassInfo); } @@ -1503,6 +1507,9 @@ private void readClassAttributes() throws IOException, ClassfileFormatException classAnnotations.add(readAnnotation()); } } + } else if (constantPoolStringEquals(attributeNameCpIdx, "Record")) { + isRecord = true; + reader.skip(attributeLength); } else if (constantPoolStringEquals(attributeNameCpIdx, "InnerClasses")) { final int numInnerClasses = reader.readUnsignedShort(); for (int j = 0; j < numInnerClasses; j++) { @@ -1513,11 +1520,15 @@ private void readClassAttributes() throws IOException, ClassfileFormatException if (innerClassInfoCpIdx != 0 && outerClassInfoCpIdx != 0) { final String innerClassName = getConstantPoolClassName(innerClassInfoCpIdx); final String outerClassName = getConstantPoolClassName(outerClassInfoCpIdx); - if (classContainmentEntries == null) { - classContainmentEntries = new ArrayList<>(); + // Record types have a Lookup inner class (that is not really an inner class) + if (!(innerClassName.equals("java.lang.invoke.MethodHandles$Lookup") + && outerClassName.equals("java.lang.invoke.MethodHandles"))) { + if (classContainmentEntries == null) { + classContainmentEntries = new ArrayList<>(); + } + classContainmentEntries.add( + new ClassContainment(innerClassName, innerClassAccessFlags, outerClassName)); } - classContainmentEntries - .add(new ClassContainment(innerClassName, innerClassAccessFlags, outerClassName)); } } } else if (constantPoolStringEquals(attributeNameCpIdx, "Signature")) { diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index d420f936f..aecf1c3e8 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -801,6 +801,36 @@ public ClassInfoList getAllClasses() { return ClassInfo.getAllClasses(classNameToClassInfo.values(), scanSpec); } + /** + * 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. + */ + public ClassInfoList getAllEnums() { + if (closed.get()) { + throw new IllegalArgumentException("Cannot use a ScanResult after it has been closed"); + } + if (!scanSpec.enableClassInfo) { + throw new IllegalArgumentException("Please call ClassGraph#enableClassInfo() before #scan()"); + } + return ClassInfo.getAllEnums(classNameToClassInfo.values(), scanSpec); + } + + /** + * 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. + */ + public ClassInfoList getAllRecords() { + if (closed.get()) { + throw new IllegalArgumentException("Cannot use a ScanResult after it has been closed"); + } + if (!scanSpec.enableClassInfo) { + throw new IllegalArgumentException("Please call ClassGraph#enableClassInfo() before #scan()"); + } + return ClassInfo.getAllRecords(classNameToClassInfo.values(), scanSpec); + } + /** * Get a map from class name to {@link ClassInfo} object for all classes, interfaces and annotations found * during the scan. diff --git a/src/test/java/io/github/classgraph/features/MultiReleaseJarTest.java b/src/test/java/io/github/classgraph/features/MultiReleaseJarTest.java index 55e5c3fd4..f1955ea20 100644 --- a/src/test/java/io/github/classgraph/features/MultiReleaseJarTest.java +++ b/src/test/java/io/github/classgraph/features/MultiReleaseJarTest.java @@ -21,7 +21,7 @@ * MultiReleaseJar. */ public class MultiReleaseJarTest { - /** The Constant jarURL. */ + /** The jar URL. */ private static final URL jarURL = MultiReleaseJarTest.class.getClassLoader() .getResource("multi-release-jar.jar"); diff --git a/src/test/java/io/github/classgraph/features/RecordTest.java b/src/test/java/io/github/classgraph/features/RecordTest.java new file mode 100644 index 000000000..f066b70bc --- /dev/null +++ b/src/test/java/io/github/classgraph/features/RecordTest.java @@ -0,0 +1,40 @@ +package io.github.classgraph.features; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.net.URL; +import java.net.URLClassLoader; + +import org.junit.jupiter.api.Test; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ClassInfo; +import io.github.classgraph.ClassInfoList; +import io.github.classgraph.FieldInfo; +import io.github.classgraph.ScanResult; + +/** + * MultiReleaseJar. + */ +public class RecordTest { + /** The jar URL. */ + private static final URL jarURL = RecordTest.class.getClassLoader().getResource("record.jar"); + + /** + * Test records (JDK 14+). + * + * @throws Exception + * the exception + */ + @Test + public void recordJar() throws Exception { + try (ScanResult scanResult = new ClassGraph().overrideClassLoaders(new URLClassLoader(new URL[] { jarURL })) + .enableAllInfo().scan()) { + final ClassInfoList classInfoList = scanResult.getAllRecords(); + assertThat(classInfoList).isNotEmpty(); + final ClassInfo classInfo = classInfoList.get(0); + final FieldInfo fieldInfo = classInfo.getFieldInfo("x"); + assertThat(fieldInfo).isNotNull(); + } + } +} diff --git a/src/test/resources/record.jar b/src/test/resources/record.jar new file mode 100644 index 0000000000000000000000000000000000000000..5968f163e41d71084cf232f783203bbe270c0208 GIT binary patch literal 1055 zcmWIWW@h1H0D)ssr2$|Dlwe_yVJOH>*AEThWMFpoEsOI2;?fFk21b?_%nS@*A^@mc zgn@~HgJBm?_qM`4+Tu(M3`Q&r3<6NyL8;04MJam8If=!^V3Q1hrWj(H)H~^H-em`o z*7GLzelpINE;)H;PIlt%6pT_C{%pIES1H_d>?TpFK;SOa#R%v60>38 zcis8e!fR24DEIlT$M0KyKR5UH^YQ8E4A?-;cMhF54OCSv_k>)$zAld!`tDU_SbL@sc{b-rZ{N4w?mQdyp`Fp+#Xh zvwlIf(kUTNiA}v5xexy=F3+fbtD^tPVo#l05I@)c!k!N=vg7v%)dlbT z>#!;_VgKn``(uwUKfRg$&iYj-+fJM2g9%q{{8wCv{J|z?^ybg?Kpva48+&Ay&g)OG zS#&dI!Og^oOP{^-HLLs|s=Zxz`Fd{Gul+CoFcn{`KV-rGGJ4B0O(xEq4AI!EAB!7& zqqc7G2xM+EJ`(!i_nf?m%XUdHU)HJbZVV6XdJq&6a_pw;qbixTt0klJU9M?9XJ2<_ zv1d+T%!0}HE>2q*@;!03T=%}Rj*}1U6+q z2gLlS2`nqIED@Z()PZRWgwax}wujF-KOg^4jT3&`pFPsj@I7_XCrrc3_nf|0AO};h z)XYj%F40+BSGl&vFa&rrGRZOH$`umO{2{>b*Ac{oM^6yVLu2J#Lw5Pk*HBEUS( GzyJXF6M7&3 literal 0 HcmV?d00001 From 21769559ec2cbd4b5b430b78a7fdcf4b97325735 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 21 Mar 2020 12:38:33 -0600 Subject: [PATCH 0675/1778] Fix Scrutinizer warning --- src/main/java/io/github/classgraph/Classfile.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index 625773b93..44a6cc9bf 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -1521,8 +1521,8 @@ private void readClassAttributes() throws IOException, ClassfileFormatException final String innerClassName = getConstantPoolClassName(innerClassInfoCpIdx); final String outerClassName = getConstantPoolClassName(outerClassInfoCpIdx); // Record types have a Lookup inner class (that is not really an inner class) - if (!(innerClassName.equals("java.lang.invoke.MethodHandles$Lookup") - && outerClassName.equals("java.lang.invoke.MethodHandles"))) { + if (!("java.lang.invoke.MethodHandles$Lookup".equals(innerClassName) + && "java.lang.invoke.MethodHandles".equals(outerClassName))) { if (classContainmentEntries == null) { classContainmentEntries = new ArrayList<>(); } From 656a6bc3961eef04f68234e6dcab48a6bd3ee568 Mon Sep 17 00:00:00 2001 From: Marco Collovati Date: Sat, 21 Mar 2020 22:50:09 +0100 Subject: [PATCH 0676/1778] Added support for Quarkus 1.3 Since version 1.3 Quarkus changed how classloading works. This change adds support for the new Quarkus classloader and removes unecessary entries added for 1.2 Runtimeclassloader. --- .../QuarkusClassLoaderHandler.java | 77 +++++++++++-------- 1 file changed, 45 insertions(+), 32 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 452b9ef7a..2debae277 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java @@ -30,7 +30,6 @@ 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; @@ -42,35 +41,39 @@ * Extract classpath entries from the Quarkus ClassLoader. */ class QuarkusClassLoaderHandler implements ClassLoaderHandler { - /** Class cannot be constructed. */ + + // Classloader until Quarkus 1.2 + private static final String RUNTIME_CLASSLOADER = "io.quarkus.runner.RuntimeClassLoader"; + // Classloader since Quarkus 1.3 + private static final String QUARKUS_CLASSLOADER = "io.quarkus.bootstrap.classloading.QuarkusClassLoader"; + + /** + * Class cannot be constructed. + */ private QuarkusClassLoaderHandler() { } /** * Can handle. * - * @param classLoaderClass - * the classloader class - * @param log - * the log - * @return true, if classLoaderClass is the Quarkus RuntimeClassloader + * @param classLoaderClass the classloader class + * @param log the log + * @return true, if classLoaderClass is the Quarkus RuntimeClassloader or QuarkusClassloader */ public static boolean canHandle(final Class classLoaderClass, final LogNode log) { - return "io.quarkus.runner.RuntimeClassLoader".equals(classLoaderClass.getName()); + return RUNTIME_CLASSLOADER.equals(classLoaderClass.getName()) + || QUARKUS_CLASSLOADER.equals(classLoaderClass.getName()); } /** * Find classloader order. * - * @param classLoader - * the class loader - * @param classLoaderOrder - * the classloader order - * @param log - * the log + * @param classLoader the class loader + * @param classLoaderOrder the classloader order + * @param log 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); } @@ -78,28 +81,38 @@ public static void findClassLoaderOrder(final ClassLoader classLoader, final Cla /** * 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. + * @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) { - @SuppressWarnings("unchecked") - final Map applicationClasses = (Map) ReflectionUtils.getFieldVal(classLoader, - "applicationClasses", false); - if (applicationClasses != null) { - for (final Path path : applicationClasses.values()) { - classpathOrder.addClasspathEntry(path.toUri(), classLoader, scanSpec, log); + final ScanSpec scanSpec, final LogNode log) { + + String classLoaderName = classLoader.getClass().getName(); + if (RUNTIME_CLASSLOADER.equals(classLoaderName)) { + findClasspathOrderForRuntimeClassloader(classLoader, classpathOrder, scanSpec, log); + } else if (QUARKUS_CLASSLOADER.equals(classLoaderName)) { + findClasspathOrderForQuarkusClassloader(classLoader, classpathOrder, scanSpec, log); + } + } + + @SuppressWarnings("unchecked") + private static void findClasspathOrderForQuarkusClassloader(ClassLoader classLoader, ClasspathOrder classpathOrder, ScanSpec scanSpec, LogNode log) { + for (Object element : (Collection) ReflectionUtils.getFieldVal(classLoader, "elements", false)) { + String elementClassName = element.getClass().getName(); + if ("io.quarkus.bootstrap.classloading.JarClassPathElement".equals(elementClassName)) { + classpathOrder.addClasspathEntry(ReflectionUtils.getFieldVal(element, "file", false), classLoader, scanSpec, log); + } else if ("io.quarkus.bootstrap.classloading.DirectoryClassPathElement".equals(elementClassName)) { + classpathOrder.addClasspathEntry(ReflectionUtils.getFieldVal(element, "root", false), classLoader, scanSpec, log); } } - @SuppressWarnings("unchecked") + } + + @SuppressWarnings("unchecked") + private static void findClasspathOrderForRuntimeClassloader(ClassLoader classLoader, ClasspathOrder classpathOrder, ScanSpec scanSpec, LogNode log) { final Collection applicationClassDirectories = (Collection) ReflectionUtils - .getFieldVal(classLoader, "applicationClassDirectories", false); + .getFieldVal(classLoader, "applicationClassDirectories", false); if (applicationClassDirectories != null) { for (final Path path : applicationClassDirectories) { classpathOrder.addClasspathEntryObject(path.toUri(), classLoader, scanSpec, log); From 5e5d3d056519b230c8225747df32ae8ee2f1d75f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 21 Mar 2020 16:19:46 -0600 Subject: [PATCH 0677/1778] Update comments --- src/main/java/io/github/classgraph/Classfile.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index 44a6cc9bf..7bce5956c 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -1509,6 +1509,9 @@ private void readClassAttributes() throws IOException, ClassfileFormatException } } else if (constantPoolStringEquals(attributeNameCpIdx, "Record")) { isRecord = true; + // No need to read record_components_info entries -- there is a 1:1 correspondence between + // record components and fields/methods of the same name and type as the record component, + // so we can just rely on the field and method reading code to work correctly with records. reader.skip(attributeLength); } else if (constantPoolStringEquals(attributeNameCpIdx, "InnerClasses")) { final int numInnerClasses = reader.readUnsignedShort(); @@ -1520,9 +1523,10 @@ private void readClassAttributes() throws IOException, ClassfileFormatException if (innerClassInfoCpIdx != 0 && outerClassInfoCpIdx != 0) { final String innerClassName = getConstantPoolClassName(innerClassInfoCpIdx); final String outerClassName = getConstantPoolClassName(outerClassInfoCpIdx); - // Record types have a Lookup inner class (that is not really an inner class) + // Record types have a Lookup inner class for boostrap methods in JDK 14 -- drop this if (!("java.lang.invoke.MethodHandles$Lookup".equals(innerClassName) && "java.lang.invoke.MethodHandles".equals(outerClassName))) { + // Store relationship between inner class and outer class if (classContainmentEntries == null) { classContainmentEntries = new ArrayList<>(); } From 6a2e188eef046ceb304758cf694ee61a4316d728 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 21 Mar 2020 16:20:07 -0600 Subject: [PATCH 0678/1778] Source > Cleanup --- .../QuarkusClassLoaderHandler.java | 54 ++++++++++++------- 1 file changed, 34 insertions(+), 20 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 2debae277..5a5b0e691 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java @@ -56,24 +56,29 @@ private QuarkusClassLoaderHandler() { /** * Can handle. * - * @param classLoaderClass the classloader class - * @param log the log + * @param classLoaderClass + * the classloader class + * @param log + * the log * @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()); + || QUARKUS_CLASSLOADER.equals(classLoaderClass.getName()); } /** * Find classloader order. * - * @param classLoader the class loader - * @param classLoaderOrder the classloader order - * @param log the log + * @param classLoader + * the class loader + * @param classLoaderOrder + * the classloader order + * @param log + * 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); } @@ -81,15 +86,19 @@ public static void findClassLoaderOrder(final ClassLoader classLoader, final Cla /** * 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. + * @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) { + final ScanSpec scanSpec, final LogNode log) { - String classLoaderName = classLoader.getClass().getName(); + final String classLoaderName = classLoader.getClass().getName(); if (RUNTIME_CLASSLOADER.equals(classLoaderName)) { findClasspathOrderForRuntimeClassloader(classLoader, classpathOrder, scanSpec, log); } else if (QUARKUS_CLASSLOADER.equals(classLoaderName)) { @@ -98,21 +107,26 @@ public static void findClasspathOrder(final ClassLoader classLoader, final Class } @SuppressWarnings("unchecked") - private static void findClasspathOrderForQuarkusClassloader(ClassLoader classLoader, ClasspathOrder classpathOrder, ScanSpec scanSpec, LogNode log) { - for (Object element : (Collection) ReflectionUtils.getFieldVal(classLoader, "elements", false)) { - String elementClassName = element.getClass().getName(); + 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)) { + final String elementClassName = element.getClass().getName(); if ("io.quarkus.bootstrap.classloading.JarClassPathElement".equals(elementClassName)) { - classpathOrder.addClasspathEntry(ReflectionUtils.getFieldVal(element, "file", false), classLoader, scanSpec, log); + classpathOrder.addClasspathEntry(ReflectionUtils.getFieldVal(element, "file", false), classLoader, + scanSpec, log); } else if ("io.quarkus.bootstrap.classloading.DirectoryClassPathElement".equals(elementClassName)) { - classpathOrder.addClasspathEntry(ReflectionUtils.getFieldVal(element, "root", false), classLoader, scanSpec, log); + classpathOrder.addClasspathEntry(ReflectionUtils.getFieldVal(element, "root", false), classLoader, + scanSpec, log); } } } @SuppressWarnings("unchecked") - private static void findClasspathOrderForRuntimeClassloader(ClassLoader classLoader, ClasspathOrder classpathOrder, ScanSpec scanSpec, LogNode log) { + 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); + .getFieldVal(classLoader, "applicationClassDirectories", false); if (applicationClassDirectories != null) { for (final Path path : applicationClassDirectories) { classpathOrder.addClasspathEntryObject(path.toUri(), classLoader, scanSpec, log); From dce7b397c0dca02d057c426f4882fac6132180d5 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 21 Mar 2020 16:20:55 -0600 Subject: [PATCH 0679/1778] Source > Format --- .../classloaderhandler/QuarkusClassLoaderHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 5a5b0e691..af3f4e270 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java @@ -41,9 +41,9 @@ * Extract classpath entries from the Quarkus ClassLoader. */ class QuarkusClassLoaderHandler implements ClassLoaderHandler { - // Classloader until Quarkus 1.2 private static final String RUNTIME_CLASSLOADER = "io.quarkus.runner.RuntimeClassLoader"; + // Classloader since Quarkus 1.3 private static final String QUARKUS_CLASSLOADER = "io.quarkus.bootstrap.classloading.QuarkusClassLoader"; From 00b0bb1c2d1e1ec6692da66f1bf6bfab316f738f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 21 Mar 2020 17:35:46 -0600 Subject: [PATCH 0680/1778] Add check --- src/main/java/io/github/classgraph/Classfile.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index 7bce5956c..87f72ad1c 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -1523,6 +1523,14 @@ private void readClassAttributes() throws IOException, ClassfileFormatException if (innerClassInfoCpIdx != 0 && outerClassInfoCpIdx != 0) { final String innerClassName = getConstantPoolClassName(innerClassInfoCpIdx); final String outerClassName = getConstantPoolClassName(outerClassInfoCpIdx); + if (innerClassName == null || outerClassName == null) { + // Should not happen (fix static analyzer warning) + throw new ClassfileFormatException("Inner and/or outer class name is null"); + } + if (innerClassName.equals(outerClassName)) { + // Invalid according to spec + throw new ClassfileFormatException("Inner and outer class name cannot be the same"); + } // Record types have a Lookup inner class for boostrap methods in JDK 14 -- drop this if (!("java.lang.invoke.MethodHandles$Lookup".equals(innerClassName) && "java.lang.invoke.MethodHandles".equals(outerClassName))) { From c30f25dbbfe73ae01c5b8fe818e1a01aa49c51c0 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 23 Mar 2020 12:38:07 -0600 Subject: [PATCH 0681/1778] Bump version number down to try release again --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0bd924405..b095a64bc 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.67-SNAPSHOT + 4.8.66-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 782e47d1b43eb016b055b468f9fb99a56c85df0f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 23 Mar 2020 12:38:37 -0600 Subject: [PATCH 0682/1778] [maven-release-plugin] prepare release classgraph-4.8.66 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b095a64bc..a0384d430 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.66-SNAPSHOT + 4.8.66 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 8b35ee37a3a0413ff756ece8a5b6c66ff99c22ce Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 23 Mar 2020 12:38:44 -0600 Subject: [PATCH 0683/1778] [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 a0384d430..0bd924405 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.66 + 4.8.67-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 6c0c09507f8195c624e7b3d6e2c318b41b2a6155 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 24 Mar 2020 19:09:57 -0600 Subject: [PATCH 0684/1778] Bump version back down --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0bd924405..b095a64bc 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.67-SNAPSHOT + 4.8.66-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 125e2e750dc3934b4a4f84b197987f6d6a61057b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 24 Mar 2020 19:10:49 -0600 Subject: [PATCH 0685/1778] [maven-release-plugin] prepare release classgraph-4.8.66 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b095a64bc..a0384d430 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.66-SNAPSHOT + 4.8.66 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 12ecf75f4bde34280648e4b36e7c4ab2cc11ecf8 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 24 Mar 2020 19:10:57 -0600 Subject: [PATCH 0686/1778] [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 a0384d430..0bd924405 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.66 + 4.8.67-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 0aeb33895b8f3b933ec4b2fdfbfb027997510d11 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 25 Mar 2020 14:43:35 -0600 Subject: [PATCH 0687/1778] Adjust version number again --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0bd924405..b095a64bc 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.67-SNAPSHOT + 4.8.66-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From f7910a4ca9fba58e463863a3cabdb461cc3da2c7 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 26 Mar 2020 01:46:25 -0600 Subject: [PATCH 0688/1778] Strip out m2e directives from pom.xml --- pom.xml | 75 ++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 64 insertions(+), 11 deletions(-) diff --git a/pom.xml b/pom.xml index b095a64bc..d77a3ed27 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,7 @@ - + 4.0.0 io.github.classgraph @@ -276,7 +279,8 @@ - + org.codehaus.mojo.signature @@ -337,17 +341,18 @@ - - - - - - - org.apache.maven.plugins maven-antrun-plugin + + + + + + + + add-module-info-to-jar package @@ -355,12 +360,60 @@ - - + + + + + + + + + strip-m2e-directives + package + + run + + + + + + + + + + + + + + + + + + + + + + + + From dd1ed7709c3fee7886d7b3508db13af2de9041c1 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 26 Mar 2020 01:47:15 -0600 Subject: [PATCH 0689/1778] [maven-release-plugin] prepare release classgraph-4.8.66 --- pom.xml | 38 ++++++++++---------------------------- 1 file changed, 10 insertions(+), 28 deletions(-) diff --git a/pom.xml b/pom.xml index d77a3ed27..d1aff155f 100644 --- a/pom.xml +++ b/pom.xml @@ -1,12 +1,9 @@ - + 4.0.0 io.github.classgraph classgraph - 4.8.66-SNAPSHOT + 4.8.66 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -279,8 +276,7 @@ - + org.codehaus.mojo.signature @@ -360,13 +356,8 @@ - - + + @@ -386,31 +377,22 @@ - + - + - + - + - + From 9dc1af98bed5658df078016b5b4d77deddce2d01 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 26 Mar 2020 01:47:34 -0600 Subject: [PATCH 0690/1778] [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 d1aff155f..5f1738c9c 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.66 + 4.8.67-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 6fd1c51c405d03b1224065cd81d45609f1270e26 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 26 Mar 2020 01:55:03 -0600 Subject: [PATCH 0691/1778] Bump version number back down --- pom.xml | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/pom.xml b/pom.xml index 5f1738c9c..d77a3ed27 100644 --- a/pom.xml +++ b/pom.xml @@ -1,9 +1,12 @@ - + 4.0.0 io.github.classgraph classgraph - 4.8.67-SNAPSHOT + 4.8.66-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -276,7 +279,8 @@ - + org.codehaus.mojo.signature @@ -356,8 +360,13 @@ - - + + @@ -377,22 +386,31 @@ - + - + - + - + - + From d63820b7e74a154e3beea722ab0f2fda270db342 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 26 Mar 2020 02:09:49 -0600 Subject: [PATCH 0692/1778] Update comment --- pom.xml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index d77a3ed27..04eed5eb4 100644 --- a/pom.xml +++ b/pom.xml @@ -373,10 +373,9 @@ - - - - + + + strip-m2e-directives package From 07d8c3c1178771067bc4d26be954802ca9dde1d7 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 26 Mar 2020 18:13:20 -0600 Subject: [PATCH 0693/1778] Convert to use lambda --- .../classgraph/issues/issue193/Issue193Test.java | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/test/java/io/github/classgraph/issues/issue193/Issue193Test.java b/src/test/java/io/github/classgraph/issues/issue193/Issue193Test.java index 6a34fef0a..27e743113 100644 --- a/src/test/java/io/github/classgraph/issues/issue193/Issue193Test.java +++ b/src/test/java/io/github/classgraph/issues/issue193/Issue193Test.java @@ -40,8 +40,6 @@ import org.ops4j.pax.url.mvn.MavenResolvers; import io.github.classgraph.ClassGraph; -import io.github.classgraph.ClassInfo; -import io.github.classgraph.ClassInfoList.ClassInfoFilter; import io.github.classgraph.ScanResult; /** @@ -72,12 +70,8 @@ public void issue193Test() throws IOException { .scan()) { final List classes = scanResult // .getAllClasses() // - .filter(new ClassInfoFilter() { - @Override - public boolean accept(final ClassInfo ci) { - return ci.getName().endsWith("$"); - } - }).getNames(); + .filter(ci -> ci.getName().endsWith("$")) // + .getNames(); assertThat(classes).contains("scala.collection.immutable.Stack$"); } } From b9959ecebd11bfc96c1f4f75a52f56ff2d60e3ff Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 26 Mar 2020 18:28:46 -0600 Subject: [PATCH 0694/1778] Fix buffer underflow bug (#407) --- .../fileslice/reader/ClassfileReader.java | 14 ++-- .../issues/issue407/Issue407Test.java | 77 +++++++++++++++++++ 2 files changed, 86 insertions(+), 5 deletions(-) create mode 100644 src/test/java/io/github/classgraph/issues/issue407/Issue407Test.java 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 ac005134d..7c9492853 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 @@ -178,13 +178,17 @@ private void readTo(final int targetArrUsed) throws IOException { maxArrLen); // Double the size of the array if it's too small to contain the new chunk of bytes - if (arr.length < maxNewArrUsed) { - arr = Arrays.copyOf(arr, (int) Math.min(arr.length * 2L, maxArrLen)); + long newArrLength = arr.length; + while (newArrLength < maxNewArrUsed) { + newArrLength = Math.min(maxNewArrUsed, newArrLength * 2L); } + if (newArrLength > FileUtils.MAX_BUFFER_SIZE) { + throw new IOException("Hit 2GB limit while trying to grow buffer array"); + } + arr = Arrays.copyOf(arr, (int) Math.min(newArrLength, maxArrLen)); - // Figure out the maximum number of bytes that can be read into the array (which is the minimum - // of the number of requested bytes, and the space left in the array) - final int maxBytesToRead = Math.min(maxNewArrUsed - arrUsed, arr.length - arrUsed); + // Figure out the maximum number of bytes that can be read into the array + final int maxBytesToRead = arr.length - arrUsed; // Read a new chunk into the buffer, starting at position arrUsed if (inflaterInputStream != null) { diff --git a/src/test/java/io/github/classgraph/issues/issue407/Issue407Test.java b/src/test/java/io/github/classgraph/issues/issue407/Issue407Test.java new file mode 100644 index 000000000..179c3c8ff --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue407/Issue407Test.java @@ -0,0 +1,77 @@ +/* + * 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.issues.issue407; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.List; + +import org.junit.jupiter.api.Test; +import org.ops4j.pax.url.mvn.MavenResolvers; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ScanResult; + +/** + * Issue193Test. + */ +public class Issue407Test { + /** + * Issue 193 test. + * + * @throws IOException + * Signals that an I/O exception has occurred. + */ + @Test + public void issue407Test() throws IOException { + // Resolve and download scala-library + final File resolvedFile = MavenResolvers.createMavenResolver(null, null).resolve("com.google.guava", + "guava", null, null, "25.0-jre"); + assertThat(resolvedFile).isFile(); + + // Create a new custom class loader + final ClassLoader classLoader = new URLClassLoader(new URL[] { resolvedFile.toURI().toURL() }, null); + + // Scan the classpath -- used to throw an exception for Stack, since companion object inherits + // from different class + try (ScanResult scanResult = new ClassGraph() // + .whitelistPackages("com.google.thirdparty.publicsuffix") // + .overrideClassLoaders(classLoader) // + .scan()) { + final List classNames = scanResult // + .getAllClasses() // + .getNames(); + assertThat(classNames).contains("com.google.thirdparty.publicsuffix.PublicSuffixPatterns"); + } + } +} From e6800e39781254f8f8930145f1d311c851c3d6e2 Mon Sep 17 00:00:00 2001 From: Stephen Kahmann Date: Thu, 26 Mar 2020 23:15:31 -0400 Subject: [PATCH 0695/1778] Added optional resolution to jdk and sun packages --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 04eed5eb4..e919bc366 100644 --- a/pom.xml +++ b/pom.xml @@ -504,7 +504,7 @@ 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,jdk.internal.misc,sun.misc,sun.nio.ch + 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" true From 63f8c4700b644dc2ad93258a54013a6dfc86fe30 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 27 Mar 2020 01:13:56 -0600 Subject: [PATCH 0696/1778] Remove whitespace from manifest entries again (#408) --- pom.xml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e919bc366..e16361842 100644 --- a/pom.xml +++ b/pom.xml @@ -503,9 +503,11 @@ ${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,jdk.internal.misc;resolution:="optional",sun.misc;resolution:="optional",sun.nio.ch;resolution:="optional" - + + true From 31f399d48a67bc44f8c46fdaf38ad4d397ee5eeb Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 27 Mar 2020 01:14:14 -0600 Subject: [PATCH 0697/1778] Improve comments --- .../io/github/classgraph/utils/FileUtils.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 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 abb03e6c8..484aea5ee 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -361,18 +361,21 @@ private static void lookupCleanMethodPrivileged() { // See: https://stackoverflow.com/a/19447758/3950982 cleanMethod = Class.forName("sun.misc.Cleaner").getMethod("clean"); cleanMethod.setAccessible(true); - final Class directByteBufferClass = Class.forName("sun.nio.ch.DirectBuffer"); - attachmentMethod = directByteBufferClass.getMethod("attachment"); + attachmentMethod = Class.forName("sun.nio.ch.DirectBuffer").getMethod("attachment"); attachmentMethod.setAccessible(true); } catch (final SecurityException e) { throw ClassGraphException.newClassGraphException( - "You need to grant classgraph RuntimePermission(\"accessClassInPackage.sun.misc\") " + "You need to grant classgraph RuntimePermission(\"accessClassInPackage.sun.misc\"), " + + "RuntimePermission(\"accessClassInPackage.sun.nio.ch\"), " + "and ReflectPermission(\"suppressAccessChecks\")", e); - } catch (final ReflectiveOperationException | LinkageError ex) { + } catch (final ReflectiveOperationException | LinkageError e) { // Ignore } } else { + // 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. try { Class unsafeClass; try { @@ -382,11 +385,11 @@ private static void lookupCleanMethodPrivileged() { // but that method should be added if sun.misc.Unsafe is removed. unsafeClass = Class.forName("jdk.internal.misc.Unsafe"); } - cleanMethod = unsafeClass.getMethod("invokeCleaner", ByteBuffer.class); - cleanMethod.setAccessible(true); final Field theUnsafeField = unsafeClass.getDeclaredField("theUnsafe"); theUnsafeField.setAccessible(true); theUnsafe = theUnsafeField.get(null); + cleanMethod = unsafeClass.getMethod("invokeCleaner", ByteBuffer.class); + cleanMethod.setAccessible(true); } catch (final SecurityException e) { throw ClassGraphException.newClassGraphException( "You need to grant classgraph RuntimePermission(\"accessClassInPackage.sun.misc\"), " @@ -480,9 +483,6 @@ private static boolean closeDirectByteBufferPrivileged(final ByteBuffer byteBuff } return false; } - // In JDK9+, calling the above code gives a reflection warning on stderr, - // need to call Unsafe.theUnsafe.invokeCleaner(byteBuffer) , which makes - // the same call, but does not print the reflection warning. try { cleanMethod.invoke(theUnsafe, byteBuffer); return true; From c3615f55a6e09c090e0e0c1aacbb2045b65558c4 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 27 Mar 2020 09:26:18 -0600 Subject: [PATCH 0698/1778] [maven-release-plugin] prepare release classgraph-4.8.66 --- pom.xml | 38 ++++++++++---------------------------- 1 file changed, 10 insertions(+), 28 deletions(-) diff --git a/pom.xml b/pom.xml index e16361842..24ed1ea62 100644 --- a/pom.xml +++ b/pom.xml @@ -1,12 +1,9 @@ - + 4.0.0 io.github.classgraph classgraph - 4.8.66-SNAPSHOT + 4.8.66 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -279,8 +276,7 @@ - + org.codehaus.mojo.signature @@ -360,13 +356,8 @@ - - + + @@ -385,31 +376,22 @@ - + - + - + - + - + From 8c3164ab144db6abd8895a3de10732041e9b725b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 27 Mar 2020 09:26:27 -0600 Subject: [PATCH 0699/1778] [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 24ed1ea62..c34e0c0cb 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.66 + 4.8.67-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 97ecc93c802990bbebe9f9c7e653a653494b3cfc Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 27 Mar 2020 12:22:50 -0600 Subject: [PATCH 0700/1778] Increase Sonatype staging timeout --- pom.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c34e0c0cb..f12aa9561 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.67-SNAPSHOT + 4.8.66-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -544,6 +544,7 @@ ossrh https://oss.sonatype.org/ true + 10 From cb0fad045b18f2e7afda159cd6729b8c1105dc8e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 27 Mar 2020 12:23:20 -0600 Subject: [PATCH 0701/1778] [maven-release-plugin] prepare release classgraph-4.8.66 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f12aa9561..14b9779a0 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.66-SNAPSHOT + 4.8.66 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 9471c31bbcbf02746109070f1b3178bb5a1a4cfa Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 27 Mar 2020 12:23:28 -0600 Subject: [PATCH 0702/1778] [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 14b9779a0..bf2859db0 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.66 + 4.8.67-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From f86a482d90738a24f4b579a802ec039a74714446 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 27 Mar 2020 13:03:06 -0600 Subject: [PATCH 0703/1778] Try release again --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bf2859db0..f12aa9561 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.67-SNAPSHOT + 4.8.66-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 76eb039d46db78333129f28cc0df6ab8ff9c60a8 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 27 Mar 2020 13:03:41 -0600 Subject: [PATCH 0704/1778] [maven-release-plugin] prepare release classgraph-4.8.66 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f12aa9561..14b9779a0 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.66-SNAPSHOT + 4.8.66 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 00985951ab627a7b9bba80bff8c4ff1367bd68d1 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 27 Mar 2020 13:03:49 -0600 Subject: [PATCH 0705/1778] [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 14b9779a0..bf2859db0 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.66 + 4.8.67-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 11af49ae1464ccf469e90b3d31444e3903264afc Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 27 Mar 2020 14:02:55 -0600 Subject: [PATCH 0706/1778] Try release again --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bf2859db0..f12aa9561 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.67-SNAPSHOT + 4.8.66-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From bfa9b08b63afd4e1e09a61aff0e84d7edbe227ae Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 27 Mar 2020 14:03:26 -0600 Subject: [PATCH 0707/1778] [maven-release-plugin] prepare release classgraph-4.8.66 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f12aa9561..14b9779a0 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.66-SNAPSHOT + 4.8.66 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 0ef88eb104c9bc107ba0c254e0e96a49602fe09c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 27 Mar 2020 14:03:34 -0600 Subject: [PATCH 0708/1778] [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 14b9779a0..bf2859db0 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.66 + 4.8.67-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From b716abb274c4ee7c361712afc169a61891fab84e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 27 Mar 2020 14:09:52 -0600 Subject: [PATCH 0709/1778] Try release again --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bf2859db0..47ea829a2 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.66 + classgraph-4.8.67 From 52d141abb5be1de86f29d71927c5752e8296020c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 27 Mar 2020 14:10:42 -0600 Subject: [PATCH 0710/1778] Try release again --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 47ea829a2..3beb05d0f 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.67-SNAPSHOT + 4.8.66-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 8b14a6e1344569f05a3248f01b0a4849a2b408dc Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 27 Mar 2020 14:11:40 -0600 Subject: [PATCH 0711/1778] [maven-release-plugin] prepare release classgraph-4.8.66 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 3beb05d0f..14b9779a0 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.66-SNAPSHOT + 4.8.66 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.67 + classgraph-4.8.66 From dc8d4d2580596358a7903495619fc5132c161936 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 27 Mar 2020 14:11:47 -0600 Subject: [PATCH 0712/1778] [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 14b9779a0..47ea829a2 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.66 + 4.8.67-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.66 + classgraph-4.8.67 From a805b32a97b31e089a0b3cd83d00e34e1c748856 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 27 Mar 2020 14:47:23 -0600 Subject: [PATCH 0713/1778] Try release again --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 47ea829a2..3beb05d0f 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.67-SNAPSHOT + 4.8.66-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 9568f922eec53d5b0362241edebd59b292f218a0 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 27 Mar 2020 14:47:59 -0600 Subject: [PATCH 0714/1778] [maven-release-plugin] prepare release classgraph-4.8.66 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 3beb05d0f..14b9779a0 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.66-SNAPSHOT + 4.8.66 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.67 + classgraph-4.8.66 From f50668de26c2a716e57f5902f8ea60420c3d393d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 27 Mar 2020 14:48:07 -0600 Subject: [PATCH 0715/1778] [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 14b9779a0..47ea829a2 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.66 + 4.8.67-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.66 + classgraph-4.8.67 From f76d080d913ccbd86684cef7d49d8ec78e4b4b8f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 28 Mar 2020 01:04:36 -0600 Subject: [PATCH 0716/1778] Try release again --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 47ea829a2..f12aa9561 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.67-SNAPSHOT + 4.8.66-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.67 + classgraph-4.8.66 From 12e49cad686dc8a8279bdbd6ad83743a385a7bd5 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 Apr 2020 14:40:37 -0600 Subject: [PATCH 0717/1778] Tiny optimization --- .../nonapi/io/github/classgraph/classpath/ClasspathFinder.java | 2 ++ 1 file changed, 2 insertions(+) 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 d70208172..3fb376c93 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java @@ -120,6 +120,7 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { // Thread.currentThread().getContextClassLoader() [.getParent()] or similar if (classLoaderClassName.equals("jdk.internal.loader.ClassLoaders$AppClassLoader")) { scanModules = true; + break; } else if (classLoaderClassName.equals("jdk.internal.loader.ClassLoaders$PlatformClassLoader")) { scanModules = true; // The platform classloader was passed in, so specifically enable system module scanning @@ -131,6 +132,7 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { } scanSpec.enableSystemJarsAndModules = true; } + break; } } } else { From 8d05735a850a9ca1b35829ae1b83048df7416c2b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 Apr 2020 14:40:54 -0600 Subject: [PATCH 0718/1778] Revert last commit --- .../nonapi/io/github/classgraph/classpath/ClasspathFinder.java | 2 -- 1 file changed, 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 3fb376c93..d70208172 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java @@ -120,7 +120,6 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { // Thread.currentThread().getContextClassLoader() [.getParent()] or similar if (classLoaderClassName.equals("jdk.internal.loader.ClassLoaders$AppClassLoader")) { scanModules = true; - break; } else if (classLoaderClassName.equals("jdk.internal.loader.ClassLoaders$PlatformClassLoader")) { scanModules = true; // The platform classloader was passed in, so specifically enable system module scanning @@ -132,7 +131,6 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { } scanSpec.enableSystemJarsAndModules = true; } - break; } } } else { From 03a04fb1153aea0f134ce2c6e8373c670d797dbe Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 Apr 2020 14:57:33 -0600 Subject: [PATCH 0719/1778] Source > Cleanup --- .../classloaderhandler/QuarkusClassLoaderHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 af3f4e270..a057642eb 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/QuarkusClassLoaderHandler.java @@ -43,7 +43,7 @@ class QuarkusClassLoaderHandler implements ClassLoaderHandler { // Classloader until Quarkus 1.2 private static final String RUNTIME_CLASSLOADER = "io.quarkus.runner.RuntimeClassLoader"; - + // Classloader since Quarkus 1.3 private static final String QUARKUS_CLASSLOADER = "io.quarkus.bootstrap.classloading.QuarkusClassLoader"; From 7e0d9c32670216e78add963e90475ef6cddec798 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 Apr 2020 14:58:56 -0600 Subject: [PATCH 0720/1778] Fix module scanning when overriding with JPMS classloader (#411, #382) --- .../classgraph/classpath/CallStackReader.java | 110 +++++++++--------- .../classpath/ClassLoaderFinder.java | 14 +-- .../classgraph/classpath/ClasspathFinder.java | 18 ++- 3 files changed, 65 insertions(+), 77 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 fd4350cbe..772f60b8a 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/CallStackReader.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/CallStackReader.java @@ -41,6 +41,7 @@ /** A class to find the unique ordered classpath elements. */ class CallStackReader { + private static Class[] callStack; /** * Constructor. @@ -141,65 +142,66 @@ private static Class[] getCallStackViaSecurityManager(final LogNode log) { * @return The classes in the call stack. */ static Class[] getClassContext(final LogNode log) { - // 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 - Class[] stackClasses = null; - 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) { - // Invoke with doPrivileged -- see: - // http://mail.openjdk.java.net/pipermail/jigsaw-dev/2018-October/013974.html - stackClasses = AccessController.doPrivileged(new PrivilegedAction[]>() { - @Override - public Class[] run() { - return getCallStackViaStackWalker(); - } - }); - } + 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) { + // 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(); + } + }); + } - // For JRE 7 and 8, use SecurityManager to get call stack - if (stackClasses == null || stackClasses.length == 0) { - stackClasses = AccessController.doPrivileged(new PrivilegedAction[]>() { - @Override - public Class[] run() { - return getCallStackViaSecurityManager(log); - } - }); - } + // 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); + } + }); + } - // As a fallback, use getStackTrace() to try to get the call stack - if (stackClasses == null || stackClasses.length == 0) { - StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); - if (stackTrace == null || stackTrace.length == 0) { - try { - throw new Exception(); - } catch (final Exception e) { - stackTrace = e.getStackTrace(); + // As a fallback, use getStackTrace() to try to get the call stack + if (callStack == null || callStack.length == 0) { + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + if (stackTrace == null || stackTrace.length == 0) { + try { + 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 + 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]); + } else { + // Last-ditch effort -- include just this class in the call stack + callStack = new Class[] { CallStackReader.class }; } - } - if (!stackClassesList.isEmpty()) { - stackClasses = stackClassesList.toArray(new Class[0]); - } else { - // Last-ditch effort -- include just this class in the call stack - stackClasses = new Class[] { CallStackReader.class }; } } - return stackClasses; + return callStack; } } 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 51f3aa6d5..4368bef07 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClassLoaderFinder.java @@ -38,9 +38,6 @@ public class ClassLoaderFinder { /** The context class loaders. */ private final ClassLoader[] contextClassLoaders; - /** The callstack. */ - private Class[] callStack; - // ------------------------------------------------------------------------------------------------------------- /** @@ -52,15 +49,6 @@ public ClassLoader[] getContextClassLoaders() { return contextClassLoaders; } - /** - * Get classes in the callstack. - * - * @return classes in the callstack. - */ - public Class[] getCallStack() { - return callStack; - } - // ------------------------------------------------------------------------------------------------------------- /** @@ -113,7 +101,7 @@ public Class[] getCallStack() { // Find classloaders for classes on callstack, in case any were missed try { - callStack = CallStackReader.getClassContext(log); + final Class[] callStack = CallStackReader.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/ClasspathFinder.java b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java index d70208172..776314278 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java @@ -97,11 +97,6 @@ public ClassLoader[] getClassLoaderOrderRespectingParentDelegation() { public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { final LogNode classpathFinderLog = log == null ? null : log.log("Finding classpath and modules"); - // 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) - : null; - // 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) @@ -139,18 +134,21 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { scanModules = scanSpec.scanModules; } - moduleFinder = scanModules && classLoaderFinder != null - ? new ModuleFinder(classLoaderFinder.getCallStack(), scanSpec, classpathFinderLog) + moduleFinder = scanModules + ? new ModuleFinder(CallStackReader.getClassContext(classpathFinderLog), scanSpec, + classpathFinderLog) : null; classpathOrder = new ClasspathOrder(scanSpec); final ClasspathOrder ignoredClasspathOrder = new ClasspathOrder(scanSpec); + // 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) + : null; final ClassLoader[] contextClassLoaders = classLoaderFinder == null ? new ClassLoader[0] : classLoaderFinder.getContextClassLoaders(); - final ClassLoader defaultClassLoader = contextClassLoaders != null && contextClassLoaders.length > 0 - ? contextClassLoaders[0] - : null; + final ClassLoader defaultClassLoader = contextClassLoaders.length > 0 ? contextClassLoaders[0] : null; if (scanSpec.overrideClasspath != null) { // Manual classpath override if (scanSpec.overrideClassLoaders != null && classpathFinderLog != null) { From 690da39f6b5e8d3f9e6aa2d52faf2922c6960d15 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 Apr 2020 15:00:01 -0600 Subject: [PATCH 0721/1778] [maven-release-plugin] prepare release classgraph-4.8.66 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f12aa9561..14b9779a0 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.66-SNAPSHOT + 4.8.66 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 15a4584b5fee6ff78d5698b281b5662982095ac6 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 Apr 2020 15:05:30 -0600 Subject: [PATCH 0722/1778] Further fix for #411, #382 --- .../nonapi/io/github/classgraph/classpath/ModuleFinder.java | 4 +--- 1 file changed, 1 insertion(+), 3 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 8812f46e2..53292cdb7 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ModuleFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ModuleFinder.java @@ -245,9 +245,7 @@ private static List findModuleRefsFromCallstack(final Class[] call * The log. */ public ModuleFinder(final Class[] callStack, final ScanSpec scanSpec, final LogNode log) { - final boolean disableModules = scanSpec.overrideClassLoaders != null || scanSpec.overrideClasspath != null; - - if (!disableModules) { + if (scanSpec.scanModules) { // Get the module resolution order List allModuleRefsList = null; if (scanSpec.overrideModuleLayers == null) { From bdb8a614c4408ea0a87d0a59a513151668511ffd Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 Apr 2020 15:07:01 -0600 Subject: [PATCH 0723/1778] Try release again --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 14b9779a0..bf2859db0 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.66 + 4.8.67-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 8c35bb16de6609a4a218f3b05faf4923fe86e236 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 Apr 2020 15:07:28 -0600 Subject: [PATCH 0724/1778] [maven-release-plugin] prepare release classgraph-4.8.67 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index bf2859db0..65b1a79b5 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.67-SNAPSHOT + 4.8.67 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.66 + classgraph-4.8.67 From 5ce41a23da7a99e1f88d890421c39f826b66ec51 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 Apr 2020 15:07:36 -0600 Subject: [PATCH 0725/1778] [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 65b1a79b5..19979cb56 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.67 + 4.8.68-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.67 + classgraph-4.8.66 From 292e372ed2a137db555559aefd6f33854cf5f627 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 Apr 2020 15:10:21 -0600 Subject: [PATCH 0726/1778] Try release again, without m2e tag --- pom.xml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 19979cb56..76cc44444 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.68-SNAPSHOT + 4.8.67-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.66 @@ -230,9 +229,6 @@ org.apache.maven.plugins maven-enforcer-plugin - - - org.codehaus.mojo From cadb501c847d6876727a8aed68aeecaa6487f107 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 Apr 2020 15:11:23 -0600 Subject: [PATCH 0727/1778] [maven-release-plugin] prepare release classgraph-4.8.67 --- pom.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 76cc44444..287a8ce62 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.67-SNAPSHOT + 4.8.67 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.67 + https://github.com/classgraph/classgraph/issues From d0c15a4665a54af16802026bd28fafa0c227a434 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 1 Apr 2020 15:11:31 -0600 Subject: [PATCH 0728/1778] [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 287a8ce62..d6d2eeab9 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.67 + 4.8.68-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.67 + HEAD From 047311772a0f2914d557d5fc3daa365fe1089164 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 3 Apr 2020 11:15:50 -0600 Subject: [PATCH 0729/1778] Attempt to fix #412 --- .../classgraph/classpath/ClasspathFinder.java | 7 +++++-- .../classgraph/classpath/ModuleFinder.java | 20 ++++++++++++++----- 2 files changed, 20 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 776314278..bc24ff718 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java @@ -264,8 +264,11 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { // Get classpath elements from java.class.path, but don't add them if the element is in an ignored // parent classloader and not in a child classloader (and don't use java.class.path at all if - // overrideClassLoaders is true or overrideClasspath is set) - if (scanSpec.overrideClassLoaders == null && scanSpec.overrideClasspath == null) { + // overrideClassLoaders is true or overrideClasspath is set). However, if only module scanning was + // enabled, and an unnamed module layer was encountered, have to forcibly scan java.class.path, + // since the ModuleLayer API doesn't allow you to open unnamed modules. + if ((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 53292cdb7..dc391c0bf 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ModuleFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ModuleFinder.java @@ -51,6 +51,9 @@ public class ModuleFinder { /** The non system module refs. */ private List nonSystemModuleRefs; + /** If true, must forcibly scan {@code java.class.path}, since there was an anonymous module layer. */ + private boolean forceScanJavaClassPath; + // ------------------------------------------------------------------------------------------------------------- /** @@ -73,6 +76,11 @@ public List getNonSystemModuleRefs() { return nonSystemModuleRefs; } + /** @return If true, must forcibly scan {@code java.class.path}, since there was an anonymous module layer. */ + public boolean forceScanJavaClassPath() { + return forceScanJavaClassPath; + } + // ------------------------------------------------------------------------------------------------------------- /** @@ -208,10 +216,9 @@ private static List findModuleRefsFromCallstack(final Class[] call if (module != null) { final Object /* ModuleLayer */ layer = ReflectionUtils.invokeMethod(module, "getLayer", /* throwException = */ true); - // getLayer() returns null for unnamed modules -- have to get their classes from java.class.path - if (layer != null) { - layers.add(layer); - } + // getLayer() returns null for unnamed modules -- still add null to list if it is returned, + // so we can get classes from java.class.path + layers.add(layer); } } } @@ -268,7 +275,10 @@ public ModuleFinder(final Class[] callStack, final ScanSpec scanSpec, final L systemModuleRefs = new ArrayList<>(); nonSystemModuleRefs = new ArrayList<>(); for (final ModuleRef moduleRef : allModuleRefsList) { - if (moduleRef.isSystemModule()) { + if (moduleRef == null) { + // getLayer() returns null for unnamed modules -- have to get classes from java.class.path + forceScanJavaClassPath = true; + } else if (moduleRef.isSystemModule()) { systemModuleRefs.add(moduleRef); } else { nonSystemModuleRefs.add(moduleRef); From 0335a336fe75cf65acc0caf3e9bbd911e890737d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 3 Apr 2020 12:18:39 -0600 Subject: [PATCH 0730/1778] Fix previous commit for #412 --- .../classgraph/classpath/ModuleFinder.java | 38 ++++++++++++------- 1 file changed, 25 insertions(+), 13 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 dc391c0bf..97a8923db 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ModuleFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ModuleFinder.java @@ -139,11 +139,15 @@ private static List findModuleRefs(final LinkedHashSet layers final Deque /* Deque */ layerOrder = new ArrayDeque<>(); final Set /* Set(); for (final Object layer : layers) { - findLayerOrder(layer, /* layerVisited = */ new HashSet<>(), parentLayers, layerOrder); + if (layer != null) { + findLayerOrder(layer, /* layerVisited = */ new HashSet<>(), parentLayers, layerOrder); + } } if (scanSpec.addedModuleLayers != null) { for (final Object layer : scanSpec.addedModuleLayers) { - findLayerOrder(layer, /* layerVisited = */ new HashSet<>(), parentLayers, layerOrder); + if (layer != null) { + findLayerOrder(layer, /* layerVisited = */ new HashSet<>(), parentLayers, layerOrder); + } } } @@ -206,7 +210,7 @@ private static List findModuleRefs(final LinkedHashSet layers * the log * @return the list */ - private static List findModuleRefsFromCallstack(final Class[] callStack, final ScanSpec scanSpec, + private List findModuleRefsFromCallstack(final Class[] callStack, final ScanSpec scanSpec, final LogNode log) { final LinkedHashSet layers = new LinkedHashSet<>(); if (callStack != null) { @@ -216,9 +220,13 @@ private static List findModuleRefsFromCallstack(final Class[] call if (module != null) { final Object /* ModuleLayer */ layer = ReflectionUtils.invokeMethod(module, "getLayer", /* throwException = */ true); - // getLayer() returns null for unnamed modules -- still add null to list if it is returned, - // so we can get classes from java.class.path - layers.add(layer); + if (layer != null) { + layers.add(layer); + } else { + // 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; + } } } } @@ -234,6 +242,11 @@ private static List findModuleRefsFromCallstack(final Class[] call /* throwException = */ false); if (bootLayer != null) { layers.add(bootLayer); + } else { + // 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.) + forceScanJavaClassPath = true; } } return findModuleRefs(layers, scanSpec, log); @@ -275,13 +288,12 @@ public ModuleFinder(final Class[] callStack, final ScanSpec scanSpec, final L systemModuleRefs = new ArrayList<>(); nonSystemModuleRefs = new ArrayList<>(); for (final ModuleRef moduleRef : allModuleRefsList) { - if (moduleRef == null) { - // getLayer() returns null for unnamed modules -- have to get classes from java.class.path - forceScanJavaClassPath = true; - } else if (moduleRef.isSystemModule()) { - systemModuleRefs.add(moduleRef); - } else { - nonSystemModuleRefs.add(moduleRef); + if (moduleRef != null) { + if (moduleRef.isSystemModule()) { + systemModuleRefs.add(moduleRef); + } else { + nonSystemModuleRefs.add(moduleRef); + } } } } From 332a6513bcc19b9d58e779528082cee110efa783 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 3 Apr 2020 17:29:59 -0600 Subject: [PATCH 0731/1778] Unit test for #413 --- .../issues/issue413/Issue413Test.java | 58 +++++++++++++++++++ .../issues/issue413/RegressionTarget.java | 47 +++++++++++++++ .../classgraph/issues/issue413/Source.java | 40 +++++++++++++ 3 files changed, 145 insertions(+) create mode 100644 src/test/java/io/github/classgraph/issues/issue413/Issue413Test.java create mode 100644 src/test/java/io/github/classgraph/issues/issue413/RegressionTarget.java create mode 100644 src/test/java/io/github/classgraph/issues/issue413/Source.java diff --git a/src/test/java/io/github/classgraph/issues/issue413/Issue413Test.java b/src/test/java/io/github/classgraph/issues/issue413/Issue413Test.java new file mode 100644 index 000000000..603a91616 --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue413/Issue413Test.java @@ -0,0 +1,58 @@ +/* + * This file is part of ClassGraph. + * + * Author: Contributed to ClassGraph by GitHub user @PyangDizzle + * + * 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.issues.issue413; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ClassInfo; +import io.github.classgraph.ClassInfoList; +import io.github.classgraph.ScanResult; + +/** + * Issue193Test. + */ +public class Issue413Test { + /** + * Issue 413 test. + */ + @Test + public void issue413Test() { + String typeName = RegressionTarget.class.getName(); + ScanResult sr = new ClassGraph().whitelistPackages(getClass().getPackage().getName()) + .enableInterClassDependencies().whitelistClasses(typeName).scan(); + + ClassInfo child = sr.getClassInfo(typeName); + ClassInfoList dependentTypes = child.getClassDependencies(); + + assertTrue(dependentTypes.toString().contains("Source")); + } +} diff --git a/src/test/java/io/github/classgraph/issues/issue413/RegressionTarget.java b/src/test/java/io/github/classgraph/issues/issue413/RegressionTarget.java new file mode 100644 index 000000000..27ef13eee --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue413/RegressionTarget.java @@ -0,0 +1,47 @@ +/* + * This file is part of ClassGraph. + * + * Author: Contributed to ClassGraph by GitHub user @PyangDizzle + * + * 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.issues.issue413; + +/** + * RegressionTarget. + */ +class RegressionTarget { + /** + * Do something. + */ + @SuppressWarnings("static-access") + public static void doSomething() { + Source.num = 123; + Source.obj = new Source(); + Source local = new Source(); + local.num = 321; + System.out.println(Source.num); + System.out.println(local.num); + } +} \ No newline at end of file diff --git a/src/test/java/io/github/classgraph/issues/issue413/Source.java b/src/test/java/io/github/classgraph/issues/issue413/Source.java new file mode 100644 index 000000000..7829fe76c --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue413/Source.java @@ -0,0 +1,40 @@ +/* + * This file is part of ClassGraph. + * + * Author: Contributed to ClassGraph by GitHub user @PyangDizzle + * + * 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.issues.issue413; + +/** + * Source. + */ +class Source { + /** num. */ + public static int num; + + /** obj. */ + public static Source obj; +} \ No newline at end of file From 2e1fac0e2835a6420339111c3865591ad5dd69a0 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 3 Apr 2020 19:15:06 -0600 Subject: [PATCH 0732/1778] Update comment --- 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 a700f033c..266c75fa8 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -131,7 +131,10 @@ public class ClassInfo extends ScanResultObject implements Comparable */ private Set referencedClassNames; - /** A list of ClassInfo objects for classes referenced by this class. */ + /** + * A list of ClassInfo objects for classes referenced by this class. Derived from {@link #referencedClassNames} + * when the relevant {@link ClassInfo} objects are created. + */ private ClassInfoList referencedClasses; /** @@ -2822,7 +2825,6 @@ protected void findReferencedClassInfo(final Map classNameToC final Set refdClassInfo) { // Add this class to the set of references super.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); - if (this.referencedClassNames != null) { for (final String refdClassName : this.referencedClassNames) { final ClassInfo classInfo = ClassInfo.getOrCreateClassInfo(refdClassName, classNameToClassInfo); From d36f701734c979c5f6b05e6ae7732fb76da64fd1 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 3 Apr 2020 19:15:56 -0600 Subject: [PATCH 0733/1778] Remove unit test (not a bug) --- .../issues/issue413/Issue413Test.java | 58 ------------------- .../issues/issue413/RegressionTarget.java | 47 --------------- .../classgraph/issues/issue413/Source.java | 40 ------------- 3 files changed, 145 deletions(-) delete mode 100644 src/test/java/io/github/classgraph/issues/issue413/Issue413Test.java delete mode 100644 src/test/java/io/github/classgraph/issues/issue413/RegressionTarget.java delete mode 100644 src/test/java/io/github/classgraph/issues/issue413/Source.java diff --git a/src/test/java/io/github/classgraph/issues/issue413/Issue413Test.java b/src/test/java/io/github/classgraph/issues/issue413/Issue413Test.java deleted file mode 100644 index 603a91616..000000000 --- a/src/test/java/io/github/classgraph/issues/issue413/Issue413Test.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * This file is part of ClassGraph. - * - * Author: Contributed to ClassGraph by GitHub user @PyangDizzle - * - * 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.issues.issue413; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -import org.junit.jupiter.api.Test; - -import io.github.classgraph.ClassGraph; -import io.github.classgraph.ClassInfo; -import io.github.classgraph.ClassInfoList; -import io.github.classgraph.ScanResult; - -/** - * Issue193Test. - */ -public class Issue413Test { - /** - * Issue 413 test. - */ - @Test - public void issue413Test() { - String typeName = RegressionTarget.class.getName(); - ScanResult sr = new ClassGraph().whitelistPackages(getClass().getPackage().getName()) - .enableInterClassDependencies().whitelistClasses(typeName).scan(); - - ClassInfo child = sr.getClassInfo(typeName); - ClassInfoList dependentTypes = child.getClassDependencies(); - - assertTrue(dependentTypes.toString().contains("Source")); - } -} diff --git a/src/test/java/io/github/classgraph/issues/issue413/RegressionTarget.java b/src/test/java/io/github/classgraph/issues/issue413/RegressionTarget.java deleted file mode 100644 index 27ef13eee..000000000 --- a/src/test/java/io/github/classgraph/issues/issue413/RegressionTarget.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * This file is part of ClassGraph. - * - * Author: Contributed to ClassGraph by GitHub user @PyangDizzle - * - * 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.issues.issue413; - -/** - * RegressionTarget. - */ -class RegressionTarget { - /** - * Do something. - */ - @SuppressWarnings("static-access") - public static void doSomething() { - Source.num = 123; - Source.obj = new Source(); - Source local = new Source(); - local.num = 321; - System.out.println(Source.num); - System.out.println(local.num); - } -} \ No newline at end of file diff --git a/src/test/java/io/github/classgraph/issues/issue413/Source.java b/src/test/java/io/github/classgraph/issues/issue413/Source.java deleted file mode 100644 index 7829fe76c..000000000 --- a/src/test/java/io/github/classgraph/issues/issue413/Source.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * This file is part of ClassGraph. - * - * Author: Contributed to ClassGraph by GitHub user @PyangDizzle - * - * 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.issues.issue413; - -/** - * Source. - */ -class Source { - /** num. */ - public static int num; - - /** obj. */ - public static Source obj; -} \ No newline at end of file From 02c1237506d74fc136cb2999c16772b49727a553 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 4 Apr 2020 11:01:27 -0600 Subject: [PATCH 0734/1778] Fix indentation --- pom.xml | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/pom.xml b/pom.xml index d6d2eeab9..4bcd9dc49 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,7 @@ - + 4.0.0 io.github.classgraph @@ -31,8 +34,8 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - HEAD - + HEAD + https://github.com/classgraph/classgraph/issues @@ -273,7 +276,8 @@ - + org.codehaus.mojo.signature @@ -353,8 +357,13 @@ - - + + @@ -373,22 +382,31 @@ - + - + - + - + - + From 3742058db41e2715582a7ee78e6cf71a6220db26 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 4 Apr 2020 11:02:23 -0600 Subject: [PATCH 0735/1778] [maven-release-plugin] prepare release classgraph-4.8.68 --- pom.xml | 40 +++++++++++----------------------------- 1 file changed, 11 insertions(+), 29 deletions(-) diff --git a/pom.xml b/pom.xml index 4bcd9dc49..5e2f8fbab 100644 --- a/pom.xml +++ b/pom.xml @@ -1,12 +1,9 @@ - + 4.0.0 io.github.classgraph classgraph - 4.8.68-SNAPSHOT + 4.8.68 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 - HEAD + classgraph-4.8.68 @@ -276,8 +273,7 @@ - + org.codehaus.mojo.signature @@ -357,13 +353,8 @@ - - + + @@ -382,31 +373,22 @@ - + - + - + - + - + From 4bb38c0bfd30b3d1ac78b67af355c5d04b76e206 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 4 Apr 2020 11:02:31 -0600 Subject: [PATCH 0736/1778] [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 5e2f8fbab..72b673c53 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.68 + 4.8.69-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.68 + HEAD From 446f0fa49065570625f009eebd655de44e06c946 Mon Sep 17 00:00:00 2001 From: Christopher Pierce Date: Sat, 11 Apr 2020 00:10:18 -0400 Subject: [PATCH 0737/1778] Fix for Libery running in "overlay" Use SmartClassPath's "getClassPath" to get the classpath URL(s). --- .../WebsphereLibertyClassLoaderHandler.java | 118 +++++++++++++++--- 1 file changed, 99 insertions(+), 19 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 e651c1988..ed9514d7e 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java @@ -29,6 +29,10 @@ package nonapi.io.github.classgraph.classloaderhandler; import java.io.File; +import java.net.URL; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; import java.util.List; import nonapi.io.github.classgraph.classpath.ClassLoaderOrder; @@ -90,40 +94,106 @@ public static void findClassLoaderOrder(final ClassLoader classLoader, final Cla } /** - * Get the path from a classpath object. + * Get the paths from a containerClassLoader object. * - * @param classpath - * the classpath object - * @return the path object as a {@link File} or {@link String}. + *

+ * The passed in object should be an instance of + * "com.ibm.ws.classloading.internal.ContainerClassLoader". + *

+ * Will attempt to use "getContainerURLs" methods to recap the classpath. + * + * @param containerClassLoader + * the containerClassLoader object + * @return Collection of path objects as a {@link URL} or {@link String}. */ - private static String getPath(final Object classpath) { - final Object container = ReflectionUtils.getFieldVal(classpath, "container", false); + private static Collection getPaths(final Object containerClassLoader) { + if(containerClassLoader == null) { + return null; + } + + // 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"); + if(urls != null && !urls.isEmpty()) { + return urls; + } + + // "getContainerURLs" didn't work, try getting the container object... + final Object container = ReflectionUtils.getFieldVal(containerClassLoader, "container", false); if (container == null) { - return ""; + return null; } + // Should be an instance of "com.ibm.wsspi.adaptable.module.Container". + // Call "getURLs" to get its classpath. + urls = callGetUrls(container, "getURLs"); + 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(container, "delegate", false); if (delegate == null) { - return ""; + return null; } final String path = (String) ReflectionUtils.getFieldVal(delegate, "path", false); if (path != null && path.length() > 0) { - return path; + return Arrays.asList((Object)path); } final Object base = ReflectionUtils.getFieldVal(delegate, "base", false); if (base == null) { // giving up. - return ""; + return null; } final Object archiveFile = ReflectionUtils.getFieldVal(base, "archiveFile", false); if (archiveFile != null) { final File file = (File) archiveFile; - return file.getAbsolutePath(); + return Arrays.asList((Object)file.getAbsolutePath()); + } + return null; + } + + /** + * Utility to call a "getURLs" method, flattening + * "collections of collections" and ignoring + * "UnsupportedOperationException". + * + * All of the "getURLs" methods eventually call + * "com.ibm.wsspi.adaptable.module.Container#getURLs()". + * + * https://www.ibm.com/support/knowledgecenter/SSEQTP_liberty/com.ibm.websphere.javadoc.liberty.doc + * /com.ibm.websphere.appserver.spi.artifact_1.2-javadoc + * /com/ibm/wsspi/adaptable/module/Container.html?view=embed#getURLs() + * "A collection of URLs that represent all of the locations on disk that contribute to this container" + */ + @SuppressWarnings("unchecked") + private static Collection callGetUrls(final Object container, final String methodName) { + if(container != null) { + try { + final Collection results = (Collection) ReflectionUtils.invokeMethod(container, methodName, false); + if(results != null && !results.isEmpty()) { + final Collection allUrls = new HashSet<>(); + for(final Object result : results) { + if(result instanceof Collection) { + // SmartClassPath returns collection of collection of URLs. + for(final Object url : ((Collection) result)) { + if(url != null) { + allUrls.add(url); + } + } + } else if(result != null){ + allUrls.add(result); + } + } + return allUrls; + } + } catch(final UnsupportedOperationException e) {/* ignore */} } - return ""; + return null; } /** @@ -148,13 +218,23 @@ public static void findClasspathOrder(final ClassLoader classLoader, final Class smartClassPath = ReflectionUtils.getFieldVal(classLoader, "smartClassPath", false); } if (smartClassPath != null) { - final List classPathElements = (List) ReflectionUtils.getFieldVal(smartClassPath, "classPath", - false); - if (classPathElements != null) { - for (final Object classpath : classPathElements) { - final String path = getPath(classpath); - if (path != null && path.length() > 0) { - classpathOrder.addClasspathEntry(path, classLoader, scanSpec, log); + // "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"); + if(paths != null && !paths.isEmpty()) { + for(final Object path : paths) { + classpathOrder.addClasspathEntry(path, classLoader, scanSpec, log); + } + } else { + // "getClassPath" didn't work... reverting to looping over "classPath" elements. + @SuppressWarnings("unchecked") + final List classPathElements = (List) ReflectionUtils.getFieldVal(smartClassPath, "classPath", false); + if(classPathElements != null && !classPathElements.isEmpty()) { + for(final Object classPathElement : classPathElements) { + final Collection subPaths = getPaths(classPathElement); + for(final Object path : subPaths) { + classpathOrder.addClasspathEntry(path, classLoader, scanSpec, log); + } } } } From 53d71bd8b7ff9e51f91046d42ccd2b5acc2e2145 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 10 Apr 2020 22:34:14 -0600 Subject: [PATCH 0738/1778] Source > Cleanup --- .../WebsphereLibertyClassLoaderHandler.java | 58 +++++++++---------- 1 file changed, 29 insertions(+), 29 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 ed9514d7e..90d87ca8a 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java @@ -97,8 +97,7 @@ public static void findClassLoaderOrder(final ClassLoader classLoader, final Cla * Get the paths from a containerClassLoader object. * *

- * The passed in object should be an instance of - * "com.ibm.ws.classloading.internal.ContainerClassLoader". + * The passed in object should be an instance of "com.ibm.ws.classloading.internal.ContainerClassLoader". *

* Will attempt to use "getContainerURLs" methods to recap the classpath. * @@ -107,7 +106,7 @@ public static void findClassLoaderOrder(final ClassLoader classLoader, final Cla * @return Collection of path objects as a {@link URL} or {@link String}. */ private static Collection getPaths(final Object containerClassLoader) { - if(containerClassLoader == null) { + if (containerClassLoader == null) { return null; } @@ -115,7 +114,7 @@ private static Collection getPaths(final Object containerClassLoader) { // "com.ibm.ws.classloading.internal.ContainerClassLoader$UniversalContainer". // Call "getContainerURLs" to get its container's classpath. Collection urls = callGetUrls(containerClassLoader, "getContainerURLs"); - if(urls != null && !urls.isEmpty()) { + if (urls != null && !urls.isEmpty()) { return urls; } @@ -128,7 +127,7 @@ private static Collection getPaths(final Object containerClassLoader) { // Should be an instance of "com.ibm.wsspi.adaptable.module.Container". // Call "getURLs" to get its classpath. urls = callGetUrls(container, "getURLs"); - if(urls != null && !urls.isEmpty()) { + if (urls != null && !urls.isEmpty()) { return urls; } @@ -140,7 +139,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 Arrays.asList((Object) path); } final Object base = ReflectionUtils.getFieldVal(delegate, "base", false); @@ -152,46 +151,46 @@ 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 Arrays.asList((Object) file.getAbsolutePath()); } return null; } /** - * Utility to call a "getURLs" method, flattening - * "collections of collections" and ignoring + * Utility to call a "getURLs" method, flattening "collections of collections" and ignoring * "UnsupportedOperationException". * - * All of the "getURLs" methods eventually call - * "com.ibm.wsspi.adaptable.module.Container#getURLs()". + * All of the "getURLs" methods eventually call "com.ibm.wsspi.adaptable.module.Container#getURLs()". * * https://www.ibm.com/support/knowledgecenter/SSEQTP_liberty/com.ibm.websphere.javadoc.liberty.doc - * /com.ibm.websphere.appserver.spi.artifact_1.2-javadoc - * /com/ibm/wsspi/adaptable/module/Container.html?view=embed#getURLs() - * "A collection of URLs that represent all of the locations on disk that contribute to this container" + * /com.ibm.websphere.appserver.spi.artifact_1.2-javadoc + * /com/ibm/wsspi/adaptable/module/Container.html?view=embed#getURLs() "A collection of URLs that represent all + * of the locations on disk that contribute to this container" */ @SuppressWarnings("unchecked") private static Collection callGetUrls(final Object container, final String methodName) { - if(container != null) { + if (container != null) { try { - final Collection results = (Collection) ReflectionUtils.invokeMethod(container, methodName, false); - if(results != null && !results.isEmpty()) { + final Collection results = (Collection) ReflectionUtils.invokeMethod(container, + methodName, false); + if (results != null && !results.isEmpty()) { final Collection allUrls = new HashSet<>(); - for(final Object result : results) { - if(result instanceof Collection) { + for (final Object result : results) { + if (result instanceof Collection) { // SmartClassPath returns collection of collection of URLs. - for(final Object url : ((Collection) result)) { - if(url != null) { + for (final Object url : ((Collection) result)) { + if (url != null) { allUrls.add(url); } } - } else if(result != null){ + } else if (result != null) { allUrls.add(result); } } return allUrls; } - } catch(final UnsupportedOperationException e) {/* ignore */} + } catch (final UnsupportedOperationException e) { + /* ignore */} } return null; } @@ -221,18 +220,19 @@ public static void findClasspathOrder(final ClassLoader classLoader, final Class // "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"); - if(paths != null && !paths.isEmpty()) { - for(final Object path : paths) { + if (paths != null && !paths.isEmpty()) { + for (final Object path : paths) { classpathOrder.addClasspathEntry(path, classLoader, scanSpec, log); } } else { // "getClassPath" didn't work... reverting to looping over "classPath" elements. @SuppressWarnings("unchecked") - final List classPathElements = (List) ReflectionUtils.getFieldVal(smartClassPath, "classPath", false); - if(classPathElements != null && !classPathElements.isEmpty()) { - for(final Object classPathElement : classPathElements) { + final List classPathElements = (List) ReflectionUtils.getFieldVal(smartClassPath, + "classPath", false); + if (classPathElements != null && !classPathElements.isEmpty()) { + for (final Object classPathElement : classPathElements) { final Collection subPaths = getPaths(classPathElement); - for(final Object path : subPaths) { + for (final Object path : subPaths) { classpathOrder.addClasspathEntry(path, classLoader, scanSpec, log); } } From 7897c3ebaaa779b348c13baa283defecd9187827 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 10 Apr 2020 22:37:13 -0600 Subject: [PATCH 0739/1778] Formatting and comments --- .../WebsphereLibertyClassLoaderHandler.java | 7 +++++-- 1 file changed, 5 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 90d87ca8a..700a991da 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java @@ -3,13 +3,15 @@ * * Author: R. Kempees * + * With contributions from @cpierceworld (#414) + * * Hosted at: https://github.com/classgraph/classgraph * * -- * * The MIT License (MIT) * - * Copyright (c) 2017 R. Kempees + * Copyright (c) 2017 R. Kempees (contributed to the ClassGraph project) * * 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 @@ -190,7 +192,8 @@ private static Collection callGetUrls(final Object container, final Stri return allUrls; } } catch (final UnsupportedOperationException e) { - /* ignore */} + /* ignore */ + } } return null; } From 2ad71a2d8f9b7fe89d49e4b74c869937b9db3c33 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 10 Apr 2020 22:47:38 -0600 Subject: [PATCH 0740/1778] Fix possible NPE (#414) --- .../WebsphereLibertyClassLoaderHandler.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 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 700a991da..835f5bff8 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/WebsphereLibertyClassLoaderHandler.java @@ -34,6 +34,7 @@ import java.net.URL; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -109,7 +110,7 @@ public static void findClassLoaderOrder(final ClassLoader classLoader, final Cla */ private static Collection getPaths(final Object containerClassLoader) { if (containerClassLoader == null) { - return null; + return Collections. emptyList(); } // Expecting this to be an instance of @@ -123,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 null; + return Collections. emptyList(); } // Should be an instance of "com.ibm.wsspi.adaptable.module.Container". @@ -136,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 null; + return Collections. emptyList(); } final String path = (String) ReflectionUtils.getFieldVal(delegate, "path", false); @@ -147,7 +148,7 @@ private static Collection getPaths(final Object containerClassLoader) { final Object base = ReflectionUtils.getFieldVal(delegate, "base", false); if (base == null) { // giving up. - return null; + return Collections. emptyList(); } final Object archiveFile = ReflectionUtils.getFieldVal(base, "archiveFile", false); @@ -155,7 +156,7 @@ private static Collection getPaths(final Object containerClassLoader) { final File file = (File) archiveFile; return Arrays.asList((Object) file.getAbsolutePath()); } - return null; + return Collections. emptyList(); } /** @@ -195,7 +196,7 @@ private static Collection callGetUrls(final Object container, final Stri /* ignore */ } } - return null; + return Collections. emptyList(); } /** @@ -223,7 +224,7 @@ public static void findClasspathOrder(final ClassLoader classLoader, final Class // "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"); - if (paths != null && !paths.isEmpty()) { + if (!paths.isEmpty()) { for (final Object path : paths) { classpathOrder.addClasspathEntry(path, classLoader, scanSpec, log); } From 917d45ea675d3bab41f974db14b7d6ba226622c7 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 11 Apr 2020 02:17:47 -0600 Subject: [PATCH 0741/1778] [maven-release-plugin] prepare release classgraph-4.8.69 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 72b673c53..6451acf49 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.69-SNAPSHOT + 4.8.69 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.69 From 5699f3f89291eba5909494f523c488b038e353ff Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 11 Apr 2020 02:17:56 -0600 Subject: [PATCH 0742/1778] [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 6451acf49..c9171c0a9 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.69 + 4.8.70-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.69 + HEAD From 872e6d24b11b9a478c5bf4af581211846aef5db3 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 17 Apr 2020 04:54:15 -0600 Subject: [PATCH 0743/1778] Remove m2e directive stripping execution (#417) --- pom.xml | 36 +++--------------------------------- 1 file changed, 3 insertions(+), 33 deletions(-) diff --git a/pom.xml b/pom.xml index c9171c0a9..197c29cb9 100644 --- a/pom.xml +++ b/pom.xml @@ -228,6 +228,9 @@ + + + org.apache.maven.plugins maven-enforcer-plugin @@ -359,39 +362,6 @@ - - - - - - strip-m2e-directives - package - - run - - - - - - - - - - - - - - - - - - - - - - - - From b3d0cf40b7764d7ad6ee3a08629ac9c4d865d3e7 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 17 Apr 2020 04:56:49 -0600 Subject: [PATCH 0744/1778] Remove m2e directive --- pom.xml | 3 --- 1 file changed, 3 deletions(-) diff --git a/pom.xml b/pom.xml index 197c29cb9..fd0827ac4 100644 --- a/pom.xml +++ b/pom.xml @@ -228,9 +228,6 @@ - - - org.apache.maven.plugins maven-enforcer-plugin From 32c47ae99594ec94c5b2bcb3a2c96dab555a700d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 17 Apr 2020 04:57:43 -0600 Subject: [PATCH 0745/1778] [maven-release-plugin] prepare release classgraph-4.8.70 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index fd0827ac4..683057deb 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.70-SNAPSHOT + 4.8.70 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.70 From 1f2080dcc8daf29521039210f1a05426e9767af4 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 17 Apr 2020 04:57:51 -0600 Subject: [PATCH 0746/1778] [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 683057deb..c3390a311 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.70 + 4.8.71-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.70 + HEAD From bedec6e78a0bf73143728969bee32dc8a5c0a62b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 17 Apr 2020 05:42:36 -0600 Subject: [PATCH 0747/1778] Ignore classfiles in default package of modular jars (#417) --- .../classgraph/ClasspathElementDir.java | 13 ++++++++++- .../classgraph/ClasspathElementModule.java | 11 ++++++++++ .../classgraph/ClasspathElementZip.java | 22 +++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementDir.java b/src/main/java/io/github/classgraph/ClasspathElementDir.java index 8b71c5930..761e233c7 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementDir.java @@ -53,6 +53,7 @@ import nonapi.io.github.classgraph.scanspec.ScanSpec.ScanSpecPathMatch; 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. */ class ClasspathElementDir extends ClasspathElement { @@ -324,6 +325,7 @@ private void scanDirRecursively(final File dir, final LogNode log) { final String dirPath = dir.getPath(); 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) { @@ -376,15 +378,24 @@ private void scanDirRecursively(final File dir, final LogNode log) { : log.log("1:" + canonicalPath, "Scanning directory: " + dir + (dir.getPath().equals(canonicalPath) ? "" : " ; canonical path: " + canonicalPath)); + // 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 a whitelisted path if (parentMatchStatus != ScanSpecPathMatch.ANCESTOR_OF_WHITELISTED_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() || "/".equals(dirRelativePath) + 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; + } // Whitelist/blacklist classpath elements based on file resource paths checkResourcePathWhiteBlackList(fileInDirRelativePath, subLog); diff --git a/src/main/java/io/github/classgraph/ClasspathElementModule.java b/src/main/java/io/github/classgraph/ClasspathElementModule.java index 2e2520355..11b3c8e5d 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementModule.java +++ b/src/main/java/io/github/classgraph/ClasspathElementModule.java @@ -51,6 +51,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.VersionFinder; /** A module classpath element. */ class ClasspathElementModule extends ClasspathElement { @@ -269,6 +270,9 @@ void scanPaths(final LogNode log) { final LogNode subLog = log == null ? null : log(classpathElementIdx, "Scanning module " + moduleRef.getName(), log); + // Determine whether this is a modular jar running under JRE 9+ + final boolean isModularJar = VersionFinder.JAVA_MAJOR_VERSION >= 9 && getModuleName() != null; + try (RecycleOnClose moduleReaderProxyRecycleOnClose // = moduleReaderProxyRecycler.acquireRecycleOnClose()) { // Look for whitelisted files in the module. @@ -311,6 +315,13 @@ void scanPaths(final LogNode log) { continue; } + // If this is a modular jar, ignore all classfiles other than "module-info.class" in the + // default package, since these are disallowed. + if (isModularJar && relativePath.indexOf('/') < 0 && relativePath.endsWith(".class") + && !relativePath.equals("module-info.class")) { + continue; + } + // Whitelist/blacklist classpath elements based on file resource paths checkResourcePathWhiteBlackList(relativePath, log); if (skipClasspathElement) { diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index eb464e14f..8bc7ce70f 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -461,6 +461,21 @@ void scanPaths(final LogNode log) { final LogNode subLog = log == null ? null : log(classpathElementIdx, "Scanning jarfile classpath element " + getZipFilePath(), log); + boolean isModularJar = false; + if (VersionFinder.JAVA_MAJOR_VERSION >= 9) { + // Determine whether this is a modular jar running under JRE 9+ + String moduleName = moduleNameFromModuleDescriptor; + if (moduleName == null || moduleName.isEmpty()) { + moduleName = moduleNameFromManifestFile; + } + if (moduleName != null && moduleName.isEmpty()) { + moduleName = null; + } + if (moduleName != null) { + isModularJar = true; + } + } + Set loggedNestedClasspathRootPrefixes = null; String prevParentRelativePath = null; ScanSpecPathMatch prevParentMatchStatus = null; @@ -485,6 +500,13 @@ void scanPaths(final LogNode log) { continue; } + // If this is a modular jar, ignore all classfiles other than "module-info.class" in the + // default package, since these are disallowed. + if (isModularJar && relativePath.indexOf('/') < 0 && relativePath.endsWith(".class") + && !relativePath.equals("module-info.class")) { + continue; + } + // Check if the relative path is within a nested classpath root if (nestedClasspathRootPrefixes != null) { // This is O(mn), which is inefficient, but the number of nested classpath roots should be small From 1e63185187339a5969a592f536033a008c36c158 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 17 Apr 2020 15:02:08 -0600 Subject: [PATCH 0748/1778] Store the major and minor classfile format in ClassInfo (#418) --- .../java/io/github/classgraph/ClassInfo.java | 39 +++++++++++++++++++ .../java/io/github/classgraph/Classfile.java | 11 +++++- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index 266c75fa8..58a2b1eac 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -73,6 +73,12 @@ public class ClassInfo extends ScanResultObject implements Comparable */ boolean isInherited; + /** The minor version of the classfile format for this class' classfile. */ + private int classfileMinorVersion; + + /** The major version of the classfile format for this class' classfile. */ + private int classfileMajorVersion; + /** The class type signature string. */ protected String typeSignatureStr; @@ -348,6 +354,19 @@ static ClassInfo getOrCreateClassInfo(final String className, return classInfo; } + /** + * Set classfile version. + * + * @param minorVersion + * the minor version of the classfile format for this class' classfile. + * @param majorVersion + * the major version of the classfile format for this class' classfile. + */ + void setClassfileVersion(final int minorVersion, final int majorVersion) { + this.classfileMinorVersion = minorVersion; + this.classfileMajorVersion = majorVersion; + } + /** * Set class modifiers. * @@ -1070,6 +1089,26 @@ public boolean isExternalClass() { return isExternalClass; } + /** + * Get the minor version of the classfile format for this class' classfile. + * + * @return The minor version of the classfile format for this class' classfile, or 0 if this {@link ClassInfo} + * object is a placeholder for a referenced class that was not found or not whitelisted during the scan. + */ + public int getClassfileMinorVersion() { + return classfileMinorVersion; + } + + /** + * Get the major version of the classfile format for this class' classfile. + * + * @return The major version of the classfile format for this class' classfile, or 0 if this {@link ClassInfo} + * object is a placeholder for a referenced class that was not found or not whitelisted during the scan. + */ + public int getClassfileMajorVersion() { + return classfileMajorVersion; + } + /** * Get the class modifier bits. * diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index 87f72ad1c..fd6acd6eb 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -76,6 +76,12 @@ class Classfile { /** The name of the class. */ private String className; + /** The minor version of the classfile format. */ + private int minorVersion; + + /** The major version of the classfile format. */ + private int majorVersion; + /** Whether this is an external class. */ private final boolean isExternalClass; @@ -464,6 +470,7 @@ void link(final Map classNameToClassInfo, // Handle regular classfile classInfo = ClassInfo.addScannedClass(className, classModifiers, isExternalClass, classNameToClassInfo, classpathElement, classfileResource); + classInfo.setClassfileVersion(minorVersion, majorVersion); classInfo.setModifiers(classModifiers); classInfo.setIsInterface(isInterface); classInfo.setIsAnnotation(isAnnotation); @@ -1644,8 +1651,8 @@ private void readClassAttributes() throws IOException, ClassfileFormatException } // Read classfile minor and major version - reader.readUnsignedShort(); - reader.readUnsignedShort(); + minorVersion = reader.readUnsignedShort(); + majorVersion = reader.readUnsignedShort(); // Read the constant pool readConstantPoolEntries(); From 87095ab92180a19c8caf6c9a83c03aad7b08f5c9 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 17 Apr 2020 15:04:32 -0600 Subject: [PATCH 0749/1778] [maven-release-plugin] prepare release classgraph-4.8.71 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index c3390a311..89fb347e8 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.71-SNAPSHOT + 4.8.71 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.71 From 0670f2c8a9b50764ad06fb913578af2519d7c45f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 17 Apr 2020 15:04:40 -0600 Subject: [PATCH 0750/1778] [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 89fb347e8..dbb34a480 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.71 + 4.8.72-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.71 + HEAD From 79c60cb9d9c0586503edd94f6614b6b06b451320 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 21 Apr 2020 15:10:29 -0600 Subject: [PATCH 0751/1778] Don't set scanResult in BaseTypeSignature (#419) --- .../io/github/classgraph/BaseTypeSignature.java | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/BaseTypeSignature.java b/src/main/java/io/github/classgraph/BaseTypeSignature.java index 2dc219b16..f51206c79 100644 --- a/src/main/java/io/github/classgraph/BaseTypeSignature.java +++ b/src/main/java/io/github/classgraph/BaseTypeSignature.java @@ -82,7 +82,7 @@ private BaseTypeSignature(final String baseType, final char typeSignatureChar) { } // ------------------------------------------------------------------------------------------------------------- - + /** * Get the type as a string. * @@ -258,6 +258,20 @@ protected void findReferencedClassNames(final Set refdClassNames) { // Don't add byte.class, int.class, etc. } + /* (non-Javadoc) + * @see io.github.classgraph.ScanResultObject#setScanResult(ScanResult) + */ + @Override + void setScanResult(ScanResult scanResult) { + // Don't set ScanResult for BaseTypeSignature objects (#419). + // The ScanResult is not needed, since this class does not classload through the ScanResult. + // Also, specific instances of BaseTypeSignature for each primitive type are assigned to static fields + // in this class, which are shared across all usages of this class, so they should not contain any + // values that are specific to a given ScanResult. Setting the ScanResult from different scan processes + // would cause the scanResult field to only reflect the result of the most recent scan, and the reference + // to that scan would prevent garbage collection. + } + // ------------------------------------------------------------------------------------------------------------- /* (non-Javadoc) From e226cc1712b1a005fb4490bfeb93a7cec2c44f78 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 21 Apr 2020 15:13:56 -0600 Subject: [PATCH 0752/1778] [maven-release-plugin] prepare release classgraph-4.8.72 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index dbb34a480..642ced38e 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.72-SNAPSHOT + 4.8.72 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.72 From 2ae98cbaf79f56a917c3f4adb5ad14b31fc8a126 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 21 Apr 2020 15:14:04 -0600 Subject: [PATCH 0753/1778] [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 642ced38e..d9a2be0de 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.72 + 4.8.73-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.72 + HEAD From 579932a302e9264fbd018d09c90e81721d394c24 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 23 Apr 2020 05:38:23 -0600 Subject: [PATCH 0754/1778] Fix comment --- .../java/io/github/classgraph/issues/issue407/Issue407Test.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/io/github/classgraph/issues/issue407/Issue407Test.java b/src/test/java/io/github/classgraph/issues/issue407/Issue407Test.java index 179c3c8ff..190a9c28d 100644 --- a/src/test/java/io/github/classgraph/issues/issue407/Issue407Test.java +++ b/src/test/java/io/github/classgraph/issues/issue407/Issue407Test.java @@ -47,7 +47,7 @@ */ public class Issue407Test { /** - * Issue 193 test. + * Test. * * @throws IOException * Signals that an I/O exception has occurred. From 930066273e75d40b9b42dc4dc2de55a2a416e398 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 23 Apr 2020 06:11:33 -0600 Subject: [PATCH 0755/1778] Allow two slashes after custom URL schemes (#420) --- .../classgraph/utils/FastPathResolver.java | 60 ++++++++++---- .../issues/issue420/Issue420Test.java | 80 +++++++++++++++++++ 2 files changed, 123 insertions(+), 17 deletions(-) create mode 100644 src/test/java/io/github/classgraph/issues/issue420/Issue420Test.java 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 8df69964c..c10e86358 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FastPathResolver.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FastPathResolver.java @@ -42,6 +42,12 @@ 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+\\-.]+:/"); + /** True if we're running on Windows. */ private static final boolean WINDOWS = File.separatorChar == '\\'; @@ -194,33 +200,33 @@ public static String resolve(final String resolveBasePath, final String relative boolean isAbsolutePath = false; boolean isFileOrJarURL = false; int startIdx = 0; - if (relativePath.regionMatches(true, startIdx, "jar:", 0, 4)) { + if (relativePath.regionMatches(true, 0, "jar:", 0, 4)) { // "jar:" prefix can be stripped - startIdx += 4; + startIdx = 4; isFileOrJarURL = true; } - if (relativePath.regionMatches(true, startIdx, "http://", 0, 7)) { + if (relativePath.regionMatches(true, 0, "http://", 0, 7)) { // Detect http:// - startIdx += 7; + 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)) { + } else if (relativePath.regionMatches(true, 0, "https://", 0, 8)) { // Detect https:// - startIdx += 8; + startIdx = 8; prefix = "https://"; isAbsolutePath = true; - } else if (relativePath.regionMatches(true, startIdx, "jrt:", 0, 5)) { + } else if (relativePath.regionMatches(true, 0, "jrt:", 0, 5)) { // Detect jrt: - startIdx += 4; + startIdx = 4; prefix = "jrt:"; isAbsolutePath = true; - } else if (relativePath.regionMatches(true, startIdx, "file:", 0, 5)) { + } else if (relativePath.regionMatches(true, 0, "file:", 0, 5)) { // Strip off any "file:" prefix from relative path - startIdx += 5; + startIdx = 5; if (WINDOWS) { if (relativePath.startsWith("\\\\\\\\", startIdx) || relativePath.startsWith("////", startIdx)) { // Windows UNC URL @@ -233,19 +239,39 @@ public static String resolve(final String resolveBasePath, final String relative } } } - if (relativePath.startsWith("//", startIdx)) { + if (relativePath.startsWith("///", startIdx)) { startIdx += 2; } isFileOrJarURL = true; - } else if (WINDOWS && (relativePath.startsWith("//") || relativePath.startsWith("\\\\"))) { - // Windows UNC path - startIdx += 2; - prefix = "//"; - isAbsolutePath = true; + } else { + // Preserve the number of slashes on custom URL schemes (#420) + final Matcher m2 = schemeTwoSlashMatcher.matcher(relativePath); + 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. + isAbsolutePath = true; + } else { + final Matcher m1 = schemeOneSlashMatcher.matcher(relativePath); + if (m1.find()) { + final String m1Match = m1.group(); + startIdx += m1Match.length(); + prefix = m1Match; + isAbsolutePath = true; + } + } } + // Handle Windows paths starting with a drive designation as an absolute path if (WINDOWS) { - if (relativePath.length() - startIdx > 2 && Character.isLetter(relativePath.charAt(startIdx)) + if ((relativePath.startsWith("//", startIdx) || relativePath.startsWith("\\\\", startIdx))) { + // Windows UNC path + startIdx += 2; + prefix = "//"; + isAbsolutePath = true; + } else if (relativePath.length() - startIdx > 2 && Character.isLetter(relativePath.charAt(startIdx)) && relativePath.charAt(startIdx + 1) == ':') { // Path like "C:/xyz" isAbsolutePath = true; diff --git a/src/test/java/io/github/classgraph/issues/issue420/Issue420Test.java b/src/test/java/io/github/classgraph/issues/issue420/Issue420Test.java new file mode 100644 index 000000000..b0976c0c3 --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue420/Issue420Test.java @@ -0,0 +1,80 @@ +/* + * 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.issues.issue420; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.junit.jupiter.api.Test; + +import com.google.common.jimfs.Jimfs; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ScanResult; + +/** + * Issue193Test. + */ +public class Issue420Test { + /** + * Test. + * + * @throws IOException + * If an I/O exception occurred. + * @throws URISyntaxException + * If a URI is bad. + */ + @Test + public void issue420Test() throws IOException, URISyntaxException { + try (FileSystem memFs = Jimfs.newFileSystem()) { + final Path jarPath = Paths + .get(getClass().getClassLoader().getResource("multi-release-jar.jar").toURI()); + final Path memFsRoot = memFs.getPath("/multi-release-jar.jar"); + final Path inMemCopyOfJar = Files.copy(jarPath, memFsRoot); + final URL url = inMemCopyOfJar.toUri().toURL(); + try (URLClassLoader childClassLoader = new URLClassLoader(new URL[] { url }, + getClass().getClassLoader())) { + final ClassGraph classGraph = new ClassGraph().enableURLScheme(url.getProtocol()) + .overrideClassLoaders(childClassLoader).ignoreParentClassLoaders().whitelistPackages("mrj") + .enableAllInfo(); + try (ScanResult scanResult = classGraph.scan()) { + assertThat(scanResult.getClassInfo("mrj.Cls")).isNotNull(); + } + } + } + } +} From 59cd264f36a09f7baa4405f0f8fb01b638ed2373 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 23 Apr 2020 06:11:41 -0600 Subject: [PATCH 0756/1778] Source > Cleanup --- src/main/java/io/github/classgraph/BaseTypeSignature.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/classgraph/BaseTypeSignature.java b/src/main/java/io/github/classgraph/BaseTypeSignature.java index f51206c79..ffe11d1d5 100644 --- a/src/main/java/io/github/classgraph/BaseTypeSignature.java +++ b/src/main/java/io/github/classgraph/BaseTypeSignature.java @@ -82,7 +82,7 @@ private BaseTypeSignature(final String baseType, final char typeSignatureChar) { } // ------------------------------------------------------------------------------------------------------------- - + /** * Get the type as a string. * @@ -262,7 +262,7 @@ protected void findReferencedClassNames(final Set refdClassNames) { * @see io.github.classgraph.ScanResultObject#setScanResult(ScanResult) */ @Override - void setScanResult(ScanResult scanResult) { + void setScanResult(final ScanResult scanResult) { // Don't set ScanResult for BaseTypeSignature objects (#419). // The ScanResult is not needed, since this class does not classload through the ScanResult. // Also, specific instances of BaseTypeSignature for each primitive type are assigned to static fields From 26186f148c90ea0d759914220eabb66f8cef8587 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 23 Apr 2020 06:13:00 -0600 Subject: [PATCH 0757/1778] Update dep for previous commit --- pom.xml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d9a2be0de..6d1a86300 100644 --- a/pom.xml +++ b/pom.xml @@ -120,6 +120,12 @@ 1.0.2.Final test + + com.google.jimfs + jimfs + 1.1 + test + @@ -228,7 +234,6 @@ - org.apache.maven.plugins maven-enforcer-plugin From 2b82ba3257be4c22fb746f8c547cf1ab09c7935e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 23 Apr 2020 06:14:28 -0600 Subject: [PATCH 0758/1778] [maven-release-plugin] prepare release classgraph-4.8.73 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 6d1a86300..d0bf4aeb4 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.73-SNAPSHOT + 4.8.73 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.73 From cc0d374c3209e93568d32ca9c5d0e7d225128b2e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 23 Apr 2020 06:14:36 -0600 Subject: [PATCH 0759/1778] [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 d0bf4aeb4..ad45c0900 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.73 + 4.8.74-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.73 + HEAD From 40025da269dcc492a53e99d6ec9959166da84125 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 23 Apr 2020 06:40:31 -0600 Subject: [PATCH 0760/1778] Don't determine system jars if system jar scanning is disabled --- .../classgraph/classpath/ClasspathFinder.java | 35 ++++++++++--------- .../classgraph/classpath/ClasspathOrder.java | 1 + .../classgraph/classpath/SystemJarFinder.java | 20 ++++++++--- .../io/github/classgraph/utils/LogNode.java | 5 --- 4 files changed, 35 insertions(+), 26 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 bc24ff718..1de94c8d4 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java @@ -170,34 +170,35 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { classLoaderOrderRespectingParentDelegation = contextClassLoaders; } else if (scanSpec.overrideClassLoaders == null) { - // If system jars are not blacklisted, add JRE rt.jar to the beginning of the classpath - final String jreRtJar = SystemJarFinder.getJreRtJarPath(); - - // Add rt.jar and/or lib/ext jars to beginning of classpath, if enabled final LogNode systemJarsLog = classpathFinderLog == null ? null : classpathFinderLog.log("System jars:"); - if (jreRtJar != null) { - if (scanSpec.enableSystemJarsAndModules) { + if (scanSpec.enableSystemJarsAndModules) { + // If system jars are not blacklisted, add JRE rt.jar to the beginning of the classpath + final String jreRtJar = SystemJarFinder.getJreRtJarPath(); + + // Add rt.jar and/or lib/ext jars to beginning of classpath, if enabled + if (jreRtJar != null) { classpathOrder.addSystemClasspathEntry(jreRtJar, defaultClassLoader); if (systemJarsLog != null) { systemJarsLog.log("Found rt.jar: " + jreRtJar); } - } else if (systemJarsLog != null) { - systemJarsLog.log((scanSpec.enableSystemJarsAndModules ? "" : "Scanning disabled for rt.jar: ") - + jreRtJar); } + } else if (systemJarsLog != null) { + systemJarsLog.log("Scanning disabled for system jars and modules"); } final boolean scanAllLibOrExtJars = !scanSpec.libOrExtJarWhiteBlackList.whitelistAndBlacklistAreEmpty(); - for (final String libOrExtJarPath : SystemJarFinder.getJreLibOrExtJars()) { - if (scanAllLibOrExtJars || scanSpec.libOrExtJarWhiteBlackList - .isSpecificallyWhitelistedAndNotBlacklisted(libOrExtJarPath)) { - classpathOrder.addSystemClasspathEntry(libOrExtJarPath, defaultClassLoader); - if (systemJarsLog != null) { - systemJarsLog.log("Found lib or ext jar: " + libOrExtJarPath); + if (scanAllLibOrExtJars) { + for (final String libOrExtJarPath : SystemJarFinder.getJreLibOrExtJars()) { + if (scanAllLibOrExtJars || scanSpec.libOrExtJarWhiteBlackList + .isSpecificallyWhitelistedAndNotBlacklisted(libOrExtJarPath)) { + classpathOrder.addSystemClasspathEntry(libOrExtJarPath, defaultClassLoader); + if (systemJarsLog != null) { + systemJarsLog.log("Found lib or ext jar: " + libOrExtJarPath); + } } - } else if (systemJarsLog != null) { - systemJarsLog.log("Scanning disabled for lib or ext jar: " + libOrExtJarPath); } + } else if (systemJarsLog != null) { + systemJarsLog.log("Scanning disabled for lib or ext jars"); } } 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 f9e88278b..31c0c0ce1 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java @@ -187,6 +187,7 @@ private boolean addClasspathEntry(final Object pathElement, final String pathEle } } else { if (scanSpec.overrideClasspath == null // + && scanSpec.enableSystemJarsAndModules // && (SystemJarFinder.getJreLibOrExtJars().contains(pathElementStr) || pathElementStr.equals(SystemJarFinder.getJreRtJarPath()))) { // JRE lib and ext jars are handled separately, so reject them as duplicates if they are 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..0dfcb6efb 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/SystemJarFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/SystemJarFinder.java @@ -32,6 +32,7 @@ import java.io.IOException; import java.util.LinkedHashSet; import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; import nonapi.io.github.classgraph.utils.FastPathResolver; import nonapi.io.github.classgraph.utils.FileUtils; @@ -41,13 +42,16 @@ /** A class to find rt.jar and any JRE "lib/" or "ext/" jars. */ public final class SystemJarFinder { /** The paths of any "rt.jar" files found in the JRE. */ - private static final Set RT_JARS = new LinkedHashSet<>(); + private static Set RT_JARS; /** The path of the first "rt.jar" found. */ - private static final String RT_JAR; + private static String RT_JAR; /** The paths of any "lib/" or "ext/" jars found in the JRE. */ - private static final Set JRE_LIB_OR_EXT_JARS = new LinkedHashSet<>(); + private static Set JRE_LIB_OR_EXT_JARS; + + /** True if the system jars have already been found. */ + private static final AtomicBoolean initialized = new AtomicBoolean(); /** * Constructor. @@ -96,7 +100,9 @@ private static boolean addJREPath(final File dir) { } // Find jars in JRE dirs ({java.home}, {java.home}/lib, {java.home}/lib/ext, etc.) - static { + private static void initialize() { + RT_JARS = new LinkedHashSet<>(); + JRE_LIB_OR_EXT_JARS = new LinkedHashSet<>(); String javaHome = VersionFinder.getProperty("java.home"); if (javaHome == null || javaHome.isEmpty()) { javaHome = System.getenv("JAVA_HOME"); @@ -176,6 +182,9 @@ private static boolean addJREPath(final File dir) { * @return The path of rt.jar (in JDK 7 or 8), or null if it wasn't found (e.g. in JDK 9+). */ public static String getJreRtJarPath() { + if (!initialized.getAndSet(true)) { + initialize(); + } // Only include the first rt.jar -- if there is a copy in both the JDK and JRE, no need to scan both return RT_JAR; } @@ -186,6 +195,9 @@ public static String getJreRtJarPath() { * @return The paths for any jarfiles found in JRE/JDK "lib/" or "ext/" directories. */ public static Set getJreLibOrExtJars() { + if (!initialized.getAndSet(true)) { + initialize(); + } return JRE_LIB_OR_EXT_JARS; } } 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..d0028ad4d 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/LogNode.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/LogNode.java @@ -42,7 +42,6 @@ import java.util.logging.Logger; import io.github.classgraph.ClassGraph; -import nonapi.io.github.classgraph.classpath.SystemJarFinder; /** * A tree-structured threadsafe log that allows you to add log entries in arbitrary order, and have the output @@ -150,10 +149,6 @@ private void logJavaInfo() { + VersionFinder.getProperty("java.runtime.version") + " (" + VersionFinder.getProperty("java.vendor") + ")"); log("Java home: " + VersionFinder.getProperty("java.home")); - final String jreRtJarPath = SystemJarFinder.getJreRtJarPath(); - if (jreRtJarPath != null) { - log("JRE rt.jar:").log(jreRtJarPath); - } } /** From f53fa75c238e000cb0001693751060b0da59c6c2 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 23 Apr 2020 07:05:32 -0600 Subject: [PATCH 0761/1778] Update test to work with Windows --- .../classgraph/issues/issue420/Issue420Test.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 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 b0976c0c3..52ebace15 100644 --- a/src/test/java/io/github/classgraph/issues/issue420/Issue420Test.java +++ b/src/test/java/io/github/classgraph/issues/issue420/Issue420Test.java @@ -63,12 +63,13 @@ public void issue420Test() throws IOException, URISyntaxException { try (FileSystem memFs = Jimfs.newFileSystem()) { final Path jarPath = Paths .get(getClass().getClassLoader().getResource("multi-release-jar.jar").toURI()); - final Path memFsRoot = memFs.getPath("/multi-release-jar.jar"); - final Path inMemCopyOfJar = Files.copy(jarPath, memFsRoot); - final URL url = inMemCopyOfJar.toUri().toURL(); - try (URLClassLoader childClassLoader = new URLClassLoader(new URL[] { url }, + final Path memFsPath = memFs.getPath("multi-release-jar.jar"); + final Path memFsCopyOfJar = Files.copy(jarPath, memFsPath); + final URL memFsCopyOfJarURL = memFsCopyOfJar.toUri().toURL(); + + try (URLClassLoader childClassLoader = new URLClassLoader(new URL[] { memFsCopyOfJarURL }, getClass().getClassLoader())) { - final ClassGraph classGraph = new ClassGraph().enableURLScheme(url.getProtocol()) + final ClassGraph classGraph = new ClassGraph().enableURLScheme(memFsCopyOfJarURL.getProtocol()) .overrideClassLoaders(childClassLoader).ignoreParentClassLoaders().whitelistPackages("mrj") .enableAllInfo(); try (ScanResult scanResult = classGraph.scan()) { From 285d6c168c2146a18f5b4631c58da029da9c018c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 23 Apr 2020 07:44:50 -0600 Subject: [PATCH 0762/1778] Optimize access to custom filesystems by using FileChannel API (#430) --- .../classgraph/ClasspathElementDir.java | 2 +- .../classgraph/ClasspathElementZip.java | 3 +- .../fastzipfilereader/NestedJarHandler.java | 77 +++-- .../fastzipfilereader/PhysicalZipFile.java | 72 +++-- .../fastzipfilereader/ZipFileSlice.java | 20 +- .../classgraph/fileslice/FileSlice.java | 11 +- .../classgraph/fileslice/PathSlice.java | 270 ++++++++++++++++++ .../io/github/classgraph/fileslice/Slice.java | 9 +- ...ava => RandomAccessFileChannelReader.java} | 5 +- .../io/github/classgraph/utils/FileUtils.java | 22 ++ 10 files changed, 435 insertions(+), 56 deletions(-) create mode 100644 src/main/java/nonapi/io/github/classgraph/fileslice/PathSlice.java rename src/main/java/nonapi/io/github/classgraph/fileslice/reader/{RandomAccessFileReader.java => RandomAccessFileChannelReader.java} (97%) diff --git a/src/main/java/io/github/classgraph/ClasspathElementDir.java b/src/main/java/io/github/classgraph/ClasspathElementDir.java index 761e233c7..fab8ef5dd 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementDir.java @@ -269,7 +269,7 @@ public void close() { } if (fileSlice != null) { fileSlice.close(); - nestedJarHandler.markFileSliceAsClosed(fileSlice); + nestedJarHandler.markSliceAsClosed(fileSlice); fileSlice = null; } } diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index 8bc7ce70f..11b11e675 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -651,7 +651,8 @@ URI getURI() { * Get the {@link File} for the outermost zipfile of this classpath element. * * @return The {@link File} for the outermost zipfile of this classpath element, or null if this file was - * downloaded from a URL directly to RAM. + * downloaded from a URL directly to RAM, or if the classpath element was backed by a custom filesystem + * that supports the {@link Path} API put not the {@link File} API. */ @Override File getFile() { 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 1a1713fee..cf98273a0 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java @@ -43,7 +43,11 @@ import java.net.URLConnection; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; +import java.nio.file.FileSystem; +import java.nio.file.FileSystemNotFoundException; import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Arrays; @@ -174,9 +178,7 @@ public Entry newInstance(final String nestedJarPathRaw, } // Download jar from URL to a ByteBuffer in RAM, or to a temp file on disk - final LogNode subLog = log == null ? null - : log.log("Downloading jar from URL " + nestedJarPath); - physicalZipFile = downloadJarFromURL(nestedJarPath, subLog); + physicalZipFile = downloadJarFromURL(nestedJarPath, log); } else { // Jarfile should be a local file -- wrap in a PhysicalZipFile instance @@ -357,7 +359,7 @@ public RecyclableInflater newInstance() throws RuntimeException { }; /** {@link FileSlice} instances that are currently open. */ - private Set openFileSlices = Collections.newSetFromMap(new ConcurrentHashMap()); + private Set openSlices = Collections.newSetFromMap(new ConcurrentHashMap()); /** Any temporary files created while scanning. */ private Set tempFiles = Collections.newSetFromMap(new ConcurrentHashMap()); @@ -459,25 +461,25 @@ void removeTempFile(final File tempFile) throws IOException, SecurityException { } /** - * Mark a {@link FileSlice} as open. + * Mark a {@link Slice} as open, so it can be closed when the {@link ScanResult} is closed. * - * @param fileSlice - * the {@link FileSlice} that was just opened. + * @param slice + * the {@link Slice} that was just opened. * @throws IOException * Signals that an I/O exception has occurred. */ - public void markFileSliceAsOpen(final FileSlice fileSlice) throws IOException { - openFileSlices.add(fileSlice); + public void markSliceAsOpen(final Slice slice) throws IOException { + openSlices.add(slice); } /** - * Mark a {@link FileSlice} as closed. + * Mark a {@link Slice} as closed. * - * @param fileSlice - * the {@link FileSlice} to close. + * @param slice + * the {@link Slice} to close. */ - public void markFileSliceAsClosed(final FileSlice fileSlice) { - openFileSlices.remove(fileSlice); + public void markSliceAsClosed(final Slice slice) { + openSlices.remove(slice); } /** @@ -512,6 +514,26 @@ 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 + try { + final Path path = Paths.get(url.toURI()); + // Fails with FileSystemNotFoundException if filesystem not registered for URL + final FileSystem fs = path.getFileSystem(); + if (log != null) { + log.log("URL " + jarURL + " is backed by filesystem " + fs.getClass().getName()); + } + // 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 FileSystemNotFoundException e) { + // Not a custom filesystem + } + } + final URLConnection conn = url.openConnection(); HttpURLConnection httpConn = null; try { @@ -538,13 +560,14 @@ private PhysicalZipFile downloadJarFromURL(final String jarURL, final LogNode lo } // 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, log); - if (log != null) { - log.addElapsedTime(); - log.log("***** Note that it is time-consuming to scan jars at non-\"file:\" URLs, " + 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 *****"); } @@ -967,15 +990,19 @@ public void close(final LogNode log) { fastZipEntryToZipFileSliceMap.clear(); fastZipEntryToZipFileSliceMap = null; } - if (openFileSlices != null) { - while (!openFileSlices.isEmpty()) { - for (final FileSlice fileSlice : new ArrayList<>(openFileSlices)) { - fileSlice.close(); - markFileSliceAsClosed(fileSlice); + if (openSlices != null) { + while (!openSlices.isEmpty()) { + for (final Slice slice : new ArrayList<>(openSlices)) { + try { + slice.close(); + } catch (final IOException e) { + // Ignore + } + markSliceAsClosed(slice); } } - openFileSlices.clear(); - openFileSlices = null; + openSlices.clear(); + openSlices = null; } if (inflaterRecycler != null) { inflaterRecycler.forceClose(); 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 aa76fc307..05afe1ca5 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/PhysicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/PhysicalZipFile.java @@ -32,10 +32,12 @@ import java.io.IOException; import java.io.InputStream; import java.nio.channels.FileChannel; +import java.nio.file.Path; import java.util.Objects; import nonapi.io.github.classgraph.fileslice.ArraySlice; import nonapi.io.github.classgraph.fileslice.FileSlice; +import nonapi.io.github.classgraph.fileslice.PathSlice; import nonapi.io.github.classgraph.fileslice.Slice; import nonapi.io.github.classgraph.utils.FastPathResolver; import nonapi.io.github.classgraph.utils.FileUtils; @@ -43,11 +45,14 @@ /** A physical zipfile, which is mmap'd using a {@link FileChannel}. */ class PhysicalZipFile { + /** The {@link Path} backing this {@link PhysicalZipFile}, if any. */ + private Path path; + /** The {@link File} backing this {@link PhysicalZipFile}, if any. */ - private final File file; + private File file; /** The path to the zipfile. */ - private final String path; + private final String pathStr; /** The {@link Slice} for the zipfile. */ Slice slice; @@ -72,15 +77,40 @@ 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.nestedJarHandler = nestedJarHandler; - this.path = FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, file.getPath()); + this.pathStr = FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, file.getPath()); this.slice = new FileSlice(file, nestedJarHandler, log); } + /** + * Construct a {@link PhysicalZipFile} from a {@link Path}. + * + * @param path + * the path + * @param nestedJarHandler + * the nested jar handler + * @param log + * the log + * @throws IOException + * if an I/O exception occurs. + */ + 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.CURR_DIR_PATH, path.toString()); + this.slice = new PathSlice(path, nestedJarHandler, log); + } + /** * Construct a {@link PhysicalZipFile} from a byte array. * @@ -88,18 +118,18 @@ class PhysicalZipFile { * the array containing the zipfile. * @param outermostFile * the outermost file - * @param path + * @param pathStr * the path * @param nestedJarHandler * the nested jar handler * @throws IOException * if an I/O exception occurs. */ - PhysicalZipFile(final byte[] arr, final File outermostFile, final String path, + PhysicalZipFile(final byte[] arr, final File outermostFile, final String pathStr, final NestedJarHandler nestedJarHandler) throws IOException { - this.file = outermostFile; - this.path = path; this.nestedJarHandler = nestedJarHandler; + this.file = outermostFile; + this.pathStr = pathStr; this.slice = new ArraySlice(arr, /* isDeflatedZipEntry = */ false, /* inflatedSizeHint = */ 0L, nestedJarHandler); } @@ -112,7 +142,7 @@ class PhysicalZipFile { * the input stream * @param inputStreamLengthHint * The number of bytes to read in inputStream, or -1 if unknown. - * @param path + * @param pathStr * the source URL the InputStream was opened from, or the zip entry path of this entry in the parent * zipfile * @param nestedJarHandler @@ -122,22 +152,32 @@ class PhysicalZipFile { * @throws IOException * if an I/O exception occurs. */ - PhysicalZipFile(final InputStream inputStream, final long inputStreamLengthHint, final String path, + PhysicalZipFile(final InputStream inputStream, final long inputStreamLengthHint, final String pathStr, final NestedJarHandler nestedJarHandler, final LogNode log) throws IOException { this.nestedJarHandler = nestedJarHandler; - this.path = path; + this.pathStr = pathStr; // Try downloading the InputStream to a byte array. If this succeeds, this will result in an ArraySlice. // If it fails, the InputStream will be spilled to disk, resulting in a FileSlice. - this.slice = nestedJarHandler.readAllBytesWithSpilloverToDisk(inputStream, /* tempFileBaseName = */ path, + this.slice = nestedJarHandler.readAllBytesWithSpilloverToDisk(inputStream, /* tempFileBaseName = */ pathStr, inputStreamLengthHint, log); this.file = this.slice instanceof FileSlice ? ((FileSlice) this.slice).file : null; } + /** + * Get the {@link Path} for the outermost jar file of this PhysicalZipFile. + * + * @return the {@link Path} for the outermost jar file of this PhysicalZipFile, or null if this file was + * downloaded from a URL directly to RAM, or is backed by a {@link File}. + */ + public Path getPath() { + return path; + } + /** * Get the {@link File} for the outermost jar file of this PhysicalZipFile. * * @return the {@link File} for the outermost jar file of this PhysicalZipFile, or null if this file was - * downloaded from a URL directly to RAM. + * downloaded from a URL directly to RAM, or is backed by a {@link Path}. */ public File getFile() { return file; @@ -150,8 +190,8 @@ public File getFile() { * @return the path for this PhysicalZipFile, which is the file path, if it is file-backed, or a compound nested * jar path, if it is memory-backed. */ - public String getPath() { - return path; + public String getPathStr() { + return pathStr; } /** @@ -197,6 +237,6 @@ public boolean equals(final Object o) { */ @Override public String toString() { - return path; + return pathStr; } } \ No newline at end of file diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSlice.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSlice.java index 09c49548b..aa6c65bac 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSlice.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSlice.java @@ -30,6 +30,7 @@ import java.io.File; import java.io.IOException; +import java.nio.file.Path; import java.util.Objects; import nonapi.io.github.classgraph.fileslice.Slice; @@ -56,7 +57,7 @@ public class ZipFileSlice { this.parentZipFileSlice = null; this.physicalZipFile = physicalZipFile; this.slice = physicalZipFile.slice; - this.pathWithinParentZipFileSlice = physicalZipFile.getPath(); + this.pathWithinParentZipFileSlice = physicalZipFile.getPathStr(); } /** @@ -174,7 +175,17 @@ public String getPath() { * from a URL directly to RAM. */ public File getPhysicalFile() { - return physicalZipFile.getFile(); + final Path path = physicalZipFile.getPath(); + if (path != null) { + try { + return path.toFile(); + } catch (final UnsupportedOperationException e) { + // Filesystem supports the Path API but not the File API + return null; + } + } else { + return physicalZipFile.getFile(); + } } /* (non-Javadoc) @@ -207,7 +218,10 @@ public int hashCode() { @Override public String toString() { final String path = getPath(); - final String fileStr = physicalZipFile.getFile() == null ? null : physicalZipFile.getFile().toString(); + String fileStr = physicalZipFile.getPath() == null ? null : physicalZipFile.getPath().toString(); + if (fileStr == null) { + fileStr = physicalZipFile.getFile() == null ? null : physicalZipFile.getFile().toString(); + } return "[" + (fileStr == null || !fileStr.equals(path) ? path + " -> " + fileStr : path) + " ; byte range: " + slice.sliceStartPos + ".." + (slice.sliceStartPos + slice.sliceLength) + " / " + physicalZipFile.length() + "]"; 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 08ad940ba..2635a8d2c 100644 --- a/src/main/java/nonapi/io/github/classgraph/fileslice/FileSlice.java +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/FileSlice.java @@ -28,7 +28,6 @@ */ package nonapi.io.github.classgraph.fileslice; -import java.io.Closeable; import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -43,13 +42,13 @@ import io.github.classgraph.ClassGraph; import nonapi.io.github.classgraph.fastzipfilereader.NestedJarHandler; import nonapi.io.github.classgraph.fileslice.reader.RandomAccessByteBufferReader; -import nonapi.io.github.classgraph.fileslice.reader.RandomAccessFileReader; +import nonapi.io.github.classgraph.fileslice.reader.RandomAccessFileChannelReader; import nonapi.io.github.classgraph.fileslice.reader.RandomAccessReader; import nonapi.io.github.classgraph.utils.FileUtils; import nonapi.io.github.classgraph.utils.LogNode; /** A {@link File} slice. */ -public class FileSlice extends Slice implements Closeable { +public class FileSlice extends Slice { /** The {@link File}. */ public final File file; @@ -159,7 +158,7 @@ public FileSlice(final File file, final boolean isDeflatedZipEntry, final long i } // Mark toplevel slice as open - nestedJarHandler.markFileSliceAsOpen(this); + nestedJarHandler.markSliceAsOpen(this); } /** @@ -211,7 +210,7 @@ public Slice slice(final long offset, final long length, final boolean isDeflate public RandomAccessReader randomAccessReader() { if (backingByteBuffer == null) { // If file was not mmap'd, return a RandomAccessReader that uses the FileChannel - return new RandomAccessFileReader(fileChannel, sliceStartPos, sliceLength); + return new RandomAccessFileChannelReader(fileChannel, sliceStartPos, sliceLength); } else { // If file was mmap'd, return a RandomAccessReader that uses the ByteBuffer return new RandomAccessByteBufferReader(backingByteBuffer, sliceStartPos, sliceLength); @@ -307,7 +306,7 @@ public void close() { // Ignore } raf = null; - nestedJarHandler.markFileSliceAsClosed(this); + nestedJarHandler.markSliceAsClosed(this); } } } diff --git a/src/main/java/nonapi/io/github/classgraph/fileslice/PathSlice.java b/src/main/java/nonapi/io/github/classgraph/fileslice/PathSlice.java new file mode 100644 index 000000000..5936cc8bb --- /dev/null +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/PathSlice.java @@ -0,0 +1,270 @@ +/* + * This file is part of ClassGraph. + * + * Author: Luke Hutchison + * + * Hosted at: https://github.com/classgraph/classgraph + * + * -- + * + * The MIT License (MIT) + * + * Copyright (c) 2020 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.fileslice; + +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.concurrent.atomic.AtomicBoolean; + +import io.github.classgraph.ClassGraph; +import nonapi.io.github.classgraph.fastzipfilereader.NestedJarHandler; +import nonapi.io.github.classgraph.fileslice.reader.RandomAccessFileChannelReader; +import nonapi.io.github.classgraph.fileslice.reader.RandomAccessReader; +import nonapi.io.github.classgraph.utils.FileUtils; +import nonapi.io.github.classgraph.utils.LogNode; + +/** A {@link Path} slice. */ +public class PathSlice extends Slice implements Closeable { + /** The {@link Path}. */ + public final Path path; + + /** The file length. */ + private final long fileLength; + + /** The {@link FileChannel} opened on the {@link Path}. */ + private FileChannel fileChannel; + + /** True if this is a top level file slice. */ + private final boolean isTopLevelFileSlice; + + /** True if {@link #close} has been called. */ + private final AtomicBoolean isClosed = new AtomicBoolean(); + + /** + * Constructor for treating a range of a file as a slice. + * + * @param parentSlice + * the parent slice + * @param offset + * the offset of the sub-slice within the parent slice + * @param length + * the length of the sub-slice + * @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 + */ + private PathSlice(final PathSlice parentSlice, final long offset, final long length, + final boolean isDeflatedZipEntry, final long inflatedLengthHint, + final NestedJarHandler nestedJarHandler) { + super(parentSlice, offset, length, isDeflatedZipEntry, inflatedLengthHint, nestedJarHandler); + + this.path = parentSlice.path; + this.fileChannel = parentSlice.fileChannel; + 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) + } + + /** + * 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 log + * the log + * @throws IOException + * if the file cannot be opened. + */ + public PathSlice(final Path path, final boolean isDeflatedZipEntry, final long inflatedLengthHint, + final NestedJarHandler nestedJarHandler, final LogNode log) throws IOException { + super(0L, isDeflatedZipEntry, inflatedLengthHint, nestedJarHandler); + + // Make sure the File is readable and is a regular file + FileUtils.checkCanReadAndIsFile(path); + + this.path = path; + this.fileChannel = FileChannel.open(path, StandardOpenOption.READ); + 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 + this.sliceLength = fileLength; + + // Mark toplevel slice as open + nestedJarHandler.markSliceAsOpen(this); + } + + /** + * Constructor for toplevel file slice. + * + * @param path + * the path + * @param nestedJarHandler + * the nested jar handler + * @param log + * the log + * @throws IOException + * if the file cannot be opened. + */ + public PathSlice(final Path path, final NestedJarHandler nestedJarHandler, final LogNode log) + throws IOException { + this(path, /* isDeflatedZipEntry = */ false, /* inflatedSizeHint = */ 0L, nestedJarHandler, log); + } + + /** + * Slice the file. + * + * @param offset + * the offset of the sub-slice within the parent slice + * @param length + * the length of the sub-slice + * @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. + * @return the slice + */ + @Override + public Slice slice(final long offset, final long length, final boolean isDeflatedZipEntry, + final long inflatedLengthHint) { + if (this.isDeflatedZipEntry) { + throw new IllegalArgumentException("Cannot slice a deflated zip entry"); + } + return new PathSlice(this, offset, length, isDeflatedZipEntry, inflatedLengthHint, nestedJarHandler); + } + + /** + * Read directly from FileChannel (slow path, but handles >2GB). + * + * @return the random access reader + */ + @Override + public RandomAccessReader randomAccessReader() { + // Return a RandomAccessReader that uses the FileChannel + return new RandomAccessFileChannelReader(fileChannel, sliceStartPos, sliceLength); + } + + /** + * Load the slice as a byte array. + * + * @return the byte[] + * @throws IOException + * Signals that an I/O exception has occurred. + */ + @Override + public byte[] load() throws IOException { + if (isDeflatedZipEntry) { + // Inflate into RAM if deflated + if (inflatedLengthHint > FileUtils.MAX_BUFFER_SIZE) { + throw new IOException("Uncompressed size is larger than 2GB"); + } + try (InputStream inputStream = open()) { + return NestedJarHandler.readAllBytesAsArray(inputStream, inflatedLengthHint); + } + } else { + // Copy from FileChannel to byte array + if (sliceLength > FileUtils.MAX_BUFFER_SIZE) { + throw new IOException("File is larger than 2GB"); + } + final RandomAccessReader reader = randomAccessReader(); + final byte[] content = new byte[(int) sliceLength]; + if (reader.read(0, content, 0, content.length) < content.length) { + // Should not happen + throw new IOException("File is truncated"); + } + return content; + } + } + + /** + * 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. + */ + @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) + if (inflatedLengthHint > FileUtils.MAX_BUFFER_SIZE) { + throw new IOException("Uncompressed size is larger than 2GB"); + } + return ByteBuffer.wrap(load()); + } + // Copy from FileChannel to byte array, then wrap in a ByteBuffer + if (sliceLength > FileUtils.MAX_BUFFER_SIZE) { + throw new IOException("File is larger than 2GB"); + } + return ByteBuffer.wrap(load()); + } + + @Override + public boolean equals(final Object o) { + return super.equals(o); + } + + @Override + public int hashCode() { + return super.hashCode(); + } + + /** Close the slice. Unmaps any backing {@link MappedByteBuffer}. */ + @Override + public void close() { + if (!isClosed.getAndSet(true)) { + if (isTopLevelFileSlice) { + // 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; + } + } + fileChannel = null; + nestedJarHandler.markSliceAsClosed(this); + } + } +} 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 0831e8384..a564bbc79 100644 --- a/src/main/java/nonapi/io/github/classgraph/fileslice/Slice.java +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/Slice.java @@ -29,6 +29,7 @@ */ package nonapi.io.github.classgraph.fileslice; +import java.io.Closeable; import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -46,7 +47,7 @@ * A slice of a {@link File}, {@link ByteBuffer} or {@link InputStream}. A single {@link Slice} instance should only * be used by a single thread. */ -public abstract class Slice { +public abstract class Slice implements Closeable { /** The {@link NestedJarHandler}. */ protected final NestedJarHandler nestedJarHandler; @@ -57,7 +58,7 @@ public abstract class Slice { public final long sliceStartPos; /** The length of the slice, or -1L if unknown (for {@link InputStream}). */ - public final long sliceLength; + public long sliceLength; /** If true, the slice is a deflated zip entry, and needs to be inflated to access the content. */ public final boolean isDeflatedZipEntry; @@ -281,6 +282,10 @@ public ByteBuffer read() throws IOException { return ByteBuffer.wrap(load()); } + @Override + public void close() throws IOException { + } + @Override public int hashCode() { if (hashCode == 0) { diff --git a/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessFileReader.java b/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessFileChannelReader.java similarity index 97% rename from src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessFileReader.java rename to src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessFileChannelReader.java index 7621a4eb6..937ba0e5b 100644 --- a/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessFileReader.java +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/reader/RandomAccessFileChannelReader.java @@ -41,7 +41,7 @@ * {@link RandomAccessReader} for a {@link File}. Reads in little endian order, as required by the zipfile * format. */ -public class RandomAccessFileReader implements RandomAccessReader { +public class RandomAccessFileChannelReader implements RandomAccessReader { /** The file channel. */ private final FileChannel fileChannel; @@ -74,7 +74,8 @@ public class RandomAccessFileReader implements RandomAccessReader { * @param sliceLength * the slice length */ - public RandomAccessFileReader(final FileChannel fileChannel, final long sliceStartPos, final long sliceLength) { + public RandomAccessFileChannelReader(final FileChannel fileChannel, final long sliceStartPos, + final long sliceLength) { this.fileChannel = fileChannel; this.sliceStartPos = sliceStartPos; this.sliceLength = sliceLength; 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 484aea5ee..2ea883ccc 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -35,6 +35,7 @@ import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; +import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.Path; import java.nio.file.Paths; @@ -281,6 +282,27 @@ public static void checkCanReadAndIsFile(final File file) throws IOException { } } + /** + * Check if a {@link Path} exists, is a regular file, and can be read. + * + * @param path + * A {@link Path}. + * @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 { + if (!Files.exists(path)) { + throw new FileNotFoundException("Path does not exist or cannot be read: " + path); + } + } catch (final SecurityException e) { + throw new FileNotFoundException("Path " + path + " cannot be accessed: " + e); + } + if (!Files.isRegularFile(path)) { + throw new IOException("Not a regular file: " + path); + } + } + /** * Check if a {@link File} exists, is a directory, and can be read. * From 36d3e9589871760ed636f084e05ac3285eee9939 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 23 Apr 2020 07:47:54 -0600 Subject: [PATCH 0763/1778] Revert "Don't determine system jars if system jar scanning is disabled" This reverts commit 40025da269dcc492a53e99d6ec9959166da84125. --- .../classgraph/classpath/ClasspathFinder.java | 35 +++++++++---------- .../classgraph/classpath/ClasspathOrder.java | 1 - .../classgraph/classpath/SystemJarFinder.java | 20 +++-------- .../io/github/classgraph/utils/LogNode.java | 5 +++ 4 files changed, 26 insertions(+), 35 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 1de94c8d4..bc24ff718 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java @@ -170,35 +170,34 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { classLoaderOrderRespectingParentDelegation = contextClassLoaders; } else if (scanSpec.overrideClassLoaders == null) { + // If system jars are not blacklisted, add JRE rt.jar to the beginning of the classpath + final String jreRtJar = SystemJarFinder.getJreRtJarPath(); + + // Add rt.jar and/or lib/ext jars to beginning of classpath, if enabled final LogNode systemJarsLog = classpathFinderLog == null ? null : classpathFinderLog.log("System jars:"); - if (scanSpec.enableSystemJarsAndModules) { - // If system jars are not blacklisted, add JRE rt.jar to the beginning of the classpath - final String jreRtJar = SystemJarFinder.getJreRtJarPath(); - - // Add rt.jar and/or lib/ext jars to beginning of classpath, if enabled - if (jreRtJar != null) { + if (jreRtJar != null) { + if (scanSpec.enableSystemJarsAndModules) { classpathOrder.addSystemClasspathEntry(jreRtJar, defaultClassLoader); if (systemJarsLog != null) { systemJarsLog.log("Found rt.jar: " + jreRtJar); } + } else if (systemJarsLog != null) { + systemJarsLog.log((scanSpec.enableSystemJarsAndModules ? "" : "Scanning disabled for rt.jar: ") + + jreRtJar); } - } else if (systemJarsLog != null) { - systemJarsLog.log("Scanning disabled for system jars and modules"); } final boolean scanAllLibOrExtJars = !scanSpec.libOrExtJarWhiteBlackList.whitelistAndBlacklistAreEmpty(); - if (scanAllLibOrExtJars) { - for (final String libOrExtJarPath : SystemJarFinder.getJreLibOrExtJars()) { - if (scanAllLibOrExtJars || scanSpec.libOrExtJarWhiteBlackList - .isSpecificallyWhitelistedAndNotBlacklisted(libOrExtJarPath)) { - classpathOrder.addSystemClasspathEntry(libOrExtJarPath, defaultClassLoader); - if (systemJarsLog != null) { - systemJarsLog.log("Found lib or ext jar: " + libOrExtJarPath); - } + for (final String libOrExtJarPath : SystemJarFinder.getJreLibOrExtJars()) { + if (scanAllLibOrExtJars || scanSpec.libOrExtJarWhiteBlackList + .isSpecificallyWhitelistedAndNotBlacklisted(libOrExtJarPath)) { + classpathOrder.addSystemClasspathEntry(libOrExtJarPath, defaultClassLoader); + if (systemJarsLog != null) { + systemJarsLog.log("Found lib or ext jar: " + libOrExtJarPath); } + } else if (systemJarsLog != null) { + systemJarsLog.log("Scanning disabled for lib or ext jar: " + libOrExtJarPath); } - } else if (systemJarsLog != null) { - systemJarsLog.log("Scanning disabled for lib or ext jars"); } } 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 31c0c0ce1..f9e88278b 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java @@ -187,7 +187,6 @@ private boolean addClasspathEntry(final Object pathElement, final String pathEle } } else { if (scanSpec.overrideClasspath == null // - && scanSpec.enableSystemJarsAndModules // && (SystemJarFinder.getJreLibOrExtJars().contains(pathElementStr) || pathElementStr.equals(SystemJarFinder.getJreRtJarPath()))) { // JRE lib and ext jars are handled separately, so reject them as duplicates if they are 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 0dfcb6efb..be2116d8e 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/SystemJarFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/SystemJarFinder.java @@ -32,7 +32,6 @@ import java.io.IOException; import java.util.LinkedHashSet; import java.util.Set; -import java.util.concurrent.atomic.AtomicBoolean; import nonapi.io.github.classgraph.utils.FastPathResolver; import nonapi.io.github.classgraph.utils.FileUtils; @@ -42,16 +41,13 @@ /** A class to find rt.jar and any JRE "lib/" or "ext/" jars. */ public final class SystemJarFinder { /** The paths of any "rt.jar" files found in the JRE. */ - private static Set RT_JARS; + private static final Set RT_JARS = new LinkedHashSet<>(); /** The path of the first "rt.jar" found. */ - private static String RT_JAR; + private static final String RT_JAR; /** The paths of any "lib/" or "ext/" jars found in the JRE. */ - private static Set JRE_LIB_OR_EXT_JARS; - - /** True if the system jars have already been found. */ - private static final AtomicBoolean initialized = new AtomicBoolean(); + private static final Set JRE_LIB_OR_EXT_JARS = new LinkedHashSet<>(); /** * Constructor. @@ -100,9 +96,7 @@ private static boolean addJREPath(final File dir) { } // Find jars in JRE dirs ({java.home}, {java.home}/lib, {java.home}/lib/ext, etc.) - private static void initialize() { - RT_JARS = new LinkedHashSet<>(); - JRE_LIB_OR_EXT_JARS = new LinkedHashSet<>(); + static { String javaHome = VersionFinder.getProperty("java.home"); if (javaHome == null || javaHome.isEmpty()) { javaHome = System.getenv("JAVA_HOME"); @@ -182,9 +176,6 @@ private static void initialize() { * @return The path of rt.jar (in JDK 7 or 8), or null if it wasn't found (e.g. in JDK 9+). */ public static String getJreRtJarPath() { - if (!initialized.getAndSet(true)) { - initialize(); - } // Only include the first rt.jar -- if there is a copy in both the JDK and JRE, no need to scan both return RT_JAR; } @@ -195,9 +186,6 @@ public static String getJreRtJarPath() { * @return The paths for any jarfiles found in JRE/JDK "lib/" or "ext/" directories. */ public static Set getJreLibOrExtJars() { - if (!initialized.getAndSet(true)) { - initialize(); - } return JRE_LIB_OR_EXT_JARS; } } 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 d0028ad4d..361f67c57 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/LogNode.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/LogNode.java @@ -42,6 +42,7 @@ import java.util.logging.Logger; import io.github.classgraph.ClassGraph; +import nonapi.io.github.classgraph.classpath.SystemJarFinder; /** * A tree-structured threadsafe log that allows you to add log entries in arbitrary order, and have the output @@ -149,6 +150,10 @@ private void logJavaInfo() { + VersionFinder.getProperty("java.runtime.version") + " (" + VersionFinder.getProperty("java.vendor") + ")"); log("Java home: " + VersionFinder.getProperty("java.home")); + final String jreRtJarPath = SystemJarFinder.getJreRtJarPath(); + if (jreRtJarPath != null) { + log("JRE rt.jar:").log(jreRtJarPath); + } } /** From a631a5db4471f2ed2b3513bd8d474f0bdc88cef3 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 23 Apr 2020 07:49:54 -0600 Subject: [PATCH 0764/1778] Fix warnings --- .../io/github/classgraph/fastzipfilereader/LogicalZipFile.java | 1 + 1 file changed, 1 insertion(+) 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 2b6a492b4..2ebf001f1 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java @@ -434,6 +434,7 @@ private void parseManifest(final FastZipEntry manifestZipEntry, final LogNode lo * @throws InterruptedException * if the thread was interrupted. */ + @SuppressWarnings("resource") private void readCentralDirectory(final NestedJarHandler nestedJarHandler, final LogNode log) throws IOException, InterruptedException { if (slice.sliceLength < 22) { From c8e7cafaa2b89afc54008369b406cc8ead6580c9 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 23 Apr 2020 07:52:58 -0600 Subject: [PATCH 0765/1778] Add missing groupId --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index ad45c0900..47754d070 100644 --- a/pom.xml +++ b/pom.xml @@ -234,6 +234,7 @@ + org.apache.maven.plugins maven-enforcer-plugin From 80defff015c8fc48b819b226f144243d8ea3cc8c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 23 Apr 2020 07:56:56 -0600 Subject: [PATCH 0766/1778] [maven-release-plugin] prepare release classgraph-4.8.74 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 47754d070..fa6ea8ec9 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.74-SNAPSHOT + 4.8.74 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.74 From 14640cae3698396c83dda7488724fe65d04739b6 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 23 Apr 2020 09:22:41 -0600 Subject: [PATCH 0767/1778] Bump version back down --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fa6ea8ec9..94ab1c920 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.74 + 4.8.74-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 79c88c3433e7089af849052355ad582a5f073a56 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 23 Apr 2020 09:23:26 -0600 Subject: [PATCH 0768/1778] [maven-release-plugin] prepare release classgraph-4.8.74 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 94ab1c920..fa6ea8ec9 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.74-SNAPSHOT + 4.8.74 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 9c55adb6dfaa6471fe6f0a7468f3acf0ac0678da Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 23 Apr 2020 09:26:30 -0600 Subject: [PATCH 0769/1778] Bump version back down --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fa6ea8ec9..94ab1c920 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.74 + 4.8.74-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From dac61c3de97155d6dfe5e09bc7da1131151f6422 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 23 Apr 2020 09:26:45 -0600 Subject: [PATCH 0770/1778] [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 94ab1c920..f9735179c 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.74-SNAPSHOT + 4.8.75-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From cbdf1c20c8dd027426a30ab1cc4226da2056d1e5 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 23 Apr 2020 12:55:51 -0600 Subject: [PATCH 0771/1778] Bump version code back down --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f9735179c..94ab1c920 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.75-SNAPSHOT + 4.8.74-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From a4c8084fd514c3c937224558a60a79324a86ae99 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 23 Apr 2020 12:56:21 -0600 Subject: [PATCH 0772/1778] [maven-release-plugin] prepare release classgraph-4.8.74 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 94ab1c920..fa6ea8ec9 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.74-SNAPSHOT + 4.8.74 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From cf3baf194189539ccbc14d925ef89481f1564fc9 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 23 Apr 2020 12:56:30 -0600 Subject: [PATCH 0773/1778] [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 fa6ea8ec9..f9735179c 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.74 + 4.8.75-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 500f18d754965514116da4daf294a2c7687ce897 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 23 Apr 2020 15:31:55 -0600 Subject: [PATCH 0774/1778] Add support for scanning Path instances in custom filesystems (#420) --- ...tDir.java => ClasspathElementFileDir.java} | 11 +- .../classgraph/ClasspathElementPathDir.java | 592 ++++++++++++++++++ .../classgraph/ClasspathElementZip.java | 3 +- .../java/io/github/classgraph/Scanner.java | 39 +- .../io/github/classgraph/utils/FileUtils.java | 36 ++ .../issues/issue420/Issue420Test.java | 40 +- 6 files changed, 708 insertions(+), 13 deletions(-) rename src/main/java/io/github/classgraph/{ClasspathElementDir.java => ClasspathElementFileDir.java} (98%) create mode 100644 src/main/java/io/github/classgraph/ClasspathElementPathDir.java diff --git a/src/main/java/io/github/classgraph/ClasspathElementDir.java b/src/main/java/io/github/classgraph/ClasspathElementFileDir.java similarity index 98% rename from src/main/java/io/github/classgraph/ClasspathElementDir.java rename to src/main/java/io/github/classgraph/ClasspathElementFileDir.java index fab8ef5dd..c4907be12 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementFileDir.java @@ -55,8 +55,8 @@ import nonapi.io.github.classgraph.utils.LogNode; import nonapi.io.github.classgraph.utils.VersionFinder; -/** A directory classpath element. */ -class ClasspathElementDir extends ClasspathElement { +/** A directory classpath element, using the {@link File} API. */ +class ClasspathElementFileDir extends ClasspathElement { /** The directory at the root of the classpath element. */ private final File classpathEltDir; @@ -81,7 +81,7 @@ class ClasspathElementDir extends ClasspathElement { * @param scanSpec * the scan spec */ - ClasspathElementDir(final File classpathEltDir, final ClassLoader classLoader, + ClasspathElementFileDir(final File classpathEltDir, final ClassLoader classLoader, final NestedJarHandler nestedJarHandler, final ScanSpec scanSpec) { super(classLoader, scanSpec); this.classpathEltDir = classpathEltDir; @@ -428,6 +428,7 @@ private void scanDirRecursively(final File dir, final LogNode log) { final Resource resource = newResource("module-info.class", fileInDir, nestedJarHandler); addWhitelistedResource(resource, parentMatchStatus, /* isClassfileOnly = */ true, subLog); fileToLastModified.put(fileInDir, fileInDir.lastModified()); + break; } } } @@ -523,10 +524,10 @@ public String toString() { public boolean equals(final Object obj) { if (obj == this) { return true; - } else if (!(obj instanceof ClasspathElementDir)) { + } else if (!(obj instanceof ClasspathElementFileDir)) { return false; } - final ClasspathElementDir other = (ClasspathElementDir) obj; + final ClasspathElementFileDir other = (ClasspathElementFileDir) obj; return this.classpathEltDir.equals(other.classpathEltDir); } diff --git a/src/main/java/io/github/classgraph/ClasspathElementPathDir.java b/src/main/java/io/github/classgraph/ClasspathElementPathDir.java new file mode 100644 index 000000000..de843853c --- /dev/null +++ b/src/main/java/io/github/classgraph/ClasspathElementPathDir.java @@ -0,0 +1,592 @@ +/* + * 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.IOError; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.nio.ByteBuffer; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.PosixFileAttributes; +import java.nio.file.attribute.PosixFilePermission; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +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.classpath.ClasspathOrder.ClasspathElementAndClassLoader; +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.PathSlice; +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.FileUtils; +import nonapi.io.github.classgraph.utils.LogNode; +import nonapi.io.github.classgraph.utils.VersionFinder; + +/** A directory classpath element, using the {@link Path} API. */ +class ClasspathElementPathDir extends ClasspathElement { + /** The directory at the root of the classpath element. */ + private final Path classpathEltPath; + + /** 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 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 ClassLoader classLoader, + final NestedJarHandler nestedJarHandler, final ScanSpec scanSpec) { + super(classLoader, scanSpec); + this.classpathEltPath = classpathEltPath; + 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: " + classpathEltPath, log); + } + skipClasspathElement = true; + return; + } + try { + // Auto-add nested lib dirs + int childClasspathEntryIdx = 0; + for (final String libDirPrefix : ClassLoaderHandlerRegistry.AUTOMATIC_LIB_DIR_PREFIXES) { + 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)) { + for (final Path filePath : stream) { + if (Files.isRegularFile(filePath) && filePath.getFileName().endsWith(".jar")) { + if (log != null) { + log(classpathElementIdx, "Found lib jar: " + filePath, log); + } + workQueue.addWorkUnit(new ClasspathEntryWorkUnit( + /* rawClasspathEntry = */ // + new ClasspathElementAndClassLoader(filePath, classLoader), + /* parentClasspathElement = */ this, + /* orderWithinParentClasspathElement = */ childClasspathEntryIdx++)); + } + } + } catch (final IOException e) { + // Ignore -- thrown by Files.newDirectoryStream + } + } + } + for (final String packageRootPrefix : ClassLoaderHandlerRegistry.AUTOMATIC_PACKAGE_ROOT_PREFIXES) { + final Path packageRootPath = classpathEltPath.resolve(packageRootPrefix); + if (FileUtils.canReadAndIsDir(packageRootPath)) { + if (log != null) { + log(classpathElementIdx, "Found package root: " + packageRootPath, log); + } + workQueue.addWorkUnit(new ClasspathEntryWorkUnit( + /* rawClasspathEntry = */ new ClasspathElementAndClassLoader(packageRootPath, + classLoader), + /* parentClasspathElement = */ this, + /* orderWithinParentClasspathElement = */ childClasspathEntryIdx++)); + } + } + } catch (final SecurityException e) { + if (log != null) { + log(classpathElementIdx, + "Skipping classpath element, since dir cannot be accessed: " + classpathEltPath, log); + } + skipClasspathElement = true; + return; + } + } + + /** + * Create a new {@link Resource} object for a resource or classfile discovered while scanning paths. + * + * @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) { + /** The {@link PathSlice} opened on the file. */ + private PathSlice pathSlice; + + /** True if the resource is open. */ + protected AtomicBoolean isOpen = new AtomicBoolean(); + + @Override + public String getPath() { + String path = resourcePath.toString(); + while (path.startsWith("/")) { + path = path.substring(1); + } + return path; + } + + @Override + public String getPathRelativeToClasspathElement() { + String path = classpathEltPath.relativize(resourcePath).toString(); + while (path.startsWith("/")) { + path = path.substring(1); + } + return path; + } + + @Override + public long getLastModified() { + try { + return resourcePath.toFile().lastModified(); + } catch (final UnsupportedOperationException e) { + return 0L; + } + } + + @SuppressWarnings("null") + @Override + public Set getPosixFilePermissions() { + Set posixFilePermissions = null; + try { + posixFilePermissions = Files.readAttributes(resourcePath, 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()"); + } + pathSlice = new PathSlice(resourcePath, nestedJarHandler, /* log = */ null); + length = pathSlice.sliceLength; + byteBuffer = pathSlice.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 PathSlice and then open it + pathSlice = new PathSlice(resourcePath, nestedJarHandler, /* log = */ null); + length = pathSlice.sliceLength; + return new ClassfileReader(pathSlice); + } + + @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()"); + } + pathSlice = new PathSlice(resourcePath, nestedJarHandler, /* log = */ null); + inputStream = pathSlice.open(); + length = pathSlice.sliceLength; + return inputStream; + } + + @Override + public byte[] load() throws IOException { + read(); + try (Resource res = this) { // Close this after use + pathSlice = new PathSlice(resourcePath, nestedJarHandler, /* log = */ null); + final byte[] bytes = pathSlice.load(); + length = bytes.length; + return bytes; + } + } + + @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 + byteBuffer = null; + } + if (pathSlice != null) { + pathSlice.close(); + nestedJarHandler.markSliceAsClosed(pathSlice); + pathSlice = null; + } + } + } + }; + } + + /** + * Get the {@link Resource} for a given relative path. + * + * @param relativePath + * 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 relativePath) { + final Path resourcePath = classpathEltPath.resolve(relativePath); + return FileUtils.canReadAndIsFile(resourcePath) ? newResource(resourcePath, nestedJarHandler) : null; + } + + /** + * Recursively scan a {@link Path} for sub-path patterns matching the scan spec. + * + * @param path + * the {@link Path} + * @param log + * 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; + try { + canonicalPath = path.toRealPath(); + if (!scannedCanonicalPaths.add(canonicalPath)) { + if (log != null) { + log.log("Reached symlink cycle, stopping recursion: " + path); + } + return; + } + } catch (final IOException | SecurityException e) { + if (log != null) { + log.log("Could not canonicalize path: " + path, e); + } + return; + } + + final Path dirRelativePath = classpathEltPath.relativize(path); + String dirRelativePathStr = dirRelativePath.toString(); + while (dirRelativePathStr.startsWith("/")) { + dirRelativePathStr = dirRelativePathStr.substring(1); + } + if (!dirRelativePathStr.endsWith("/")) { + dirRelativePathStr += "/"; + } + final boolean isDefaultPackage = dirRelativePathStr.equals("/"); + + if (nestedClasspathRootPrefixes != null && nestedClasspathRootPrefixes.contains(dirRelativePathStr)) { + if (log != null) { + log.log("Reached nested classpath root, stopping recursion to avoid duplicate scanning: " + + dirRelativePathStr); + } + 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 (dirRelativePathStr.startsWith(LogicalZipFile.MULTI_RELEASE_PATH_PREFIX)) { + if (log != null) { + log.log("Found unexpected nested versioned entry in directory classpath element -- skipping: " + + dirRelativePathStr); + } + return; + } + + // Whitelist/blacklist classpath elements based on dir resource paths + checkResourcePathWhiteBlackList(dirRelativePathStr, log); + if (skipClasspathElement) { + return; + } + + final ScanSpecPathMatch parentMatchStatus = scanSpec.dirWhitelistMatchStatus(dirRelativePathStr); + if (parentMatchStatus == ScanSpecPathMatch.HAS_BLACKLISTED_PATH_PREFIX) { + // Reached a non-whitelisted or blacklisted path -- stop the recursive scan + if (log != null) { + log.log("Reached blacklisted directory, stopping recursive scan: " + dirRelativePathStr); + } + return; + } + if (parentMatchStatus == ScanSpecPathMatch.NOT_WITHIN_WHITELISTED_PATH) { + // Reached a non-whitelisted and non-blacklisted path -- stop the recursive scan + return; + } + + final List pathsInDir = new ArrayList<>(); + try (DirectoryStream stream = Files.newDirectoryStream(path)) { + for (final Path subPath : stream) { + pathsInDir.add(subPath); + } + } catch (IOException | SecurityException e) { + if (log != null) { + log.log("Could not read directory " + path + " : " + e.getMessage()); + } + skipClasspathElement = true; + return; + } + Collections.sort(pathsInDir); + + final LogNode subLog = log == null ? null + // Log dirs after files (addWhitelistedResources() precedes log entry with "0:") + : log.log("1:" + canonicalPath, "Scanning directory: " + path + + (path.equals(canonicalPath) ? "" : " ; canonical path: " + canonicalPath)); + + // 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 a whitelisted path + if (parentMatchStatus != ScanSpecPathMatch.ANCESTOR_OF_WHITELISTED_PATH) { + // Do preorder traversal (files in dir, then subdirs), to reduce filesystem cache misses + for (final Path subPath : pathsInDir) { + // Process files in dir before recursing + if (Files.isRegularFile(subPath)) { + final Path subPathRelative = classpathEltPath.relativize(subPath); + final String subPathRelativeStr = subPathRelative.toString(); + // 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 && subPathRelativeStr.endsWith(".class") + && !subPathRelativeStr.equals("module-info.class")) { + continue; + } + + // Whitelist/blacklist classpath elements based on file resource paths + checkResourcePathWhiteBlackList(subPathRelativeStr, subLog); + if (skipClasspathElement) { + return; + } + + // If relative path is whitelisted + if (parentMatchStatus == ScanSpecPathMatch.HAS_WHITELISTED_PATH_PREFIX + || parentMatchStatus == ScanSpecPathMatch.AT_WHITELISTED_PATH + || (parentMatchStatus == ScanSpecPathMatch.AT_WHITELISTED_CLASS_PACKAGE + && scanSpec.classfileIsSpecificallyWhitelisted(subPathRelativeStr))) { + // Resource is whitelisted + final Resource resource = newResource(subPath, nestedJarHandler); + addWhitelistedResource(resource, parentMatchStatus, /* isClassfileOnly = */ false, subLog); + + // Save last modified time + try { + fileToLastModified.put(subPath.toFile(), subPath.toFile().lastModified()); + } catch (final UnsupportedOperationException e) { + // Ignore + } + } else { + if (subLog != null) { + subLog.log("Skipping non-whitelisted file: " + subPathRelative); + } + } + } + } + } else if (scanSpec.enableClassInfo && dirRelativePathStr.equals("/")) { + // Always check for module descriptor in package root, even if package root isn't in whitelist + for (final Path subPath : pathsInDir) { + if (subPath.getFileName().equals("module-info.class") && Files.isRegularFile(subPath)) { + final Resource resource = newResource(subPath, nestedJarHandler); + addWhitelistedResource(resource, parentMatchStatus, /* isClassfileOnly = */ true, subLog); + try { + fileToLastModified.put(subPath.toFile(), subPath.toFile().lastModified()); + } catch (final UnsupportedOperationException e) { + // Ignore + } + break; + } + } + } + // Recurse into subdirectories + for (final Path subPath : pathsInDir) { + try { + if (Files.isDirectory(subPath)) { + scanPathRecursively(subPath, subLog); + // If a blacklisted 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) { + subLog.log("Could not read sub-directory " + subPath + " : " + e.getMessage()); + } + } + } + + if (subLog != null) { + subLog.addElapsedTime(); + } + + // Save the last modified time of the directory + try { + final File file = path.toFile(); + fileToLastModified.put(file, file.lastModified()); + } catch (final UnsupportedOperationException e) { + // Ignore + } + } + + /** + * 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 " + toString()); + } + + final LogNode subLog = log == null ? null + : log(classpathElementIdx, "Scanning directory classpath element " + classpathEltPath, log); + + scanPathRecursively(classpathEltPath, 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() { + try { + return classpathEltPath.toFile(); + } catch (final UnsupportedOperationException e) { + return null; + } + } + + /* (non-Javadoc) + * @see io.github.classgraph.ClasspathElement#getURI() + */ + @Override + URI getURI() { + return classpathEltPath.toUri(); + } + + /** + * Return the classpath element directory as a String. + * + * @return the string + */ + @Override + public String toString() { + try { + return classpathEltPath.toUri().toString(); + } catch (IOError | SecurityException e) { + return classpathEltPath.toString(); + } + } + + /* (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 ClasspathElementPathDir)) { + return false; + } + final ClasspathElementPathDir other = (ClasspathElementPathDir) obj; + return this.classpathEltPath.equals(other.classpathEltPath); + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return classpathEltPath.hashCode(); + } +} diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index 11b11e675..1fec087ec 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -35,6 +35,7 @@ import java.net.URISyntaxException; import java.net.URL; import java.nio.ByteBuffer; +import java.nio.file.Path; import java.nio.file.attribute.PosixFilePermission; import java.util.HashSet; import java.util.Map.Entry; @@ -99,7 +100,7 @@ class ClasspathElementZip extends ClasspathElement { ClasspathElementZip(final Object rawPathObj, final ClassLoader classLoader, final NestedJarHandler nestedJarHandler, final ScanSpec scanSpec) { super(classLoader, scanSpec); - // Convert the raw path object (String, URL or URI) to a string. + // Convert the raw path object (String, URL, or URI) to a string. // Any required URL/URI parsing are done in NestedJarHandler. this.rawPath = rawPathObj.toString(); this.zipFilePath = rawPath; // May change when open() is called diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index b14f71b99..aed108135 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -32,8 +32,13 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.net.MalformedURLException; +import java.net.URISyntaxException; import java.net.URL; import java.net.URLDecoder; +import java.nio.file.FileSystemNotFoundException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Collection; @@ -413,10 +418,32 @@ public ClasspathElement newInstance(final ClasspathElementAndClassLoader classpa return new ClasspathElementZip(classpathEntryURL, classpathEntry.classLoader, nestedJarHandler, scanSpec); } else { - // For custom URL schemes, assume it must be for a jar, not a directory + try { + // See if the URL resolves to a Path of type directory + final Path path = Paths.get(classpathEntryURL.toURI()); + if (Files.isDirectory(path)) { + // If URL maps to a directory, use the NIO Path API instead of the File API + return new ClasspathElementPathDir(path, classpathEntry.classLoader, + nestedJarHandler, scanSpec); + } + } catch (final URISyntaxException | IllegalArgumentException | SecurityException e) { + throw new IOException( + "Cannot handle URL " + classpathEntryURL + " : " + e.getMessage()); + } catch (final FileSystemNotFoundException e) { + // Fall through to below -- this is a custom URL scheme without a backing FileSystem + } + + // For custom URL schemes that do not resolve to directories, assume the URL + // points to a jarfile return new ClasspathElementZip(classpathEntryURL, classpathEntry.classLoader, nestedJarHandler, scanSpec); } + } else if (classpathEntryObj instanceof Path) { + // classpathEntryObj is a Path (which points to a lib/ext jar inside a parent Path -- + // see ClasspathElementPath.java) + return new ClasspathElementZip(((Path) classpathEntryObj).toUri(), + classpathEntry.classLoader, nestedJarHandler, scanSpec); + } else { // classpathEntryObj is a string classpathEntryPath = classpathEntryObj.toString(); @@ -480,7 +507,7 @@ public ClasspathElement newInstance(final ClasspathElementAndClassLoader classpa return isJar ? new ClasspathElementZip(canonicalPathNormalized, classpathEntry.classLoader, nestedJarHandler, scanSpec) - : new ClasspathElementDir(fileCanonicalized, classpathEntry.classLoader, + : new ClasspathElementFileDir(fileCanonicalized, classpathEntry.classLoader, nestedJarHandler, scanSpec); } } @@ -488,7 +515,7 @@ public ClasspathElement newInstance(final ClasspathElementAndClassLoader classpa /** * Create a WorkUnitProcessor for opening traditional classpath entries (which are mapped to - * {@link ClasspathElementDir} or {@link ClasspathElementZip} -- {@link ClasspathElementModule is handled + * {@link ClasspathElementFileDir} or {@link ClasspathElementZip} -- {@link ClasspathElementModule is handled * separately}). * * @param openedClasspathElementsSet @@ -763,10 +790,10 @@ private void preprocessClasspathElementsByType(final List fina final List> classpathEltDirs = new ArrayList<>(); final List> classpathEltZips = new ArrayList<>(); for (final ClasspathElement classpathElt : finalTraditionalClasspathEltOrder) { - if (classpathElt instanceof ClasspathElementDir) { + if (classpathElt instanceof ClasspathElementFileDir) { // Separate out ClasspathElementDir elements from other types - classpathEltDirs.add( - new SimpleEntry<>(((ClasspathElementDir) classpathElt).getFile().getPath(), classpathElt)); + classpathEltDirs.add(new SimpleEntry<>(((ClasspathElementFileDir) 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/utils/FileUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java index 2ea883ccc..d24bb25f5 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -261,6 +261,24 @@ public static boolean canReadAndIsFile(final File file) { return file.isFile(); } + /** + * Check if a {@link Path} exists, is a regular file, and can be read. + * + * @param path + * A {@link Path}. + * @return true if the file exists, is a regular file, and can be read. + */ + public static boolean canReadAndIsFile(final Path path) { + try { + if (!Files.exists(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. * @@ -321,6 +339,24 @@ public static boolean canReadAndIsDir(final File file) { return file.isDirectory(); } + /** + * Check if a {@link Path} exists, is a directory, and can be read. + * + * @param path + * A {@link Path}. + * @return true if the file exists, is a directory, and can be read. + */ + public static boolean canReadAndIsDir(final Path path) { + try { + if (!Files.exists(path)) { + return false; + } + } catch (final SecurityException e) { + return false; + } + return Files.isDirectory(path); + } + /** * Check if a {@link File} exists, is a directory, and can be read. * 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 52ebace15..21de9312f 100644 --- a/src/test/java/io/github/classgraph/issues/issue420/Issue420Test.java +++ b/src/test/java/io/github/classgraph/issues/issue420/Issue420Test.java @@ -41,6 +41,7 @@ import org.junit.jupiter.api.Test; +import com.google.common.jimfs.Configuration; import com.google.common.jimfs.Jimfs; import io.github.classgraph.ClassGraph; @@ -59,7 +60,7 @@ public class Issue420Test { * If a URI is bad. */ @Test - public void issue420Test() throws IOException, URISyntaxException { + public void testScanningFileBackedByFileSystem() throws IOException, URISyntaxException { try (FileSystem memFs = Jimfs.newFileSystem()) { final Path jarPath = Paths .get(getClass().getClassLoader().getResource("multi-release-jar.jar").toURI()); @@ -78,4 +79,41 @@ public void issue420Test() throws IOException, URISyntaxException { } } } + + /** + * Test. + * + * @throws IOException + * If an I/O exception occurred. + * @throws URISyntaxException + * If a URI is bad. + */ + @Test + public void testScanningDirBackedByFileSystem() throws IOException, URISyntaxException { + try (FileSystem memFs = Jimfs + // Change working directory from the default of "/work" to "/" + .newFileSystem(Configuration.unix().toBuilder().setWorkingDirectory("/").build())) { + final String packageName = "io.github.classgraph.issues.issue146"; + final String packagePath = packageName.replace('.', '/'); + final String className = packageName + ".CompiledWithJDK8"; + final String classFilePath = className.replace('.', '/') + ".class"; + final Path jarPath = Paths.get(getClass().getClassLoader().getResource(classFilePath).toURI()); + final Path memFsDirPath = memFs.getPath(packagePath); + Files.createDirectories(memFsDirPath); + final Path memFsFilePath = memFs.getPath(classFilePath); + final Path memFsCopyOfClassFile = Files.copy(jarPath, memFsFilePath); + assertThat(Files.exists(memFsCopyOfClassFile)); + final Path memFsRoot = memFs.getPath("/"); + final URL memFsRootURL = memFsRoot.toUri().toURL(); + try (URLClassLoader childClassLoader = new URLClassLoader(new URL[] { memFsRootURL }, + getClass().getClassLoader())) { + final ClassGraph classGraph = new ClassGraph().enableURLScheme(memFsRootURL.getProtocol()) + .overrideClassLoaders(childClassLoader).ignoreParentClassLoaders() + .whitelistPackages(packageName).enableAllInfo(); + try (ScanResult scanResult = classGraph.scan()) { + assertThat(scanResult.getClassInfo(className)).isNotNull(); + } + } + } + } } From 7d6bcd629ebfcf55a96d20875805912f56e66809 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 23 Apr 2020 15:32:30 -0600 Subject: [PATCH 0775/1778] [maven-release-plugin] prepare release classgraph-4.8.75 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index f9735179c..4841a11f6 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.75-SNAPSHOT + 4.8.75 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.74 + classgraph-4.8.75 From 2691867c3a537704135e1454fb6f3f96372d2ac0 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 23 Apr 2020 15:32:38 -0600 Subject: [PATCH 0776/1778] [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 4841a11f6..174d26008 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.75 + 4.8.76-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.75 + classgraph-4.8.74 From 6e8159fe7e4a8f2fb1b25b912ccbbd603202c50b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 24 Apr 2020 00:52:38 -0600 Subject: [PATCH 0777/1778] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9fdcef83c..63148bc14 100644 --- a/README.md +++ b/README.md @@ -172,7 +172,7 @@ Some other classpath scanning mechanisms include: **The MIT License (MIT)** -**Copyright (c) 2019 Luke Hutchison** +**Copyright (c) 2020 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 cfa7f97e43355b7b1c7bef8daf41d453d7ed1270 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 26 Apr 2020 17:38:22 -0600 Subject: [PATCH 0778/1778] Allow package hierarchy to be rooted at non-root URL (#420) --- .../classgraph/ClasspathElementFileDir.java | 90 +++++++++++-------- .../classgraph/ClasspathElementPathDir.java | 73 ++++++++------- .../classgraph/ClasspathElementZip.java | 9 +- .../java/io/github/classgraph/Scanner.java | 77 +++++++++++----- .../classgraph/classpath/ClasspathOrder.java | 40 +++++++-- .../issues/issue420/Issue420Test.java | 54 ++++++++--- 6 files changed, 222 insertions(+), 121 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementFileDir.java b/src/main/java/io/github/classgraph/ClasspathElementFileDir.java index c4907be12..0d7839216 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementFileDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementFileDir.java @@ -38,6 +38,7 @@ import java.nio.file.attribute.PosixFilePermission; import java.util.Arrays; import java.util.HashSet; +import java.util.Objects; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; @@ -60,8 +61,8 @@ class ClasspathElementFileDir extends ClasspathElement { /** The directory at the root of the classpath element. */ private final File classpathEltDir; - /** The number of characters to ignore to strip the classpath element path and relativize the path. */ - private final int ignorePrefixLen; + /** 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<>(); @@ -81,11 +82,11 @@ class ClasspathElementFileDir extends ClasspathElement { * @param scanSpec * the scan spec */ - ClasspathElementFileDir(final File classpathEltDir, final ClassLoader classLoader, - final NestedJarHandler nestedJarHandler, final ScanSpec scanSpec) { + ClasspathElementFileDir(final File classpathEltDir, final String packageRootPrefix, + final ClassLoader classLoader, final NestedJarHandler nestedJarHandler, final ScanSpec scanSpec) { super(classLoader, scanSpec); this.classpathEltDir = classpathEltDir; - this.ignorePrefixLen = classpathEltDir.getPath().length() + 1; + this.packageRootDir = new File(classpathEltDir, packageRootPrefix); this.nestedJarHandler = nestedJarHandler; } @@ -120,7 +121,6 @@ void open(final WorkQueue workQueue, final LogNode log) log(classpathElementIdx, "Found lib jar: " + file, log); } workQueue.addWorkUnit(new ClasspathEntryWorkUnit( - /* rawClasspathEntry = */ // new ClasspathElementAndClassLoader(file.getPath(), classLoader), /* parentClasspathElement = */ this, /* orderWithinParentClasspathElement = */ childClasspathEntryIdx++)); @@ -129,17 +129,21 @@ void open(final WorkQueue workQueue, final LogNode log) } } } - for (final String packageRootPrefix : ClassLoaderHandlerRegistry.AUTOMATIC_PACKAGE_ROOT_PREFIXES) { - final File packageRootDir = new File(classpathEltDir, packageRootPrefix); - if (FileUtils.canReadAndIsDir(packageRootDir)) { - if (log != null) { - log(classpathElementIdx, "Found package root: " + packageRootDir, log); + // Only look for package roots if the package root is the root of the classpath element + if (packageRootDir.equals(classpathEltDir)) { + for (final String packageRootPrefix : ClassLoaderHandlerRegistry.AUTOMATIC_PACKAGE_ROOT_PREFIXES) { + final File packageRootDir = new File(classpathEltDir, packageRootPrefix); + if (FileUtils.canReadAndIsDir(packageRootDir)) { + if (log != null) { + log(classpathElementIdx, "Found package root: " + packageRootDir, log); + } + workQueue + .addWorkUnit(new ClasspathEntryWorkUnit( + new ClasspathElementAndClassLoader(classpathEltDir, packageRootPrefix, + classLoader), + /* parentClasspathElement = */ this, + /* orderWithinParentClasspathElement = */ childClasspathEntryIdx++)); } - workQueue.addWorkUnit(new ClasspathEntryWorkUnit( - /* rawClasspathEntry = */ new ClasspathElementAndClassLoader(packageRootDir.getPath(), - classLoader), - /* parentClasspathElement = */ this, - /* orderWithinParentClasspathElement = */ childClasspathEntryIdx++)); } } } catch (final SecurityException e) { @@ -155,15 +159,15 @@ void open(final WorkQueue workQueue, final LogNode log) /** * Create a new {@link Resource} object for a resource or classfile discovered while scanning paths. * - * @param relativePath - * the relative path of the resource + * @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 relativePath, final File resourceFile, + 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. */ @@ -174,12 +178,19 @@ private Resource newResource(final String relativePath, final File resourceFile, @Override public String getPath() { - return relativePath; + return pathRelativeToPackageRoot; } @Override public String getPathRelativeToClasspathElement() { - return relativePath; + // Relativize resource file to classpath element dir + final File resourceFile = new File(packageRootDir, pathRelativeToPackageRoot); + String pathRelativeToClasspathElt = resourceFile.getPath() + .substring(classpathEltDir.getPath().length()); + while (pathRelativeToClasspathElt.startsWith("/")) { + pathRelativeToClasspathElt = pathRelativeToClasspathElt.substring(1); + } + return pathRelativeToClasspathElt; } @Override @@ -280,15 +291,16 @@ public void close() { /** * Get the {@link Resource} for a given relative path. * - * @param relativePath + * @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 relativePath) { - final File resourceFile = new File(classpathEltDir, relativePath); - return FileUtils.canReadAndIsFile(resourceFile) ? newResource(relativePath, resourceFile, nestedJarHandler) + Resource getResource(final String pathRelativeToPackageRoot) { + final File resourceFile = new File(packageRootDir, pathRelativeToPackageRoot); + return FileUtils.canReadAndIsFile(resourceFile) + ? newResource(pathRelativeToPackageRoot, resourceFile, nestedJarHandler) : null; } @@ -323,6 +335,7 @@ private void scanDirRecursively(final File dir, final LogNode log) { } final String dirPath = dir.getPath(); + final int ignorePrefixLen = packageRootDir.getPath().length() + 1; final String dirRelativePath = ignorePrefixLen > dirPath.length() ? "/" // : dirPath.substring(ignorePrefixLen).replace(File.separatorChar, '/') + "/"; final boolean isDefaultPackage = "/".equals(dirRelativePath); @@ -471,9 +484,9 @@ void scanPaths(final LogNode log) { } final LogNode subLog = log == null ? null - : log(classpathElementIdx, "Scanning directory classpath element " + classpathEltDir, log); + : log(classpathElementIdx, "Scanning directory classpath element " + packageRootDir, log); - scanDirRecursively(classpathEltDir, subLog); + scanDirRecursively(packageRootDir, subLog); finishScanPaths(subLog); } @@ -504,7 +517,7 @@ public File getFile() { */ @Override URI getURI() { - return classpathEltDir.toURI(); + return packageRootDir.toURI(); } /** @@ -514,7 +527,15 @@ URI getURI() { */ @Override public String toString() { - return classpathEltDir.toString(); + return packageRootDir.toString(); + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return Objects.hash(classpathEltDir, packageRootDir); } /* (non-Javadoc) @@ -528,14 +549,7 @@ public boolean equals(final Object obj) { return false; } final ClasspathElementFileDir other = (ClasspathElementFileDir) obj; - return this.classpathEltDir.equals(other.classpathEltDir); - } - - /* (non-Javadoc) - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() { - return classpathEltDir.hashCode(); + return Objects.equals(this.classpathEltDir, other.classpathEltDir) + && Objects.equals(this.packageRootDir, other.packageRootDir); } } diff --git a/src/main/java/io/github/classgraph/ClasspathElementPathDir.java b/src/main/java/io/github/classgraph/ClasspathElementPathDir.java index de843853c..d36da3c2a 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementPathDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementPathDir.java @@ -43,6 +43,7 @@ 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; @@ -65,6 +66,9 @@ 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<>(); @@ -83,10 +87,11 @@ class ClasspathElementPathDir extends ClasspathElement { * @param scanSpec * the scan spec */ - ClasspathElementPathDir(final Path classpathEltPath, final ClassLoader classLoader, + ClasspathElementPathDir(final Path classpathEltPath, final String packageRoot, final ClassLoader classLoader, final NestedJarHandler nestedJarHandler, final ScanSpec scanSpec) { super(classLoader, scanSpec); this.classpathEltPath = classpathEltPath; + this.packageRootPath = classpathEltPath.resolve(packageRoot); this.nestedJarHandler = nestedJarHandler; } @@ -118,7 +123,6 @@ void open(final WorkQueue workQueue, final LogNode log) log(classpathElementIdx, "Found lib jar: " + filePath, log); } workQueue.addWorkUnit(new ClasspathEntryWorkUnit( - /* rawClasspathEntry = */ // new ClasspathElementAndClassLoader(filePath, classLoader), /* parentClasspathElement = */ this, /* orderWithinParentClasspathElement = */ childClasspathEntryIdx++)); @@ -129,17 +133,20 @@ void open(final WorkQueue workQueue, final LogNode log) } } } - for (final String packageRootPrefix : ClassLoaderHandlerRegistry.AUTOMATIC_PACKAGE_ROOT_PREFIXES) { - final Path packageRootPath = classpathEltPath.resolve(packageRootPrefix); - if (FileUtils.canReadAndIsDir(packageRootPath)) { - if (log != null) { - log(classpathElementIdx, "Found package root: " + packageRootPath, log); + // Only look for package roots if the package root is the root of the classpath element + if (packageRootPath.equals(classpathEltPath)) { + for (final String packageRootPrefix : ClassLoaderHandlerRegistry.AUTOMATIC_PACKAGE_ROOT_PREFIXES) { + final Path packageRootPath = classpathEltPath.resolve(packageRootPrefix); + if (FileUtils.canReadAndIsDir(packageRootPath)) { + if (log != null) { + log(classpathElementIdx, "Found package root: " + packageRootPrefix, log); + } + workQueue.addWorkUnit(new ClasspathEntryWorkUnit( + new ClasspathElementAndClassLoader(classpathEltPath, packageRootPrefix, + classLoader), + /* parentClasspathElement = */ this, + /* orderWithinParentClasspathElement = */ childClasspathEntryIdx++)); } - workQueue.addWorkUnit(new ClasspathEntryWorkUnit( - /* rawClasspathEntry = */ new ClasspathElementAndClassLoader(packageRootPath, - classLoader), - /* parentClasspathElement = */ this, - /* orderWithinParentClasspathElement = */ childClasspathEntryIdx++)); } } } catch (final SecurityException e) { @@ -177,7 +184,7 @@ private Resource newResource(final Path resourcePath, final NestedJarHandler nes @Override public String getPath() { - String path = resourcePath.toString(); + String path = packageRootPath.relativize(resourcePath).toString(); while (path.startsWith("/")) { path = path.substring(1); } @@ -302,7 +309,7 @@ public void close() { */ @Override Resource getResource(final String relativePath) { - final Path resourcePath = classpathEltPath.resolve(relativePath); + final Path resourcePath = packageRootPath.resolve(relativePath); return FileUtils.canReadAndIsFile(resourcePath) ? newResource(resourcePath, nestedJarHandler) : null; } @@ -336,7 +343,7 @@ private void scanPathRecursively(final Path path, final LogNode log) { return; } - final Path dirRelativePath = classpathEltPath.relativize(path); + final Path dirRelativePath = packageRootPath.relativize(path); String dirRelativePathStr = dirRelativePath.toString(); while (dirRelativePathStr.startsWith("/")) { dirRelativePathStr = dirRelativePathStr.substring(1); @@ -400,7 +407,7 @@ private void scanPathRecursively(final Path path, final LogNode log) { final LogNode subLog = log == null ? null // Log dirs after files (addWhitelistedResources() precedes log entry with "0:") - : log.log("1:" + canonicalPath, "Scanning directory: " + path + : log.log("1:" + canonicalPath, "Scanning Path: " + path + (path.equals(canonicalPath) ? "" : " ; canonical path: " + canonicalPath)); // Determine whether this is a modular jar running under JRE 9+ @@ -452,7 +459,7 @@ 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 whitelist for (final Path subPath : pathsInDir) { - if (subPath.getFileName().equals("module-info.class") && Files.isRegularFile(subPath)) { + if (subPath.getFileName().toString().equals("module-info.class") && Files.isRegularFile(subPath)) { final Resource resource = newResource(subPath, nestedJarHandler); addWhitelistedResource(resource, parentMatchStatus, /* isClassfileOnly = */ true, subLog); try { @@ -514,9 +521,9 @@ void scanPaths(final LogNode log) { } final LogNode subLog = log == null ? null - : log(classpathElementIdx, "Scanning directory classpath element " + classpathEltPath, log); + : log(classpathElementIdx, "Scanning Path classpath element " + getURI(), log); - scanPathRecursively(classpathEltPath, subLog); + scanPathRecursively(packageRootPath, subLog); finishScanPaths(subLog); } @@ -535,7 +542,8 @@ public String getModuleName() { /** * Get the directory {@link File}. * - * @return The classpath element directory as a {@link File}. + * @return The classpath element directory as a {@link File}, or null if this classpath element is not backed by + * a directory (should not happen). */ @Override public File getFile() { @@ -551,7 +559,7 @@ public File getFile() { */ @Override URI getURI() { - return classpathEltPath.toUri(); + return packageRootPath.toUri(); } /** @@ -562,12 +570,20 @@ URI getURI() { @Override public String toString() { try { - return classpathEltPath.toUri().toString(); + return packageRootPath.toUri().toString(); } catch (IOError | SecurityException e) { - return classpathEltPath.toString(); + return packageRootPath.toString(); } } + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return Objects.hash(classpathEltPath, packageRootPath); + } + /* (non-Javadoc) * @see java.lang.Object#equals(java.lang.Object) */ @@ -579,14 +595,7 @@ public boolean equals(final Object obj) { return false; } final ClasspathElementPathDir other = (ClasspathElementPathDir) obj; - return this.classpathEltPath.equals(other.classpathEltPath); - } - - /* (non-Javadoc) - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() { - return classpathEltPath.hashCode(); + return Objects.equals(this.classpathEltPath, other.classpathEltPath) + && Objects.equals(this.packageRootPath, other.packageRootPath); } } diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index 1fec087ec..f4ab83ff7 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -189,6 +189,7 @@ void open(final WorkQueue workQueue, final LogNode log) if (scanSpec.scanNestedJars) { for (final FastZipEntry zipEntry : logicalZipFile.entries) { for (final String libDirPrefix : ClassLoaderHandlerRegistry.AUTOMATIC_LIB_DIR_PREFIXES) { + // Even if a package root is given, e.g. BOOT-INF/classes, still look in lib/ etc. for jars if (zipEntry.entryNameUnversioned.startsWith(libDirPrefix) && zipEntry.entryNameUnversioned.endsWith(".jar")) { final String entryPath = zipEntry.getPath(); @@ -196,8 +197,7 @@ void open(final WorkQueue workQueue, final LogNode log) subLog.log("Found nested lib jar: " + entryPath); } workQueue.addWorkUnit(new ClasspathEntryWorkUnit( - /* rawClasspathEntry = */ new ClasspathElementAndClassLoader(entryPath, - classLoader), + new ClasspathElementAndClassLoader(entryPath, classLoader), /* parentClasspathElement = */ this, /* orderWithinParentClasspathElement = */ childClasspathEntryIdx++)); @@ -238,8 +238,8 @@ void open(final WorkQueue workQueue, final LogNode log) // Schedule child classpath element for scanning workQueue.addWorkUnit( // new ClasspathEntryWorkUnit( - /* rawClasspathEntry = */ new ClasspathElementAndClassLoader( - childClassPathEltPathWithPrefix, classLoader), + new ClasspathElementAndClassLoader(childClassPathEltPathWithPrefix, + classLoader), /* parentClasspathElement = */ this, /* orderWithinParentClasspathElement = */ childClasspathEntryIdx++)); @@ -269,7 +269,6 @@ void open(final WorkQueue workQueue, final LogNode log) if (scheduledChildClasspathElements.add(childClassPathEltPath)) { // Schedule child classpath element for scanning workQueue.addWorkUnit(new ClasspathEntryWorkUnit( - /* rawClasspathEntry = */ // new ClasspathElementAndClassLoader(childClassPathEltPath, classLoader), /* parentClasspathElement = */ this, /* orderWithinParentClasspathElement = */ diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index aed108135..95b2d5909 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -394,8 +394,26 @@ public ClasspathEntryWorkUnit(final ClasspathElementAndClassLoader rawClasspathE @Override public ClasspathElement newInstance(final ClasspathElementAndClassLoader classpathEntry, final LogNode log) throws IOException, InterruptedException { - final Object classpathEntryObj = classpathEntry.classpathElement; - String classpathEntryPath; + Object classpathEntryObj = classpathEntry.classpathElementRoot; + String dirOrPathPackageRoot = classpathEntry.dirOrPathPackageRoot; + while (dirOrPathPackageRoot.startsWith("/")) { + dirOrPathPackageRoot = dirOrPathPackageRoot.substring(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 (JarUtils.URL_SCHEME_PATTERN.matcher(classpathEntryStr).matches()) { + try { + classpathEntryObj = new URL(classpathEntryStr); + } catch (final MalformedURLException e) { + throw new IOException("Malformed URL: " + classpathEntryStr); + } + } + } + + // Check type of classpath entry object + String classpathEntryPathStr; if (classpathEntryObj instanceof URL) { URL classpathEntryURL = (URL) classpathEntryObj; String scheme = classpathEntryURL.getProtocol(); @@ -412,7 +430,8 @@ public ClasspathElement newInstance(final ClasspathElementAndClassLoader classpa if ("file".equals(scheme)) { // Extract file path, and use below as a path string to determine if this // classpath element is a file (jar) or directory - classpathEntryPath = URLDecoder.decode(classpathEntryURL.getPath(), "UTF-8"); + classpathEntryPathStr = URLDecoder.decode(classpathEntryURL.getPath(), "UTF-8"); + // Fall through } else if ("http".equals(scheme) || "https".equals(scheme)) { // Jar URL or URI (remote URLs/URIs must be jars) return new ClasspathElementZip(classpathEntryURL, classpathEntry.classLoader, @@ -423,14 +442,14 @@ public ClasspathElement newInstance(final ClasspathElementAndClassLoader classpa final Path path = Paths.get(classpathEntryURL.toURI()); if (Files.isDirectory(path)) { // If URL maps to a directory, use the NIO Path API instead of the File API - return new ClasspathElementPathDir(path, classpathEntry.classLoader, - nestedJarHandler, scanSpec); + return new ClasspathElementPathDir(path, dirOrPathPackageRoot, + classpathEntry.classLoader, nestedJarHandler, scanSpec); } } catch (final URISyntaxException | IllegalArgumentException | SecurityException e) { throw new IOException( "Cannot handle URL " + classpathEntryURL + " : " + e.getMessage()); } catch (final FileSystemNotFoundException e) { - // Fall through to below -- this is a custom URL scheme without a backing FileSystem + // This is a custom URL scheme without a backing FileSystem } // For custom URL schemes that do not resolve to directories, assume the URL @@ -439,25 +458,29 @@ public ClasspathElement newInstance(final ClasspathElementAndClassLoader classpa nestedJarHandler, scanSpec); } } else if (classpathEntryObj instanceof Path) { - // classpathEntryObj is a Path (which points to a lib/ext jar inside a parent Path -- - // see ClasspathElementPath.java) - return new ClasspathElementZip(((Path) classpathEntryObj).toUri(), - classpathEntry.classLoader, nestedJarHandler, scanSpec); + final Path classpathEntryPath = (Path) classpathEntryObj; + 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.toUri(), classpathEntry.classLoader, + nestedJarHandler, scanSpec); + } else if (FileUtils.canReadAndIsDir(packageRootPath)) { + // classpathEntryObj is a Path which points to a dir -- need to scan it recursively + return new ClasspathElementPathDir((Path) classpathEntryObj, dirOrPathPackageRoot, + classpathEntry.classLoader, nestedJarHandler, scanSpec); + } else { + throw new IOException("Path is not a directory or file: " + classpathEntryPath); + } } else { // classpathEntryObj is a string - classpathEntryPath = classpathEntryObj.toString(); + classpathEntryPathStr = classpathEntryObj.toString(); } + // Fall through for file paths // Normalize path -- strip off any leading "jar:" / "file:", and normalize separators final String pathNormalized = FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, - classpathEntryPath); - - // "http:", "https:" or any other URL/URI scheme must indicate a jar - if (JarUtils.URL_SCHEME_PATTERN.matcher(pathNormalized).matches()) { - return new ClasspathElementZip(classpathEntryPath, classpathEntry.classLoader, - nestedJarHandler, scanSpec); - } + classpathEntryPathStr); // Strip everything after first "!", to get path of base jarfile or dir final int plingIdx = pathNormalized.indexOf('!'); @@ -472,7 +495,7 @@ public ClasspathElement newInstance(final ClasspathElementAndClassLoader classpa if (!FileUtils.canRead(fileCanonicalized)) { throw new IOException("Cannot read file or directory"); } - boolean isJar = classpathEntryPath.regionMatches(true, 0, "jar:", 0, 4) || plingIdx > 0; + boolean isJar = classpathEntryPathStr.regionMatches(true, 0, "jar:", 0, 4) || plingIdx > 0; if (fileCanonicalized.isFile()) { // If a file, must be a jar isJar = true; @@ -495,7 +518,7 @@ public ClasspathElement newInstance(final ClasspathElementAndClassLoader classpa // idempotent) try { return this.get(new ClasspathElementAndClassLoader(canonicalPathNormalized, - classpathEntry.classLoader), log); + dirOrPathPackageRoot, classpathEntry.classLoader), log); } catch (final NullSingletonException e) { throw new IOException("Cannot get classpath element for canonical path " + canonicalPathNormalized + " : " + e); @@ -507,8 +530,8 @@ public ClasspathElement newInstance(final ClasspathElementAndClassLoader classpa return isJar ? new ClasspathElementZip(canonicalPathNormalized, classpathEntry.classLoader, nestedJarHandler, scanSpec) - : new ClasspathElementFileDir(fileCanonicalized, classpathEntry.classLoader, - nestedJarHandler, scanSpec); + : new ClasspathElementFileDir(fileCanonicalized, dirOrPathPackageRoot, + classpathEntry.classLoader, nestedJarHandler, scanSpec); } } }; @@ -547,12 +570,15 @@ public void processWorkUnit(final ClasspathEntryWorkUnit workUnit, // 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)) { + 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, log); + 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. @@ -570,7 +596,10 @@ public void processWorkUnit(final ClasspathEntryWorkUnit workUnit, } } catch (final IOException | SecurityException e) { if (log != null) { - log.log("Skipping invalid classpath element " + workUnit.rawClasspathEntry.classpathElement + log.log("Skipping invalid classpath element " + + workUnit.rawClasspathEntry.classpathElementRoot + + (workUnit.rawClasspathEntry.dirOrPathPackageRoot.isEmpty() ? "" + : "/" + workUnit.rawClasspathEntry.dirOrPathPackageRoot) + " : " + e); } } 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 f9e88278b..fab2aa997 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java @@ -33,6 +33,7 @@ import java.net.MalformedURLException; import java.net.URI; import java.net.URL; +import java.nio.file.Path; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -61,28 +62,50 @@ public class ClasspathOrder { * A classpath element and the {@link ClassLoader} it was obtained from. */ public static class ClasspathElementAndClassLoader { - /** The classpath element (a {@link String} or {@link URL}). */ - public final Object classpathElement; + /** The classpath element root (a {@link String} or {@link URL} or {@link Path}). */ + public final Object classpathElementRoot; + + /** The classpath element package root, prefix, e.g. "BOOT-INF/classes" or "". */ + public final String dirOrPathPackageRoot; /** The classloader the classpath element was obtained from. */ public final ClassLoader classLoader; + /** + * Constructor for directory or {@link Path} classpath entries. + * + * @param classpathElementRoot + * the classpath element root (a {@link String} or {@link URL} or {@link Path}). + * @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, + final ClassLoader classLoader) { + this.classpathElementRoot = classpathElementRoot; + this.dirOrPathPackageRoot = dirOrPathPackageRoot; + this.classLoader = classLoader; + } + /** * Constructor. * - * @param classpathElement - * the classpath element (a {@link String} or {@link URL}). + * @param classpathElementRoot + * the classpath element root (a {@link String} or {@link URL} or {@link Path}). * @param classLoader * the classloader the classpath element was obtained from. */ - public ClasspathElementAndClassLoader(final Object classpathElement, final ClassLoader classLoader) { - this.classpathElement = classpathElement; + public ClasspathElementAndClassLoader(final Object classpathElementRoot, final ClassLoader classLoader) { + this.classpathElementRoot = classpathElementRoot; + this.dirOrPathPackageRoot = ""; this.classLoader = classLoader; } @Override public int hashCode() { - return Objects.hash(classpathElement, classLoader); + return Objects.hash(classpathElementRoot, dirOrPathPackageRoot, classLoader); } @Override @@ -93,7 +116,8 @@ public boolean equals(final Object obj) { return false; } final ClasspathElementAndClassLoader other = (ClasspathElementAndClassLoader) obj; - return Objects.equals(this.classpathElement, other.classpathElement) + return Objects.equals(this.dirOrPathPackageRoot, other.dirOrPathPackageRoot) + && Objects.equals(this.classpathElementRoot, other.classpathElementRoot) && Objects.equals(this.classLoader, other.classLoader); } } 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 21de9312f..30c0b90ba 100644 --- a/src/test/java/io/github/classgraph/issues/issue420/Issue420Test.java +++ b/src/test/java/io/github/classgraph/issues/issue420/Issue420Test.java @@ -41,7 +41,6 @@ import org.junit.jupiter.api.Test; -import com.google.common.jimfs.Configuration; import com.google.common.jimfs.Jimfs; import io.github.classgraph.ClassGraph; @@ -52,7 +51,7 @@ */ public class Issue420Test { /** - * Test. + * Test accessing a jar over Jimfs. * * @throws IOException * If an I/O exception occurred. @@ -81,29 +80,29 @@ public void testScanningFileBackedByFileSystem() throws IOException, URISyntaxEx } /** - * Test. + * Test accessing a package hierarchy in Jimfs. * + * @param packageRootPrefix + * The package root prefix. * @throws IOException * If an I/O exception occurred. * @throws URISyntaxException * If a URI is bad. */ - @Test - public void testScanningDirBackedByFileSystem() throws IOException, URISyntaxException { - try (FileSystem memFs = Jimfs - // Change working directory from the default of "/work" to "/" - .newFileSystem(Configuration.unix().toBuilder().setWorkingDirectory("/").build())) { + private void testDir(final String packageRootPrefix) throws IOException, URISyntaxException { + try (FileSystem memFs = Jimfs.newFileSystem()) { final String packageName = "io.github.classgraph.issues.issue146"; + final String className = "CompiledWithJDK8"; final String packagePath = packageName.replace('.', '/'); - final String className = packageName + ".CompiledWithJDK8"; - final String classFilePath = className.replace('.', '/') + ".class"; + final String classFullyQualifiedName = packageName + ".CompiledWithJDK8"; + final String classFilePath = classFullyQualifiedName.replace('.', '/') + ".class"; final Path jarPath = Paths.get(getClass().getClassLoader().getResource(classFilePath).toURI()); - final Path memFsDirPath = memFs.getPath(packagePath); + final Path memFsDirPath = memFs.getPath(packageRootPrefix + packagePath); Files.createDirectories(memFsDirPath); - final Path memFsFilePath = memFs.getPath(classFilePath); + final Path memFsFilePath = memFs.getPath(memFsDirPath + "/" + className + ".class"); final Path memFsCopyOfClassFile = Files.copy(jarPath, memFsFilePath); assertThat(Files.exists(memFsCopyOfClassFile)); - final Path memFsRoot = memFs.getPath("/"); + final Path memFsRoot = memFs.getPath(""); final URL memFsRootURL = memFsRoot.toUri().toURL(); try (URLClassLoader childClassLoader = new URLClassLoader(new URL[] { memFsRootURL }, getClass().getClassLoader())) { @@ -111,9 +110,36 @@ public void testScanningDirBackedByFileSystem() throws IOException, URISyntaxExc .overrideClassLoaders(childClassLoader).ignoreParentClassLoaders() .whitelistPackages(packageName).enableAllInfo(); try (ScanResult scanResult = classGraph.scan()) { - assertThat(scanResult.getClassInfo(className)).isNotNull(); + assertThat(scanResult.getClassInfo(classFullyQualifiedName)).isNotNull(); } } } } + + /** + * Test accessing a package hierarchy rooted at the default dir of "work/" in Jimfs. + * + * @throws IOException + * If an I/O exception occurred. + * @throws URISyntaxException + * If a URI is bad. + */ + @Test + public void testScanningDirBackedByFileSystem() throws IOException, URISyntaxException { + testDir(""); + } + + /** + * Test accessing a package hierarchy rooted at "work/classes/" (i.e. with an automatically-detected package + * root) in Jimfs. + * + * @throws IOException + * If an I/O exception occurred. + * @throws URISyntaxException + * If a URI is bad. + */ + @Test + public void testScanningDirBackedByFileSystemWithPackageRoot() throws IOException, URISyntaxException { + testDir("classes/"); + } } From 26c3d77f9378b8186787223b4809e236ee10919d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 26 Apr 2020 17:39:38 -0600 Subject: [PATCH 0779/1778] [maven-release-plugin] prepare release classgraph-4.8.76 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 174d26008..7bea1bc7e 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.76-SNAPSHOT + 4.8.76 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.74 + classgraph-4.8.76 From be05dcf59e90de9bf99a222a247ee38dc775dc7a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 26 Apr 2020 17:39:45 -0600 Subject: [PATCH 0780/1778] [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 7bea1bc7e..672101373 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.76 + 4.8.77-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.76 + classgraph-4.8.74 From 209a9766829acfc39db3d3b043b16920dc2f509b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 26 Apr 2020 18:57:30 -0600 Subject: [PATCH 0781/1778] Improve logging --- .../io/github/classgraph/ClasspathElementFileDir.java | 9 +++++---- .../io/github/classgraph/ClasspathElementPathDir.java | 10 +++++----- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementFileDir.java b/src/main/java/io/github/classgraph/ClasspathElementFileDir.java index 0d7839216..ee3d6d0fc 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementFileDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementFileDir.java @@ -378,6 +378,11 @@ private void scanDirRecursively(final File dir, final LogNode log) { return; } + final LogNode subLog = log == null ? null + // Log dirs after files (addWhitelistedResources() 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) { @@ -386,10 +391,6 @@ private void scanDirRecursively(final File dir, final LogNode log) { return; } Arrays.sort(filesInDir); - final LogNode subLog = log == null ? null - // Log dirs after files (addWhitelistedResources() precedes log entry with "0:") - : log.log("1:" + canonicalPath, "Scanning directory: " + dir - + (dir.getPath().equals(canonicalPath) ? "" : " ; canonical path: " + canonicalPath)); // Determine whether this is a modular jar running under JRE 9+ final boolean isModularJar = VersionFinder.JAVA_MAJOR_VERSION >= 9 && getModuleName() != null; diff --git a/src/main/java/io/github/classgraph/ClasspathElementPathDir.java b/src/main/java/io/github/classgraph/ClasspathElementPathDir.java index d36da3c2a..a41d9ada6 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementPathDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementPathDir.java @@ -391,6 +391,11 @@ private void scanPathRecursively(final Path path, final LogNode log) { return; } + final LogNode subLog = log == null ? null + // Log dirs after files (addWhitelistedResources() precedes log entry with "0:") + : log.log("1:" + canonicalPath, "Scanning Path: " + path + + (path.equals(canonicalPath) ? "" : " ; canonical path: " + canonicalPath)); + final List pathsInDir = new ArrayList<>(); try (DirectoryStream stream = Files.newDirectoryStream(path)) { for (final Path subPath : stream) { @@ -405,11 +410,6 @@ private void scanPathRecursively(final Path path, final LogNode log) { } Collections.sort(pathsInDir); - final LogNode subLog = log == null ? null - // Log dirs after files (addWhitelistedResources() precedes log entry with "0:") - : log.log("1:" + canonicalPath, "Scanning Path: " + path - + (path.equals(canonicalPath) ? "" : " ; canonical path: " + canonicalPath)); - // Determine whether this is a modular jar running under JRE 9+ final boolean isModularJar = VersionFinder.JAVA_MAJOR_VERSION >= 9 && getModuleName() != null; From 7dde56a49630b053a18e61acd34962448b0ae5f0 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 26 Apr 2020 19:08:05 -0600 Subject: [PATCH 0782/1778] Windows compatibility fixes (#420) --- .../github/classgraph/ClasspathElementPathDir.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementPathDir.java b/src/main/java/io/github/classgraph/ClasspathElementPathDir.java index a41d9ada6..60f7ed109 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementPathDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementPathDir.java @@ -57,6 +57,7 @@ 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; @@ -184,7 +185,7 @@ private Resource newResource(final Path resourcePath, final NestedJarHandler nes @Override public String getPath() { - String path = packageRootPath.relativize(resourcePath).toString(); + String path = FastPathResolver.resolve(packageRootPath.relativize(resourcePath).toString()); while (path.startsWith("/")) { path = path.substring(1); } @@ -193,7 +194,7 @@ public String getPath() { @Override public String getPathRelativeToClasspathElement() { - String path = classpathEltPath.relativize(resourcePath).toString(); + String path = FastPathResolver.resolve(classpathEltPath.relativize(resourcePath).toString()); while (path.startsWith("/")) { path = path.substring(1); } @@ -343,8 +344,7 @@ private void scanPathRecursively(final Path path, final LogNode log) { return; } - final Path dirRelativePath = packageRootPath.relativize(path); - String dirRelativePathStr = dirRelativePath.toString(); + String dirRelativePathStr = FastPathResolver.resolve(packageRootPath.relativize(path).toString()); while (dirRelativePathStr.startsWith("/")) { dirRelativePathStr = dirRelativePathStr.substring(1); } @@ -393,8 +393,10 @@ private void scanPathRecursively(final Path path, final LogNode log) { final LogNode subLog = log == null ? null // Log dirs after files (addWhitelistedResources() precedes log entry with "0:") - : log.log("1:" + canonicalPath, "Scanning Path: " + path - + (path.equals(canonicalPath) ? "" : " ; canonical path: " + canonicalPath)); + : log.log("1:" + canonicalPath, + "Scanning Path: " + FastPathResolver.resolve(path.toString()) + (path.equals(canonicalPath) + ? "" + : " ; canonical path: " + FastPathResolver.resolve(canonicalPath.toString()))); final List pathsInDir = new ArrayList<>(); try (DirectoryStream stream = Files.newDirectoryStream(path)) { From b0f348014677966feb0ba30179516e9e6b420091 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 26 Apr 2020 19:16:11 -0600 Subject: [PATCH 0783/1778] Add serialVersionUID --- src/main/java/io/github/classgraph/AnnotationInfoList.java | 3 +++ .../io/github/classgraph/AnnotationParameterValueList.java | 2 ++ src/main/java/io/github/classgraph/ClassInfoList.java | 3 +++ src/main/java/io/github/classgraph/FieldInfoList.java | 2 ++ src/main/java/io/github/classgraph/MappableInfoList.java | 3 +++ src/main/java/io/github/classgraph/MethodInfoList.java | 3 +++ src/main/java/io/github/classgraph/ModuleInfoList.java | 3 +++ src/main/java/io/github/classgraph/PackageInfoList.java | 6 ++++++ 8 files changed, 25 insertions(+) diff --git a/src/main/java/io/github/classgraph/AnnotationInfoList.java b/src/main/java/io/github/classgraph/AnnotationInfoList.java index dabe39cae..28f6093a7 100644 --- a/src/main/java/io/github/classgraph/AnnotationInfoList.java +++ b/src/main/java/io/github/classgraph/AnnotationInfoList.java @@ -47,6 +47,9 @@ public class AnnotationInfoList extends MappableInfoList { */ private AnnotationInfoList directlyRelatedAnnotations; + /** serialVersionUID */ + private static final long serialVersionUID = 1L; + /** An unmodifiable empty {@link AnnotationInfoList}. */ static final AnnotationInfoList EMPTY_LIST = new AnnotationInfoList(); static { diff --git a/src/main/java/io/github/classgraph/AnnotationParameterValueList.java b/src/main/java/io/github/classgraph/AnnotationParameterValueList.java index 93414cc4d..ac67629cf 100644 --- a/src/main/java/io/github/classgraph/AnnotationParameterValueList.java +++ b/src/main/java/io/github/classgraph/AnnotationParameterValueList.java @@ -34,6 +34,8 @@ /** A list of {@link AnnotationParameterValue} objects. */ public class AnnotationParameterValueList extends MappableInfoList { + /** serialVersionUID */ + private static final long serialVersionUID = 1L; /** An unmodifiable empty {@link AnnotationParameterValueList}. */ static final AnnotationParameterValueList EMPTY_LIST = new AnnotationParameterValueList(); diff --git a/src/main/java/io/github/classgraph/ClassInfoList.java b/src/main/java/io/github/classgraph/ClassInfoList.java index 1293436cb..d9f88d990 100644 --- a/src/main/java/io/github/classgraph/ClassInfoList.java +++ b/src/main/java/io/github/classgraph/ClassInfoList.java @@ -67,6 +67,9 @@ public class ClassInfoList extends MappableInfoList { /** Whether to sort by name. */ private final boolean sortByName; + /** serialVersionUID */ + private static final long serialVersionUID = 1L; + /** An unmodifiable empty {@link ClassInfoList}. */ static final ClassInfoList EMPTY_LIST = new ClassInfoList(); static { diff --git a/src/main/java/io/github/classgraph/FieldInfoList.java b/src/main/java/io/github/classgraph/FieldInfoList.java index 821116136..2c100aa0c 100644 --- a/src/main/java/io/github/classgraph/FieldInfoList.java +++ b/src/main/java/io/github/classgraph/FieldInfoList.java @@ -34,6 +34,8 @@ /** A list of {@link FieldInfo} objects. */ public class FieldInfoList extends MappableInfoList { + /** serialVersionUID */ + private static final long serialVersionUID = 1L; /** An unmodifiable empty {@link FieldInfoList}. */ static final FieldInfoList EMPTY_LIST = new FieldInfoList(); diff --git a/src/main/java/io/github/classgraph/MappableInfoList.java b/src/main/java/io/github/classgraph/MappableInfoList.java index 89a52ab70..50811f988 100644 --- a/src/main/java/io/github/classgraph/MappableInfoList.java +++ b/src/main/java/io/github/classgraph/MappableInfoList.java @@ -39,6 +39,9 @@ * the element type */ public class MappableInfoList extends InfoList { + /** serialVersionUID */ + private static final long serialVersionUID = 1L; + /** * Constructor. */ diff --git a/src/main/java/io/github/classgraph/MethodInfoList.java b/src/main/java/io/github/classgraph/MethodInfoList.java index 40d489c5c..8d0a12f09 100644 --- a/src/main/java/io/github/classgraph/MethodInfoList.java +++ b/src/main/java/io/github/classgraph/MethodInfoList.java @@ -35,6 +35,9 @@ /** A list of {@link MethodInfo} objects. */ public class MethodInfoList extends InfoList { + /** serialVersionUID */ + private static final long serialVersionUID = 1L; + /** An unmodifiable empty {@link MethodInfoList}. */ static final MethodInfoList EMPTY_LIST = new MethodInfoList(); static { diff --git a/src/main/java/io/github/classgraph/ModuleInfoList.java b/src/main/java/io/github/classgraph/ModuleInfoList.java index 234aff4ae..475ea152b 100644 --- a/src/main/java/io/github/classgraph/ModuleInfoList.java +++ b/src/main/java/io/github/classgraph/ModuleInfoList.java @@ -32,6 +32,9 @@ /** A list of {@link ModuleInfo} objects. */ public class ModuleInfoList extends MappableInfoList { + /** serialVersionUID */ + private static final long serialVersionUID = 1L; + /** * Constructor. */ diff --git a/src/main/java/io/github/classgraph/PackageInfoList.java b/src/main/java/io/github/classgraph/PackageInfoList.java index afde28467..f5811270c 100644 --- a/src/main/java/io/github/classgraph/PackageInfoList.java +++ b/src/main/java/io/github/classgraph/PackageInfoList.java @@ -32,6 +32,9 @@ /** A list of {@link PackageInfo} objects. */ public class PackageInfoList extends MappableInfoList { + /** serialVersionUID */ + private static final long serialVersionUID = 1L; + /** * Constructor. */ @@ -61,6 +64,9 @@ public class PackageInfoList extends MappableInfoList { /** An unmodifiable {@link PackageInfoList}. */ static final PackageInfoList EMPTY_LIST = new PackageInfoList() { + /** serialVersionUID */ + private static final long serialVersionUID = 1L; + @Override public boolean add(final PackageInfo e) { throw new IllegalArgumentException("List is immutable"); From ade0a014abac8ecbb7c8a212eb2076162b89b566 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 26 Apr 2020 19:17:05 -0600 Subject: [PATCH 0784/1778] [maven-release-plugin] prepare release classgraph-4.8.77 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 672101373..28e60e69a 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.77-SNAPSHOT + 4.8.77 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.74 + classgraph-4.8.77 From 29fccf695c50d70b0eb96941a184887e7c28b689 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 26 Apr 2020 19:17:12 -0600 Subject: [PATCH 0785/1778] [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 28e60e69a..46e5b43e2 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.77 + 4.8.78-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.77 + classgraph-4.8.74 From e7c978e75057b2e417a70fc935ae69a2fa6ffd0e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 26 Apr 2020 19:19:47 -0600 Subject: [PATCH 0786/1778] Windows compatibility fixes --- .../io/github/classgraph/ClasspathElementFileDir.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementFileDir.java b/src/main/java/io/github/classgraph/ClasspathElementFileDir.java index ee3d6d0fc..32824ca58 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementFileDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementFileDir.java @@ -52,6 +52,7 @@ 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; @@ -178,15 +179,19 @@ private Resource newResource(final String pathRelativeToPackageRoot, final File @Override public String getPath() { - return pathRelativeToPackageRoot; + String path = FastPathResolver.resolve(pathRelativeToPackageRoot); + while (path.startsWith("/")) { + path = path.substring(1); + } + return path; } @Override public String getPathRelativeToClasspathElement() { // Relativize resource file to classpath element dir final File resourceFile = new File(packageRootDir, pathRelativeToPackageRoot); - String pathRelativeToClasspathElt = resourceFile.getPath() - .substring(classpathEltDir.getPath().length()); + String pathRelativeToClasspathElt = FastPathResolver + .resolve(resourceFile.getPath().substring(classpathEltDir.getPath().length())); while (pathRelativeToClasspathElt.startsWith("/")) { pathRelativeToClasspathElt = pathRelativeToClasspathElt.substring(1); } From e346bf26168d68def29d33b9692df9211d57ba6e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 26 Apr 2020 19:30:11 -0600 Subject: [PATCH 0787/1778] Drop version number back down --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 46e5b43e2..672101373 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.78-SNAPSHOT + 4.8.77-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From ffa38a17bf329e9024072a720daafdbcc8595be9 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 26 Apr 2020 19:30:45 -0600 Subject: [PATCH 0788/1778] [maven-release-plugin] prepare release classgraph-4.8.77 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 672101373..28e60e69a 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.77-SNAPSHOT + 4.8.77 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.74 + classgraph-4.8.77 From 10419b329ae1ecd7fe2d5414588b27aff3087f39 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 26 Apr 2020 19:30:53 -0600 Subject: [PATCH 0789/1778] [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 28e60e69a..46e5b43e2 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.77 + 4.8.78-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.77 + classgraph-4.8.74 From 6200bc33a2aeb64857d9ca9441b8fd7176e766f8 Mon Sep 17 00:00:00 2001 From: gus Date: Thu, 30 Apr 2020 09:42:07 -0400 Subject: [PATCH 0790/1778] Don't read through and store classpath elements if they won't be used. Fixes #421 --- .../github/classgraph/classpath/ClasspathFinder.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 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 bc24ff718..4f60ff3f6 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java @@ -243,8 +243,10 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { : classloaderURLLog .log("Ignoring parent classloader " + classLoader + ", normally handled by " + classLoaderHandlerRegistryEntry.classLoaderHandlerClass.getName()); - classLoaderHandlerRegistryEntry.findClasspathOrder(classLoader, ignoredClasspathOrder, scanSpec, + if (willReadJavaClassPath(scanSpec)) { + classLoaderHandlerRegistryEntry.findClasspathOrder(classLoader, ignoredClasspathOrder, scanSpec, classloaderHandlerLog); + } } else { // Otherwise add classpath entries to classpathOrder, and add the classloader to the // final classloader ordering @@ -267,8 +269,7 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { // overrideClassLoaders is true or overrideClasspath is set). However, if only module scanning was // enabled, and an unnamed module layer was encountered, have to forcibly scan java.class.path, // since the ModuleLayer API doesn't allow you to open unnamed modules. - if ((scanSpec.overrideClassLoaders == null && scanSpec.overrideClasspath == null) - || (moduleFinder != null && moduleFinder.forceScanJavaClassPath())) { + if (willReadJavaClassPath(scanSpec)) { final String[] pathElements = JarUtils.smartPathSplit(System.getProperty("java.class.path"), scanSpec); if (pathElements.length > 0) { final LogNode sysPropLog = classpathFinderLog == null ? null @@ -291,4 +292,9 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { } } } + + private boolean willReadJavaClassPath(ScanSpec scanSpec) { + return (scanSpec.overrideClassLoaders == null && scanSpec.overrideClasspath == null) + || (moduleFinder != null && moduleFinder.forceScanJavaClassPath()); + } } From 725f720eb6c0959011d8f6cc5e1debc0716b43be Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 30 Apr 2020 15:29:11 -0600 Subject: [PATCH 0791/1778] Fix typo in log message (#423) --- .../github/classgraph/fastzipfilereader/NestedJarHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 cf98273a0..a5da6d2ef 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java @@ -115,7 +115,7 @@ public ZipFileSlice newInstance(final FastZipEntry childZipEntry, final LogNode // the contents of the entry before its central directory can be read (most of // the time nested zipfiles are stored, not deflated, so this should be rare) if (log != null) { - log.log("Deflating nested zip entry: " + childZipEntry + " ; uncompressed size: " + log.log("Inflating nested zip entry: " + childZipEntry + " ; uncompressed size: " + childZipEntry.uncompressedSize); } From 0b732584857bc7dc5413e1e2289f36eb8d0bbdde Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 30 Apr 2020 15:45:30 -0600 Subject: [PATCH 0792/1778] Tweaks to previous commit (#422) --- .../classgraph/classpath/ClasspathFinder.java | 52 ++++++------------- 1 file changed, 17 insertions(+), 35 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 4f60ff3f6..30607045f 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java @@ -140,7 +140,6 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { : null; classpathOrder = new ClasspathOrder(scanSpec); - final ClasspathOrder ignoredClasspathOrder = new ClasspathOrder(scanSpec); // Only look for environment classloaders if classpath and classloaders are not overridden final ClassLoaderFinder classLoaderFinder = scanSpec.overrideClasspath == null @@ -236,18 +235,7 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { final ClassLoader classLoader = ent.getKey(); final ClassLoaderHandlerRegistryEntry classLoaderHandlerRegistryEntry = ent.getValue(); // Add classpath entries to ignoredClasspathOrder or classpathOrder - if (scanSpec.ignoreParentClassLoaders && allParentClassLoaders.contains(classLoader)) { - // If this is a parent and parent classloaders are being ignored, add classpath entries - // to ignoredClasspathOrder - final LogNode classloaderHandlerLog = classloaderURLLog == null ? null - : classloaderURLLog - .log("Ignoring parent classloader " + classLoader + ", normally handled by " - + classLoaderHandlerRegistryEntry.classLoaderHandlerClass.getName()); - if (willReadJavaClassPath(scanSpec)) { - classLoaderHandlerRegistryEntry.findClasspathOrder(classLoader, ignoredClasspathOrder, scanSpec, - classloaderHandlerLog); - } - } else { + 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 @@ -256,6 +244,13 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { classLoaderHandlerRegistryEntry.findClasspathOrder(classLoader, classpathOrder, scanSpec, classloaderHandlerLog); finalClassLoaderOrder.add(classLoader); + } else { + // If this is a parent and parent classloaders are being ignored, add classpath entries + // to ignoredClasspathOrder + final LogNode classloaderHandlerLog = classloaderURLLog == null ? null + : classloaderURLLog + .log("Ignoring parent classloader " + classLoader + ", normally handled by " + + classLoaderHandlerRegistryEntry.classLoaderHandlerClass.getName()); } } @@ -264,12 +259,13 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { classLoaderOrderRespectingParentDelegation = finalClassLoaderOrder.toArray(new ClassLoader[0]); } - // Get classpath elements from java.class.path, but don't add them if the element is in an ignored - // parent classloader and not in a child classloader (and don't use java.class.path at all if - // overrideClassLoaders is true or overrideClasspath is set). However, if only module scanning was - // enabled, and an unnamed module layer was encountered, have to forcibly scan java.class.path, - // since the ModuleLayer API doesn't allow you to open unnamed modules. - if (willReadJavaClassPath(scanSpec)) { + // 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())) { final String[] pathElements = JarUtils.smartPathSplit(System.getProperty("java.class.path"), scanSpec); if (pathElements.length > 0) { final LogNode sysPropLog = classpathFinderLog == null ? null @@ -277,24 +273,10 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { for (final String pathElement : pathElements) { final String pathElementResolved = FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, pathElement); - if (!ignoredClasspathOrder.getClasspathEntryUniqueResolvedPaths() - .contains(pathElementResolved)) { - // pathElement is not also listed in an ignored parent classloader - classpathOrder.addClasspathEntry(pathElement, defaultClassLoader, scanSpec, sysPropLog); - } else { - // pathElement is also listed in an ignored parent classloader, ignore it (Issue #169) - if (sysPropLog != null) { - sysPropLog.log("Found classpath element in java.class.path that will be ignored, " - + "since it is also found in an ignored parent classloader: " + pathElement); - } - } + // pathElement is not also listed in an ignored parent classloader + classpathOrder.addClasspathEntry(pathElement, defaultClassLoader, scanSpec, sysPropLog); } } } } - - private boolean willReadJavaClassPath(ScanSpec scanSpec) { - return (scanSpec.overrideClassLoaders == null && scanSpec.overrideClasspath == null) - || (moduleFinder != null && moduleFinder.forceScanJavaClassPath()); - } } From 86a74c88ca655ad499e407ec95d5533318b793c4 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 30 Apr 2020 16:31:56 -0600 Subject: [PATCH 0793/1778] Improve logging --- .../io/github/classgraph/fastzipfilereader/ZipFileSlice.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSlice.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSlice.java index aa6c65bac..282b3dce7 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSlice.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSlice.java @@ -222,7 +222,7 @@ public String toString() { if (fileStr == null) { fileStr = physicalZipFile.getFile() == null ? null : physicalZipFile.getFile().toString(); } - return "[" + (fileStr == null || !fileStr.equals(path) ? path + " -> " + fileStr : path) + " ; byte range: " + return "[" + (fileStr != null && !fileStr.equals(path) ? path + " -> " + fileStr : path) + " ; byte range: " + slice.sliceStartPos + ".." + (slice.sliceStartPos + slice.sliceLength) + " / " + physicalZipFile.length() + "]"; } From 349a1c3f6a05a19d5e56f17464bb1de15d08b97c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 30 Apr 2020 17:14:23 -0600 Subject: [PATCH 0794/1778] Code cleanups --- .../classgraph/classpath/ClasspathFinder.java | 14 +++++--------- 1 file changed, 5 insertions(+), 9 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 30607045f..7ac885dd7 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java @@ -244,13 +244,9 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { classLoaderHandlerRegistryEntry.findClasspathOrder(classLoader, classpathOrder, scanSpec, classloaderHandlerLog); finalClassLoaderOrder.add(classLoader); - } else { - // If this is a parent and parent classloaders are being ignored, add classpath entries - // to ignoredClasspathOrder - final LogNode classloaderHandlerLog = classloaderURLLog == null ? null - : classloaderURLLog - .log("Ignoring parent classloader " + classLoader + ", normally handled by " - + classLoaderHandlerRegistryEntry.classLoaderHandlerClass.getName()); + } else if (classloaderURLLog != null) { + classloaderURLLog.log("Ignoring parent classloader " + classLoader + ", normally handled by " + + classLoaderHandlerRegistryEntry.classLoaderHandlerClass.getName()); } } @@ -271,10 +267,10 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { final LogNode sysPropLog = classpathFinderLog == null ? null : 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, pathElement); - // pathElement is not also listed in an ignored parent classloader - classpathOrder.addClasspathEntry(pathElement, defaultClassLoader, scanSpec, sysPropLog); + classpathOrder.addClasspathEntry(pathElementResolved, defaultClassLoader, scanSpec, sysPropLog); } } } From 3feaed1ab110b6e60b22b6a3822e13297aa83026 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 30 Apr 2020 17:19:03 -0600 Subject: [PATCH 0795/1778] Prevent IndexOutOfBoundsException (#423) --- .../io/github/classgraph/fastzipfilereader/LogicalZipFile.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 2ebf001f1..25a3cdf5d 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java @@ -447,7 +447,7 @@ private void readCentralDirectory(final NestedJarHandler nestedJarHandler, final // so need to scan back that far to determine if this is a valid zipfile. However for speed, // initially just try reading back a maximum of 32 characters. long eocdPos = -1; - for (long i = slice.sliceLength - 22, iMin = slice.sliceLength - 22 - 32; i >= iMin; --i) { + for (long i = slice.sliceLength - 22, iMin = slice.sliceLength - 22 - 32; i >= iMin && i >= 0L; --i) { if (reader.readInt(i) == 0x06054b50) { eocdPos = i; break; From 7be54be3240cb58cfb84fde2905ca7234d767e5d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 30 Apr 2020 17:23:04 -0600 Subject: [PATCH 0796/1778] Optimization for small files that do not have a central directory marker --- .../io/github/classgraph/fastzipfilereader/LogicalZipFile.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 25a3cdf5d..c8946171f 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java @@ -453,7 +453,7 @@ private void readCentralDirectory(final NestedJarHandler nestedJarHandler, final break; } } - if (eocdPos < 0) { + if (eocdPos < 0 && slice.sliceLength > 22 + 32) { // If EOCD signature was not found, read the last 64kB of file to RAM in a single chunk // so that we can scan back through it at higher speed to locate the EOCD signature final int bytesToRead = (int) Math.min(slice.sliceLength, 22L + (1 << 16)); From fd62a8386db68edf40fd04494057c48270eed112 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 30 Apr 2020 20:58:11 -0600 Subject: [PATCH 0797/1778] Add "main/" to AUTOMATIC_PACKAGE_ROOT_PREFIXES (#423) --- .../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 08272a75d..2f193974b 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 @@ -105,7 +106,9 @@ public class ClassLoaderHandlerRegistry { // Spring-Boot "BOOT-INF/classes/", // Tomcat - "WEB-INF/classes/" // + "WEB-INF/classes/", + // UnoJar + "main/" // }; // ------------------------------------------------------------------------------------------------------------- From 8b619326ed9d4697ff8a4625a1fc5f796d6ff41c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 30 Apr 2020 21:55:57 -0600 Subject: [PATCH 0798/1778] Add ClassLoaderHandler for UnoJar / One-Jar (#423) --- .../ClassLoaderHandlerRegistry.java | 3 +- .../UnoOneJarClassLoaderHandler.java | 111 ++++++++++++++++++ 2 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 src/main/java/nonapi/io/github/classgraph/classloaderhandler/UnoOneJarClassLoaderHandler.java 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 2f193974b..dbf722c97 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java @@ -60,6 +60,7 @@ public class ClassLoaderHandlerRegistry { new ClassLoaderHandlerRegistryEntry(TomcatWebappClassLoaderBaseHandler.class), new ClassLoaderHandlerRegistryEntry(PlexusClassWorldsClassRealmClassLoaderHandler.class), new ClassLoaderHandlerRegistryEntry(QuarkusClassLoaderHandler.class), + new ClassLoaderHandlerRegistryEntry(UnoOneJarClassLoaderHandler.class), // For unit testing of PARENT_LAST delegation order new ClassLoaderHandlerRegistryEntry(ParentLastDelegationOrderTestClassLoaderHandler.class), @@ -107,7 +108,7 @@ public class ClassLoaderHandlerRegistry { "BOOT-INF/classes/", // Tomcat "WEB-INF/classes/", - // UnoJar + // UnoJar and One-Jar "main/" // }; diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/UnoOneJarClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/UnoOneJarClassLoaderHandler.java new file mode 100644 index 000000000..5aa645b0e --- /dev/null +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/UnoOneJarClassLoaderHandler.java @@ -0,0 +1,111 @@ +/* + * 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.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; + +/** Extract classpath entries from the UnoJar's JarClassLoader and One-Jar's JarClassLoader. */ +class UnoOneJarClassLoaderHandler implements ClassLoaderHandler { + /** Class cannot be constructed. */ + private UnoOneJarClassLoaderHandler() { + } + + /** + * 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 "com.needhamsoftware.unojar.JarClassLoader".equals(classLoaderClass.getName()) + || "com.simontuffs.onejar.JarClassLoader".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) { + 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) { + + // For UnoJar: + + final String unoJarPath = (String) ReflectionUtils.invokeMethod(classLoader, "getOneJarPath", false); + classpathOrder.addClasspathEntry(unoJarPath, classLoader, scanSpec, log); + + // For One-Jar: + + // If this property is defined, OneJar jar path was specified on commandline. Otherwise, jar path + // should be contained in java.class.path (which will be separately picked up by ClassGraph, as + // long as classloaders/classpath are not overloaded and parent classloaders are not ignored). + final String oneJarJarPath = System.getProperty("one-jar.jar.path"); + classpathOrder.addClasspathEntry(oneJarJarPath, classLoader, scanSpec, log); + + // If this property is defined, additional classpath entries were specified in OneJar format + // on the commandline, with '|' as a separator + final String oneJarClassPath = System.getProperty("one-jar.class.path"); + if (oneJarClassPath != null) { + classpathOrder.addClasspathEntryObject(oneJarClassPath.split("\\|"), classLoader, scanSpec, log); + } + + // For both UnoJar and OneJar, "libs/" will be automatically picked up as a library root (containing + // nested jars) and "main/" will be automatically picked up as a package root. + } +} From 864753192bbcf227a13ecb9640f4ad1bf5085f23 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 30 Apr 2020 22:22:18 -0600 Subject: [PATCH 0799/1778] Move `main/` to AUTOMATIC_LIB_DIR_PREFIXES (#424) --- .../classloaderhandler/ClassLoaderHandlerRegistry.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 dbf722c97..91728423d 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java @@ -92,7 +92,9 @@ public class ClassLoaderHandlerRegistry { // Tomcat and others "lib/", // Extension dir - "lib/ext/" // + "lib/ext/", + // UnoJar and One-Jar + "main/" // }; /** @@ -108,8 +110,6 @@ public class ClassLoaderHandlerRegistry { "BOOT-INF/classes/", // Tomcat "WEB-INF/classes/", - // UnoJar and One-Jar - "main/" // }; // ------------------------------------------------------------------------------------------------------------- From 14da6b116de40a3a6a9b2b99c888360997cdb670 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 30 Apr 2020 22:35:24 -0600 Subject: [PATCH 0800/1778] Update comment --- .../classloaderhandler/UnoOneJarClassLoaderHandler.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 5aa645b0e..82c491657 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/UnoOneJarClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/UnoOneJarClassLoaderHandler.java @@ -105,7 +105,7 @@ public static void findClasspathOrder(final ClassLoader classLoader, final Class classpathOrder.addClasspathEntryObject(oneJarClassPath.split("\\|"), classLoader, scanSpec, log); } - // For both UnoJar and OneJar, "libs/" will be automatically picked up as a library root (containing - // nested jars) and "main/" will be automatically picked up as a package root. + // For both UnoJar and OneJar, "libs/" and "main/" will be automatically picked up as library roots + // for nested jars. } } From 26cc518e382f4ba0ba3a85511b7cce4802cb6fb3 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 30 Apr 2020 22:38:31 -0600 Subject: [PATCH 0801/1778] Update comment --- .../classloaderhandler/UnoOneJarClassLoaderHandler.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 82c491657..e2a4e6b24 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/UnoOneJarClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/UnoOneJarClassLoaderHandler.java @@ -34,7 +34,7 @@ import nonapi.io.github.classgraph.utils.LogNode; import nonapi.io.github.classgraph.utils.ReflectionUtils; -/** Extract classpath entries from the UnoJar's JarClassLoader and One-Jar's JarClassLoader. */ +/** Extract classpath entries from the Uno-Jar's JarClassLoader and One-Jar's JarClassLoader. */ class UnoOneJarClassLoaderHandler implements ClassLoaderHandler { /** Class cannot be constructed. */ private UnoOneJarClassLoaderHandler() { @@ -85,7 +85,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) { - // For UnoJar: + // For Uno-Jar: final String unoJarPath = (String) ReflectionUtils.invokeMethod(classLoader, "getOneJarPath", false); classpathOrder.addClasspathEntry(unoJarPath, classLoader, scanSpec, log); From e7c723d6e626b0933ee68712c79d82f8b2b2782a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 30 Apr 2020 23:08:51 -0600 Subject: [PATCH 0802/1778] Updates (#423) --- .../UnoOneJarClassLoaderHandler.java | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) 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 e2a4e6b24..8331fa2f9 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/UnoOneJarClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/UnoOneJarClassLoaderHandler.java @@ -87,16 +87,16 @@ public static void findClasspathOrder(final ClassLoader classLoader, final Class // For Uno-Jar: - final String unoJarPath = (String) ReflectionUtils.invokeMethod(classLoader, "getOneJarPath", false); - classpathOrder.addClasspathEntry(unoJarPath, classLoader, scanSpec, log); + final String unoJarOneJarPath = (String) ReflectionUtils.invokeMethod(classLoader, "getOneJarPath", false); + classpathOrder.addClasspathEntry(unoJarOneJarPath, classLoader, scanSpec, log); - // For One-Jar: - - // If this property is defined, OneJar jar path was specified on commandline. Otherwise, jar path + // If this property is defined, Uno-Jar jar path was specified on commandline. Otherwise, jar path // should be contained in java.class.path (which will be separately picked up by ClassGraph, as // long as classloaders/classpath are not overloaded and parent classloaders are not ignored). - final String oneJarJarPath = System.getProperty("one-jar.jar.path"); - classpathOrder.addClasspathEntry(oneJarJarPath, classLoader, scanSpec, log); + final String unoJarJarPath = System.getProperty("uno-jar.jar.path"); + classpathOrder.addClasspathEntry(unoJarJarPath, classLoader, scanSpec, log); + + // For One-Jar: // If this property is defined, additional classpath entries were specified in OneJar format // on the commandline, with '|' as a separator @@ -105,7 +105,14 @@ public static void findClasspathOrder(final ClassLoader classLoader, final Class classpathOrder.addClasspathEntryObject(oneJarClassPath.split("\\|"), classLoader, scanSpec, log); } + // If this property is defined, One-Jar jar path was specified on commandline. Otherwise, jar path + // should be contained in java.class.path (which will be separately picked up by ClassGraph, as + // long as classloaders/classpath are not overloaded and parent classloaders are not ignored). + final String oneJarJarPath = System.getProperty("one-jar.jar.path"); + classpathOrder.addClasspathEntry(oneJarJarPath, classLoader, scanSpec, log); + // For both UnoJar and OneJar, "libs/" and "main/" will be automatically picked up as library roots - // for nested jars. + // for nested jars, based on ClassLoaderHandlerRegistry.AUTOMATIC_LIB_DIR_PREFIXES. + // ("main/" contains "main.jar".) } } From 36ce9908ff58b0164ec363209c768a598d38f6e1 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 1 May 2020 03:18:46 -0600 Subject: [PATCH 0803/1778] Reorder lines --- .../UnoOneJarClassLoaderHandler.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) 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 8331fa2f9..17fc15f60 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/UnoOneJarClassLoaderHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/UnoOneJarClassLoaderHandler.java @@ -98,6 +98,12 @@ public static void findClasspathOrder(final ClassLoader classLoader, final Class // For One-Jar: + // If this property is defined, One-Jar jar path was specified on commandline. Otherwise, jar path + // should be contained in java.class.path (which will be separately picked up by ClassGraph, as + // long as classloaders/classpath are not overloaded and parent classloaders are not ignored). + final String oneJarJarPath = System.getProperty("one-jar.jar.path"); + classpathOrder.addClasspathEntry(oneJarJarPath, classLoader, scanSpec, log); + // If this property is defined, additional classpath entries were specified in OneJar format // on the commandline, with '|' as a separator final String oneJarClassPath = System.getProperty("one-jar.class.path"); @@ -105,12 +111,6 @@ public static void findClasspathOrder(final ClassLoader classLoader, final Class classpathOrder.addClasspathEntryObject(oneJarClassPath.split("\\|"), classLoader, scanSpec, log); } - // If this property is defined, One-Jar jar path was specified on commandline. Otherwise, jar path - // should be contained in java.class.path (which will be separately picked up by ClassGraph, as - // long as classloaders/classpath are not overloaded and parent classloaders are not ignored). - final String oneJarJarPath = System.getProperty("one-jar.jar.path"); - classpathOrder.addClasspathEntry(oneJarJarPath, classLoader, scanSpec, log); - // For both UnoJar and OneJar, "libs/" and "main/" will be automatically picked up as library roots // for nested jars, based on ClassLoaderHandlerRegistry.AUTOMATIC_LIB_DIR_PREFIXES. // ("main/" contains "main.jar".) From 68d5fd15df155796ed2f0b0360067e91ff0f9ef6 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 1 May 2020 16:14:44 -0600 Subject: [PATCH 0804/1778] Fix normalization of paths with trailing '/' (#425) --- .../io/github/classgraph/ClasspathElementZip.java | 4 ++-- src/main/java/io/github/classgraph/ScanResult.java | 11 ++++++++--- .../ClassLoaderHandlerRegistry.java | 3 +-- .../fastzipfilereader/LogicalZipFile.java | 3 ++- .../fastzipfilereader/NestedJarHandler.java | 3 ++- .../github/classgraph/scanspec/WhiteBlackList.java | 7 ++++++- .../github/classgraph/utils/FastPathResolver.java | 5 +++-- .../io/github/classgraph/utils/FileUtils.java | 13 +++++++++++-- 8 files changed, 35 insertions(+), 14 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index f4ab83ff7..21483ae07 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -263,8 +263,8 @@ void open(final WorkQueue workQueue, final LogNode log) // entry that they were obtained from). if (!childBundlePath.isEmpty() && !childBundlePath.equals(".")) { // Resolve Bundle-ClassPath entry within jar - final String childClassPathEltPath = zipFilePathPrefix - + FileUtils.sanitizeEntryPath(childBundlePath, /* removeInitialSlash = */ true); + final String childClassPathEltPath = zipFilePathPrefix + FileUtils.sanitizeEntryPath( + childBundlePath, /* removeInitialSlash = */ true, /* removeFinalSlash = */ true); // Only add child classpath elements once if (scheduledChildClasspathElements.add(childClassPathEltPath)) { // Schedule child classpath element for scanning diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index aecf1c3e8..6801cab72 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -506,7 +506,8 @@ public ResourceList getResourcesWithPath(final String resourcePath) { if (closed.get()) { throw new IllegalArgumentException("Cannot use a ScanResult after it has been closed"); } - final String path = FileUtils.sanitizeEntryPath(resourcePath, /* removeInitialSlash = */ true); + final String path = FileUtils.sanitizeEntryPath(resourcePath, /* removeInitialSlash = */ true, + /* removeFinalSlash = */ true); if (getResourcesWithPathCallCount.incrementAndGet() > 3) { // If numerous calls are made, produce and cache a single HashMap for O(1) access time return getAllResourcesAsMap().get(path); @@ -530,7 +531,10 @@ public ResourceList getResourcesWithPath(final String resourcePath) { /** * Get the list of all resources found in any classpath element, whether in whitelisted packages or not (as * long as the resource is not blacklisted), that have the given path, relative to the package root of the - * classpath element. May match several resources, up to one per classpath element. + * classpath element. May match several resources, up to one per classpath element. Note that this may not + * return a non-whitelisted resource, particularly when scanning directory classpath elements, because recursive + * scanning terminates once there are no possible whitelisted resources below a given directory. However, + * resources in ancestral directories of whitelisted directories can be found using this method. * * @param resourcePath * A complete resource path, relative to the classpath entry package root. @@ -542,7 +546,8 @@ public ResourceList getResourcesWithPathIgnoringWhitelist(final String resourceP if (closed.get()) { throw new IllegalArgumentException("Cannot use a ScanResult after it has been closed"); } - final String path = FileUtils.sanitizeEntryPath(resourcePath, /* removeInitialSlash = */ true); + final String path = FileUtils.sanitizeEntryPath(resourcePath, /* removeInitialSlash = */ true, + /* removeFinalSlash = */ true); final ResourceList matchingResources = new ResourceList(); for (final ClasspathElement classpathElt : classpathOrder) { final Resource matchingResource = classpathElt.getResource(path); 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 91728423d..5b57b2a7b 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java @@ -109,8 +109,7 @@ public class ClassLoaderHandlerRegistry { // Spring-Boot "BOOT-INF/classes/", // Tomcat - "WEB-INF/classes/", - }; + "WEB-INF/classes/", }; // ------------------------------------------------------------------------------------------------------------- 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 c8946171f..dbcdf4741 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java @@ -616,7 +616,8 @@ private void readCentralDirectory(final NestedJarHandler nestedJarHandler, final break; } final String entryName = cenReader.readString(filenameStartOff, filenameLen); - String entryNameSanitized = FileUtils.sanitizeEntryPath(entryName, /* removeInitialSlash = */ true); + String entryNameSanitized = FileUtils.sanitizeEntryPath(entryName, /* removeInitialSlash = */ true, + /* removeFinalSlash = */ false); if (entryNameSanitized.isEmpty() || entryName.endsWith("/")) { // Skip directory entries continue; 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 a5da6d2ef..f1837e95d 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java @@ -215,7 +215,8 @@ public Entry newInstance(final String nestedJarPathRaw, final String parentPath = nestedJarPath.substring(0, lastPlingIdx); String childPath = nestedJarPath.substring(lastPlingIdx + 1); // "file.jar!/path" -> "file.jar!path" - childPath = FileUtils.sanitizeEntryPath(childPath, /* removeInitialSlash = */ true); + 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 diff --git a/src/main/java/nonapi/io/github/classgraph/scanspec/WhiteBlackList.java b/src/main/java/nonapi/io/github/classgraph/scanspec/WhiteBlackList.java index 0e30fea3f..ad2be685f 100644 --- a/src/main/java/nonapi/io/github/classgraph/scanspec/WhiteBlackList.java +++ b/src/main/java/nonapi/io/github/classgraph/scanspec/WhiteBlackList.java @@ -36,6 +36,7 @@ import java.util.regex.Pattern; import nonapi.io.github.classgraph.utils.CollectionUtils; +import nonapi.io.github.classgraph.utils.FastPathResolver; import nonapi.io.github.classgraph.utils.FileUtils; import nonapi.io.github.classgraph.utils.JarUtils; @@ -512,7 +513,11 @@ public boolean isBlacklisted(final String str) { * @return The normalized path. */ public static String normalizePath(final String path) { - return FileUtils.sanitizeEntryPath(path, /* removeInitialSlash = */ true); + String pathResolved = FastPathResolver.resolve(path); + while (pathResolved.startsWith("/")) { + pathResolved = pathResolved.substring(1); + } + return pathResolved; } /** 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 c10e86358..b80851085 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FastPathResolver.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FastPathResolver.java @@ -314,12 +314,13 @@ public static String resolve(final String resolveBasePath, final String relative if (isAbsolutePath || resolveBasePath == null || resolveBasePath.isEmpty()) { // There is no base path to resolve against, or path is an absolute path or http(s):// URL // (ignore the base path) - pathResolved = FileUtils.sanitizeEntryPath(pathStr, /* removeInitialSlash = */ false); + pathResolved = FileUtils.sanitizeEntryPath(pathStr, /* removeInitialSlash = */ false, + /* removeFinalSlash = */ true); } else { // Path is a relative path -- resolve it relative to the base path pathResolved = FileUtils.sanitizeEntryPath( resolveBasePath + (resolveBasePath.endsWith("/") ? "" : "/") + pathStr, - /* removeInitialSlash = */ false); + /* removeInitialSlash = */ false, /* removeFinalSlash = */ true); } // Add any prefix back, e.g. "https://" 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 d24bb25f5..828c90f88 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -111,10 +111,13 @@ private FileUtils() { * @param path * The path to sanitize. * @param removeInitialSlash - * If true, additionally removes any "/" character(s) from the beginning of the returned path. + * If true, remove any '/' character(s) from the beginning of the returned path. + * @param removeFinalSlash + * If true, remove any '/' character(s) from the end of the returned path. * @return The sanitized path. */ - public static String sanitizeEntryPath(final String path, final boolean removeInitialSlash) { + public static String sanitizeEntryPath(final String path, final boolean removeInitialSlash, + final boolean removeFinalSlash) { if (path.isEmpty()) { return ""; } @@ -209,6 +212,12 @@ public static String sanitizeEntryPath(final String path, final boolean removeIn startIdx++; } } + if (removeFinalSlash) { + while (pathSanitized.length() > 0 && pathSanitized.charAt(pathSanitized.length() - 1) == '/') { + pathSanitized.setLength(pathSanitized.length() - 1); + } + } + return pathSanitized.substring(startIdx); } From 809a063040af77914ce649b2d9ae814b0d3a8221 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 1 May 2020 22:31:20 -0600 Subject: [PATCH 0805/1778] [maven-release-plugin] prepare release classgraph-4.8.78 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 46e5b43e2..5550d2049 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.78-SNAPSHOT + 4.8.78 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.74 + classgraph-4.8.78 From 74a938dc4291bec1d4bf94bd6b1443d60de8f157 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 1 May 2020 22:31:28 -0600 Subject: [PATCH 0806/1778] [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 5550d2049..a16bda5ab 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.78 + 4.8.79-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.78 + classgraph-4.8.74 From 6de163e053cacc42ae211adb29051a05289000e0 Mon Sep 17 00:00:00 2001 From: "Sean C. Sullivan" Date: Sat, 9 May 2020 12:45:31 -0700 Subject: [PATCH 0807/1778] assertj 3.16.1 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a16bda5ab..b9e2e7fd1 100644 --- a/pom.xml +++ b/pom.xml @@ -87,7 +87,7 @@ org.assertj assertj-core - 3.15.0 + 3.16.1 test From e654742706c2b0ad36a90fb1a866fc8a0ca08d71 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 31 May 2020 02:26:47 -0600 Subject: [PATCH 0808/1778] Fix #430 (Make InputStreamConsumer to be able to throw IOException) --- pom.xml | 2 +- .../io/github/classgraph/ResourceList.java | 185 ++++++++++++++++-- 2 files changed, 171 insertions(+), 16 deletions(-) diff --git a/pom.xml b/pom.xml index b9e2e7fd1..25d100e5e 100644 --- a/pom.xml +++ b/pom.xml @@ -233,7 +233,7 @@ - + org.apache.maven.plugins maven-enforcer-plugin diff --git a/src/main/java/io/github/classgraph/ResourceList.java b/src/main/java/io/github/classgraph/ResourceList.java index 1924a6f65..862892853 100644 --- a/src/main/java/io/github/classgraph/ResourceList.java +++ b/src/main/java/io/github/classgraph/ResourceList.java @@ -324,6 +324,26 @@ public interface ByteArrayConsumer { void accept(final Resource resource, final byte[] byteArray); } + /** + * A {@link FunctionalInterface} for consuming the contents of a {@link Resource} as a byte array, throwing + * {@link IOException} to the caller if an IO exception occurs. + */ + @FunctionalInterface + public interface ByteArrayConsumerThrowsIOException { + /** + * Consume the complete content of a {@link Resource} as a byte array, possibly throwing + * {@link IOException}. + * + * @param resource + * The {@link Resource} used to load the byte array. + * @param byteArray + * The complete content of the resource. + * @throws IOException + * if an IO exception occurs. + */ + void accept(final Resource resource, final byte[] byteArray) throws IOException; + } + /** * Fetch the content of each {@link Resource} in this {@link ResourceList} as a byte array, pass the byte array * to the given {@link ByteArrayConsumer}, then close the underlying InputStream or release the underlying @@ -337,7 +357,10 @@ public interface ByteArrayConsumer { * @throws IllegalArgumentException * if ignoreExceptions is false, and an {@link IOException} is thrown while trying to load any of * the resources. + * @deprecated Use {@link #forEachByteArray(ByteArrayConsumer)} or + * {@link #forEachByteArray(ByteArrayConsumerThrowsIOException)} instead. */ + @Deprecated public void forEachByteArray(final ByteArrayConsumer byteArrayConsumer, final boolean ignoreIOExceptions) { for (final Resource resource : this) { try { @@ -356,15 +379,45 @@ public void forEachByteArray(final ByteArrayConsumer byteArrayConsumer, final bo /** * Fetch the content of each {@link Resource} in this {@link ResourceList} as a byte array, pass the byte array * to the given {@link ByteArrayConsumer}, then close the underlying InputStream or release the underlying - * ByteBuffer by calling {@link Resource#close()}. + * ByteBuffer by calling {@link Resource#close()} for each {@link Resource}. If an {@link IOException} occurs + * while opening or reading from any resource, the resource is silently skipped. * * @param byteArrayConsumer * The {@link ByteArrayConsumer}. - * @throws IllegalArgumentException - * if trying to load any of the resources results in an {@link IOException} being thrown. */ public void forEachByteArray(final ByteArrayConsumer byteArrayConsumer) { - forEachByteArray(byteArrayConsumer, /* ignoreIOExceptions = */ false); + for (final Resource resource : this) { + try { + final byte[] resourceContent = resource.load(); + byteArrayConsumer.accept(resource, resourceContent); + } catch (final IOException e) { + // Ignore + } finally { + resource.close(); + } + } + } + + /** + * Fetch the content of each {@link Resource} in this {@link ResourceList} as a byte array, pass the byte array + * to the given {@link ByteArrayConsumer}, then close the underlying InputStream or release the underlying + * ByteBuffer by calling {@link Resource#close()}. + * + * @param byteArrayConsumerThrowsIOException + * The {@link ByteArrayConsumerThrowsIOException}. + * @throws IOException + * if trying to load any of the resources results in an {@link IOException} being thrown. + */ + public void forEachByteArray(final ByteArrayConsumerThrowsIOException byteArrayConsumerThrowsIOException) + throws IOException { + for (final Resource resource : this) { + try { + final byte[] resourceContent = resource.load(); + byteArrayConsumerThrowsIOException.accept(resource, resourceContent); + } finally { + resource.close(); + } + } } // ------------------------------------------------------------------------------------------------------------- @@ -383,10 +436,30 @@ public interface InputStreamConsumer { void accept(final Resource resource, final InputStream inputStream); } + /** + * A {@link FunctionalInterface} for consuming the contents of a {@link Resource} as an {@link InputStream}, + * throwing {@link IOException} to the caller if an IO exception occurs. + */ + @FunctionalInterface + public interface InputStreamConsumerThrowsIOException { + /** + * Consume the complete content of a {@link Resource} as a byte array, possibly throwing + * {@link IOException}. + * + * @param resource + * The {@link Resource} used to load the byte array. + * @param inputStream + * The {@link InputStream} opened on the resource. + * @throws IOException + * if an IO exception occurs. + */ + void accept(final Resource resource, final InputStream inputStream) throws IOException; + } + /** * Fetch an {@link InputStream} for each {@link Resource} in this {@link ResourceList}, pass the * {@link InputStream} to the given {@link InputStreamConsumer}, then close the {@link InputStream} after the - * {@link InputStreamConsumer} returns, by calling {@link Resource#close()}. + * {@link InputStreamConsumer} returns, by calling {@link Resource#close()} for each {@link Resource}. * * @param inputStreamConsumer * The {@link InputStreamConsumer}. @@ -396,7 +469,10 @@ public interface InputStreamConsumer { * @throws IllegalArgumentException * if ignoreExceptions is false, and an {@link IOException} is thrown while trying to open any of * the resources. + * @deprecated Use {@link #forEachInputStream(InputStreamConsumer)} or + * {@link #forEachInputStream(InputStreamConsumerThrowsIOException)} instead. */ + @Deprecated public void forEachInputStream(final InputStreamConsumer inputStreamConsumer, final boolean ignoreIOExceptions) { for (final Resource resource : this) { @@ -415,15 +491,44 @@ public void forEachInputStream(final InputStreamConsumer inputStreamConsumer, /** * Fetch an {@link InputStream} for each {@link Resource} in this {@link ResourceList}, pass the * {@link InputStream} to the given {@link InputStreamConsumer}, then close the {@link InputStream} after the - * {@link InputStreamConsumer} returns, by calling {@link Resource#close()}. + * {@link InputStreamConsumer} returns, by calling {@link Resource#close()} for each {@link Resource}. If an + * {@link IOException} occurs while opening or reading from any resource, the resource is silently skipped. * * @param inputStreamConsumer * The {@link InputStreamConsumer}. - * @throws IllegalArgumentException - * if trying to open any of the resources results in an {@link IOException} being thrown. */ public void forEachInputStream(final InputStreamConsumer inputStreamConsumer) { - forEachInputStream(inputStreamConsumer, /* ignoreIOExceptions = */ false); + for (final Resource resource : this) { + try { + inputStreamConsumer.accept(resource, resource.open()); + } catch (final IOException e) { + // Ignore + } finally { + resource.close(); + } + } + } + + /** + * Fetch an {@link InputStream} for each {@link Resource} in this {@link ResourceList}, pass the + * {@link InputStream} to the given {@link InputStreamConsumer}, then close the {@link InputStream} after the + * {@link InputStreamConsumer} returns, by calling {@link Resource#close()}. + * + * @param inputStreamConsumerThrowsIOException + * The {@link InputStreamConsumerThrowsIOException}. + * @throws IOException + * if trying to open or read from any of the resources results in an {@link IOException} being + * thrown. + */ + public void forEachInputStream(final InputStreamConsumerThrowsIOException inputStreamConsumerThrowsIOException) + throws IOException { + for (final Resource resource : this) { + try { + inputStreamConsumerThrowsIOException.accept(resource, resource.open()); + } finally { + resource.close(); + } + } } // ------------------------------------------------------------------------------------------------------------- @@ -432,7 +537,7 @@ public void forEachInputStream(final InputStreamConsumer inputStreamConsumer) { @FunctionalInterface public interface ByteBufferConsumer { /** - * Consume a {@link Resource} as a {@link ByteBuffer}. + * Consume a {@link Resource} as a {@link ByteBuffer}, possibly throwing {@link IOException}. * * @param resource * The {@link Resource} whose content is reflected in the {@link ByteBuffer}. @@ -442,10 +547,29 @@ public interface ByteBufferConsumer { void accept(final Resource resource, final ByteBuffer byteBuffer); } + /** + * A {@link FunctionalInterface} for consuming the contents of a {@link Resource} as a {@link ByteBuffer}, + * throwing {@link IOException} to the caller if an IO exception occurs. + */ + @FunctionalInterface + public interface ByteBufferConsumerThrowsIOException { + /** + * Consume the complete content of a {@link Resource} as a byte array. + * + * @param resource + * The {@link Resource} used to load the byte array, possibly throwing {@link IOException}. + * @param byteBuffer + * The {@link ByteBuffer} mapped to the resource. + * @throws IOException + * if an IO exception occurs. + */ + void accept(final Resource resource, final ByteBuffer byteBuffer) throws IOException; + } + /** * Read each {@link Resource} in this {@link ResourceList} as a {@link ByteBuffer}, pass the {@link ByteBuffer} * to the given {@link InputStreamConsumer}, then release the {@link ByteBuffer} after the - * {@link ByteBufferConsumer} returns, by calling {@link Resource#close()}. + * {@link ByteBufferConsumer} returns, by calling {@link Resource#close()} for each {@link Resource}. * * @param byteBufferConsumer * The {@link ByteBufferConsumer}. @@ -456,6 +580,7 @@ public interface ByteBufferConsumer { * if ignoreExceptions is false, and an {@link IOException} is thrown while trying to load any of * the resources. */ + @Deprecated public void forEachByteBuffer(final ByteBufferConsumer byteBufferConsumer, final boolean ignoreIOExceptions) { for (final Resource resource : this) { try { @@ -474,15 +599,45 @@ public void forEachByteBuffer(final ByteBufferConsumer byteBufferConsumer, final /** * Read each {@link Resource} in this {@link ResourceList} as a {@link ByteBuffer}, pass the {@link ByteBuffer} * to the given {@link InputStreamConsumer}, then release the {@link ByteBuffer} after the - * {@link ByteBufferConsumer} returns, by calling {@link Resource#close()}. + * {@link ByteBufferConsumer} returns, by calling {@link Resource#close()} for each {@link Resource}. If an + * {@link IOException} occurs while opening or reading from any resource, the resource is silently skipped. * * @param byteBufferConsumer * The {@link ByteBufferConsumer}. - * @throws IllegalArgumentException - * if trying to load any of the resources results in an {@link IOException} being thrown. */ public void forEachByteBuffer(final ByteBufferConsumer byteBufferConsumer) { - forEachByteBuffer(byteBufferConsumer, /* ignoreIOExceptions = */ false); + for (final Resource resource : this) { + try { + final ByteBuffer byteBuffer = resource.read(); + byteBufferConsumer.accept(resource, byteBuffer); + } catch (final IOException e) { + // Ignore + } finally { + resource.close(); + } + } + } + + /** + * Read each {@link Resource} in this {@link ResourceList} as a {@link ByteBuffer}, pass the {@link ByteBuffer} + * to the given {@link InputStreamConsumer}, then release the {@link ByteBuffer} after the + * {@link ByteBufferConsumer} returns, by calling {@link Resource#close()}. + * + * @param byteBufferConsumerThrowsIOException + * The {@link ByteBufferConsumerThrowsIOException}. + * @throws IOException + * if trying to load any of the resources results in an {@link IOException} being thrown. + */ + public void forEachByteBuffer(final ByteBufferConsumerThrowsIOException byteBufferConsumerThrowsIOException) + throws IOException { + for (final Resource resource : this) { + try { + final ByteBuffer byteBuffer = resource.read(); + byteBufferConsumerThrowsIOException.accept(resource, byteBuffer); + } finally { + resource.close(); + } + } } // ------------------------------------------------------------------------------------------------------------- From f4cf02b75b41c3fbe7f3008613d7327c4a62b751 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 31 May 2020 02:37:40 -0600 Subject: [PATCH 0809/1778] Improve API of previous commit (#430) --- .../io/github/classgraph/ResourceList.java | 70 ++++++++++++++++--- 1 file changed, 60 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/github/classgraph/ResourceList.java b/src/main/java/io/github/classgraph/ResourceList.java index 862892853..2693930b8 100644 --- a/src/main/java/io/github/classgraph/ResourceList.java +++ b/src/main/java/io/github/classgraph/ResourceList.java @@ -357,8 +357,8 @@ public interface ByteArrayConsumerThrowsIOException { * @throws IllegalArgumentException * if ignoreExceptions is false, and an {@link IOException} is thrown while trying to load any of * the resources. - * @deprecated Use {@link #forEachByteArray(ByteArrayConsumer)} or - * {@link #forEachByteArray(ByteArrayConsumerThrowsIOException)} instead. + * @deprecated Use {@link #forEach(ByteArrayConsumer)} or {@link #forEach(ByteArrayConsumerThrowsIOException)} + * instead. */ @Deprecated public void forEachByteArray(final ByteArrayConsumer byteArrayConsumer, final boolean ignoreIOExceptions) { @@ -376,6 +376,22 @@ public void forEachByteArray(final ByteArrayConsumer byteArrayConsumer, final bo } } + /** + * Fetch the content of each {@link Resource} in this {@link ResourceList} as a byte array, pass the byte array + * to the given {@link ByteArrayConsumer}, then close the underlying InputStream or release the underlying + * ByteBuffer by calling {@link Resource#close()}. + * + * @param byteArrayConsumer + * The {@link ByteArrayConsumer}. + * @throws IllegalArgumentException + * if an {@link IOException} is thrown while trying to load any of the resources. + * @deprecated Use {@link #forEach(ByteArrayConsumerThrowsIOException)} instead. + */ + @Deprecated + public void forEachByteArray(final ByteArrayConsumer byteArrayConsumer) { + forEachByteArray(byteArrayConsumer, false); + } + /** * Fetch the content of each {@link Resource} in this {@link ResourceList} as a byte array, pass the byte array * to the given {@link ByteArrayConsumer}, then close the underlying InputStream or release the underlying @@ -385,7 +401,7 @@ public void forEachByteArray(final ByteArrayConsumer byteArrayConsumer, final bo * @param byteArrayConsumer * The {@link ByteArrayConsumer}. */ - public void forEachByteArray(final ByteArrayConsumer byteArrayConsumer) { + public void forEach(final ByteArrayConsumer byteArrayConsumer) { for (final Resource resource : this) { try { final byte[] resourceContent = resource.load(); @@ -408,7 +424,7 @@ public void forEachByteArray(final ByteArrayConsumer byteArrayConsumer) { * @throws IOException * if trying to load any of the resources results in an {@link IOException} being thrown. */ - public void forEachByteArray(final ByteArrayConsumerThrowsIOException byteArrayConsumerThrowsIOException) + public void forEach(final ByteArrayConsumerThrowsIOException byteArrayConsumerThrowsIOException) throws IOException { for (final Resource resource : this) { try { @@ -469,8 +485,8 @@ public interface InputStreamConsumerThrowsIOException { * @throws IllegalArgumentException * if ignoreExceptions is false, and an {@link IOException} is thrown while trying to open any of * the resources. - * @deprecated Use {@link #forEachInputStream(InputStreamConsumer)} or - * {@link #forEachInputStream(InputStreamConsumerThrowsIOException)} instead. + * @deprecated Use {@link #forEach(InputStreamConsumer)} or + * {@link #forEach(InputStreamConsumerThrowsIOException)} instead. */ @Deprecated public void forEachInputStream(final InputStreamConsumer inputStreamConsumer, @@ -488,6 +504,22 @@ public void forEachInputStream(final InputStreamConsumer inputStreamConsumer, } } + /** + * Fetch an {@link InputStream} for each {@link Resource} in this {@link ResourceList}, pass the + * {@link InputStream} to the given {@link InputStreamConsumer}, then close the {@link InputStream} after the + * {@link InputStreamConsumer} returns, by calling {@link Resource#close()} for each {@link Resource}. + * + * @param inputStreamConsumer + * The {@link InputStreamConsumer}. + * @throws IllegalArgumentException + * an {@link IOException} is thrown while trying to open any of the resources. + * @deprecated Use {@link #forEach(InputStreamConsumerThrowsIOException)} instead. + */ + @Deprecated + public void forEachInputStream(final InputStreamConsumer inputStreamConsumer) { + forEachInputStream(inputStreamConsumer, false); + } + /** * Fetch an {@link InputStream} for each {@link Resource} in this {@link ResourceList}, pass the * {@link InputStream} to the given {@link InputStreamConsumer}, then close the {@link InputStream} after the @@ -497,7 +529,7 @@ public void forEachInputStream(final InputStreamConsumer inputStreamConsumer, * @param inputStreamConsumer * The {@link InputStreamConsumer}. */ - public void forEachInputStream(final InputStreamConsumer inputStreamConsumer) { + public void forEach(final InputStreamConsumer inputStreamConsumer) { for (final Resource resource : this) { try { inputStreamConsumer.accept(resource, resource.open()); @@ -520,7 +552,7 @@ public void forEachInputStream(final InputStreamConsumer inputStreamConsumer) { * if trying to open or read from any of the resources results in an {@link IOException} being * thrown. */ - public void forEachInputStream(final InputStreamConsumerThrowsIOException inputStreamConsumerThrowsIOException) + public void forEach(final InputStreamConsumerThrowsIOException inputStreamConsumerThrowsIOException) throws IOException { for (final Resource resource : this) { try { @@ -579,6 +611,8 @@ public interface ByteBufferConsumerThrowsIOException { * @throws IllegalArgumentException * if ignoreExceptions is false, and an {@link IOException} is thrown while trying to load any of * the resources. + * @deprecated Use {@link #forEach(ByteBufferConsumer)} or {@link #forEach(ByteBufferConsumerThrowsIOException)} + * instead. */ @Deprecated public void forEachByteBuffer(final ByteBufferConsumer byteBufferConsumer, final boolean ignoreIOExceptions) { @@ -596,6 +630,22 @@ public void forEachByteBuffer(final ByteBufferConsumer byteBufferConsumer, final } } + /** + * Read each {@link Resource} in this {@link ResourceList} as a {@link ByteBuffer}, pass the {@link ByteBuffer} + * to the given {@link InputStreamConsumer}, then release the {@link ByteBuffer} after the + * {@link ByteBufferConsumer} returns, by calling {@link Resource#close()} for each {@link Resource}. + * + * @param byteBufferConsumer + * The {@link ByteBufferConsumer}. + * @throws IllegalArgumentException + * if an {@link IOException} is thrown while trying to load any of the resources. + * @deprecated Use {@link #forEach(ByteBufferConsumerThrowsIOException)} instead. + */ + @Deprecated + public void forEachByteBuffer(final ByteBufferConsumer byteBufferConsumer) { + forEachByteBuffer(byteBufferConsumer, false); + } + /** * Read each {@link Resource} in this {@link ResourceList} as a {@link ByteBuffer}, pass the {@link ByteBuffer} * to the given {@link InputStreamConsumer}, then release the {@link ByteBuffer} after the @@ -605,7 +655,7 @@ public void forEachByteBuffer(final ByteBufferConsumer byteBufferConsumer, final * @param byteBufferConsumer * The {@link ByteBufferConsumer}. */ - public void forEachByteBuffer(final ByteBufferConsumer byteBufferConsumer) { + public void forEach(final ByteBufferConsumer byteBufferConsumer) { for (final Resource resource : this) { try { final ByteBuffer byteBuffer = resource.read(); @@ -628,7 +678,7 @@ public void forEachByteBuffer(final ByteBufferConsumer byteBufferConsumer) { * @throws IOException * if trying to load any of the resources results in an {@link IOException} being thrown. */ - public void forEachByteBuffer(final ByteBufferConsumerThrowsIOException byteBufferConsumerThrowsIOException) + public void forEach(final ByteBufferConsumerThrowsIOException byteBufferConsumerThrowsIOException) throws IOException { for (final Resource resource : this) { try { From c6fc3bbc6ffd7075f643e7273c0044904f61687d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 31 May 2020 02:38:39 -0600 Subject: [PATCH 0810/1778] Remove m2e directive --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 25d100e5e..b9e2e7fd1 100644 --- a/pom.xml +++ b/pom.xml @@ -233,7 +233,7 @@ - + org.apache.maven.plugins maven-enforcer-plugin From 41884c7f39c3bd6fd4f1c0b6623d702683a50f28 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 31 May 2020 02:39:30 -0600 Subject: [PATCH 0811/1778] [maven-release-plugin] prepare release classgraph-4.8.79 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index b9e2e7fd1..99bc3e2f8 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.79-SNAPSHOT + 4.8.79 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.74 + classgraph-4.8.79 From bbb3757f082ec822de3b2ab0ad226dc23fc971ee Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 31 May 2020 02:39:37 -0600 Subject: [PATCH 0812/1778] [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 99bc3e2f8..7dc14619f 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.79 + 4.8.80-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.79 + classgraph-4.8.74 From 64376e479a1f22dd84118fc2406559ff3a6d9244 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 31 May 2020 02:51:12 -0600 Subject: [PATCH 0813/1778] Fix lambda ambiguity (#430) --- .../io/github/classgraph/ResourceList.java | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/main/java/io/github/classgraph/ResourceList.java b/src/main/java/io/github/classgraph/ResourceList.java index 2693930b8..b551ea12e 100644 --- a/src/main/java/io/github/classgraph/ResourceList.java +++ b/src/main/java/io/github/classgraph/ResourceList.java @@ -357,8 +357,8 @@ public interface ByteArrayConsumerThrowsIOException { * @throws IllegalArgumentException * if ignoreExceptions is false, and an {@link IOException} is thrown while trying to load any of * the resources. - * @deprecated Use {@link #forEach(ByteArrayConsumer)} or {@link #forEach(ByteArrayConsumerThrowsIOException)} - * instead. + * @deprecated Use {@link #forEachByteArrayIgnoringIOException(ByteArrayConsumer)} or + * {@link #forEachByteArrayThrowingIOException(ByteArrayConsumerThrowsIOException)} instead. */ @Deprecated public void forEachByteArray(final ByteArrayConsumer byteArrayConsumer, final boolean ignoreIOExceptions) { @@ -385,7 +385,7 @@ public void forEachByteArray(final ByteArrayConsumer byteArrayConsumer, final bo * The {@link ByteArrayConsumer}. * @throws IllegalArgumentException * if an {@link IOException} is thrown while trying to load any of the resources. - * @deprecated Use {@link #forEach(ByteArrayConsumerThrowsIOException)} instead. + * @deprecated Use {@link #forEachByteArrayThrowingIOException(ByteArrayConsumerThrowsIOException)} instead. */ @Deprecated public void forEachByteArray(final ByteArrayConsumer byteArrayConsumer) { @@ -401,7 +401,7 @@ public void forEachByteArray(final ByteArrayConsumer byteArrayConsumer) { * @param byteArrayConsumer * The {@link ByteArrayConsumer}. */ - public void forEach(final ByteArrayConsumer byteArrayConsumer) { + public void forEachByteArrayIgnoringIOException(final ByteArrayConsumer byteArrayConsumer) { for (final Resource resource : this) { try { final byte[] resourceContent = resource.load(); @@ -424,8 +424,8 @@ public void forEach(final ByteArrayConsumer byteArrayConsumer) { * @throws IOException * if trying to load any of the resources results in an {@link IOException} being thrown. */ - public void forEach(final ByteArrayConsumerThrowsIOException byteArrayConsumerThrowsIOException) - throws IOException { + public void forEachByteArrayThrowingIOException( + final ByteArrayConsumerThrowsIOException byteArrayConsumerThrowsIOException) throws IOException { for (final Resource resource : this) { try { final byte[] resourceContent = resource.load(); @@ -485,8 +485,8 @@ public interface InputStreamConsumerThrowsIOException { * @throws IllegalArgumentException * if ignoreExceptions is false, and an {@link IOException} is thrown while trying to open any of * the resources. - * @deprecated Use {@link #forEach(InputStreamConsumer)} or - * {@link #forEach(InputStreamConsumerThrowsIOException)} instead. + * @deprecated Use {@link #forEachInputStreamIgnoringIOException(InputStreamConsumer)} or + * {@link #forEachInputStreamThrowingIOException(InputStreamConsumerThrowsIOException)} instead. */ @Deprecated public void forEachInputStream(final InputStreamConsumer inputStreamConsumer, @@ -513,7 +513,7 @@ public void forEachInputStream(final InputStreamConsumer inputStreamConsumer, * The {@link InputStreamConsumer}. * @throws IllegalArgumentException * an {@link IOException} is thrown while trying to open any of the resources. - * @deprecated Use {@link #forEach(InputStreamConsumerThrowsIOException)} instead. + * @deprecated Use {@link #forEachInputStreamThrowingIOException(InputStreamConsumerThrowsIOException)} instead. */ @Deprecated public void forEachInputStream(final InputStreamConsumer inputStreamConsumer) { @@ -529,7 +529,7 @@ public void forEachInputStream(final InputStreamConsumer inputStreamConsumer) { * @param inputStreamConsumer * The {@link InputStreamConsumer}. */ - public void forEach(final InputStreamConsumer inputStreamConsumer) { + public void forEachInputStreamIgnoringIOException(final InputStreamConsumer inputStreamConsumer) { for (final Resource resource : this) { try { inputStreamConsumer.accept(resource, resource.open()); @@ -552,8 +552,8 @@ public void forEach(final InputStreamConsumer inputStreamConsumer) { * if trying to open or read from any of the resources results in an {@link IOException} being * thrown. */ - public void forEach(final InputStreamConsumerThrowsIOException inputStreamConsumerThrowsIOException) - throws IOException { + public void forEachInputStreamThrowingIOException( + final InputStreamConsumerThrowsIOException inputStreamConsumerThrowsIOException) throws IOException { for (final Resource resource : this) { try { inputStreamConsumerThrowsIOException.accept(resource, resource.open()); @@ -611,8 +611,8 @@ public interface ByteBufferConsumerThrowsIOException { * @throws IllegalArgumentException * if ignoreExceptions is false, and an {@link IOException} is thrown while trying to load any of * the resources. - * @deprecated Use {@link #forEach(ByteBufferConsumer)} or {@link #forEach(ByteBufferConsumerThrowsIOException)} - * instead. + * @deprecated Use {@link #forEachByteBufferIgnoringIOException(ByteBufferConsumer)} or + * {@link #forEachByteBufferThrowingIOException(ByteBufferConsumerThrowsIOException)} instead. */ @Deprecated public void forEachByteBuffer(final ByteBufferConsumer byteBufferConsumer, final boolean ignoreIOExceptions) { @@ -639,7 +639,7 @@ public void forEachByteBuffer(final ByteBufferConsumer byteBufferConsumer, final * The {@link ByteBufferConsumer}. * @throws IllegalArgumentException * if an {@link IOException} is thrown while trying to load any of the resources. - * @deprecated Use {@link #forEach(ByteBufferConsumerThrowsIOException)} instead. + * @deprecated Use {@link #forEachByteBufferThrowingIOException(ByteBufferConsumerThrowsIOException)} instead. */ @Deprecated public void forEachByteBuffer(final ByteBufferConsumer byteBufferConsumer) { @@ -655,7 +655,7 @@ public void forEachByteBuffer(final ByteBufferConsumer byteBufferConsumer) { * @param byteBufferConsumer * The {@link ByteBufferConsumer}. */ - public void forEach(final ByteBufferConsumer byteBufferConsumer) { + public void forEachByteBufferIgnoringIOException(final ByteBufferConsumer byteBufferConsumer) { for (final Resource resource : this) { try { final ByteBuffer byteBuffer = resource.read(); @@ -678,8 +678,8 @@ public void forEach(final ByteBufferConsumer byteBufferConsumer) { * @throws IOException * if trying to load any of the resources results in an {@link IOException} being thrown. */ - public void forEach(final ByteBufferConsumerThrowsIOException byteBufferConsumerThrowsIOException) - throws IOException { + public void forEachByteBufferThrowingIOException( + final ByteBufferConsumerThrowsIOException byteBufferConsumerThrowsIOException) throws IOException { for (final Resource resource : this) { try { final ByteBuffer byteBuffer = resource.read(); From 1f3b66e33fea205e59d3e02ed821534808cc322a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 31 May 2020 02:52:26 -0600 Subject: [PATCH 0814/1778] Bump version number back down --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7dc14619f..b9e2e7fd1 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.80-SNAPSHOT + 4.8.79-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 69bebf29c007872f1723e1576d451472c8d38733 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 31 May 2020 02:53:05 -0600 Subject: [PATCH 0815/1778] [maven-release-plugin] prepare release classgraph-4.8.79 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index b9e2e7fd1..99bc3e2f8 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.79-SNAPSHOT + 4.8.79 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.74 + classgraph-4.8.79 From 8c9944657a87008f4c455470ae2460c35e1781c3 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 31 May 2020 02:53:13 -0600 Subject: [PATCH 0816/1778] [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 99bc3e2f8..7dc14619f 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.79 + 4.8.80-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.79 + classgraph-4.8.74 From 3067e49d9938fb3556a1a6f1c6747a0b67feea61 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 31 May 2020 02:57:03 -0600 Subject: [PATCH 0817/1778] [maven-release-plugin] prepare release classgraph-4.8.80 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 7dc14619f..3ef410e92 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.80-SNAPSHOT + 4.8.80 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.74 + classgraph-4.8.80 From 7b2f285e822695edc37099aa29ceb56b2f9c872e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 31 May 2020 02:57:11 -0600 Subject: [PATCH 0818/1778] [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 3ef410e92..b9abf679a 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.80 + 4.8.81-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.80 + classgraph-4.8.74 From 7bfe870258ffedc4a83df3775c74cf55cbb8576b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 31 May 2020 04:53:34 -0600 Subject: [PATCH 0819/1778] Fix Scrutinizer static analyzer warnings --- .../classgraph/ClasspathElementFileDir.java | 6 +++--- .../classgraph/ClasspathElementPathDir.java | 12 ++++++------ .../fastzipfilereader/LogicalZipFile.java | 16 +++++++++------- .../fastzipfilereader/PhysicalZipFile.java | 2 +- .../github/classgraph/fileslice/PathSlice.java | 12 +++--------- 5 files changed, 22 insertions(+), 26 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementFileDir.java b/src/main/java/io/github/classgraph/ClasspathElementFileDir.java index 32824ca58..4e48d1ca1 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementFileDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementFileDir.java @@ -133,10 +133,10 @@ 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 (packageRootDir.equals(classpathEltDir)) { for (final String packageRootPrefix : ClassLoaderHandlerRegistry.AUTOMATIC_PACKAGE_ROOT_PREFIXES) { - final File packageRootDir = new File(classpathEltDir, packageRootPrefix); - if (FileUtils.canReadAndIsDir(packageRootDir)) { + final File packageRoot = new File(classpathEltDir, packageRootPrefix); + if (FileUtils.canReadAndIsDir(packageRoot)) { if (log != null) { - log(classpathElementIdx, "Found package root: " + packageRootDir, log); + log(classpathElementIdx, "Found package root: " + packageRoot, log); } workQueue .addWorkUnit(new ClasspathEntryWorkUnit( diff --git a/src/main/java/io/github/classgraph/ClasspathElementPathDir.java b/src/main/java/io/github/classgraph/ClasspathElementPathDir.java index 60f7ed109..a6fdc67de 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementPathDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementPathDir.java @@ -137,8 +137,8 @@ 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)) { for (final String packageRootPrefix : ClassLoaderHandlerRegistry.AUTOMATIC_PACKAGE_ROOT_PREFIXES) { - final Path packageRootPath = classpathEltPath.resolve(packageRootPrefix); - if (FileUtils.canReadAndIsDir(packageRootPath)) { + final Path packageRoot = classpathEltPath.resolve(packageRootPrefix); + if (FileUtils.canReadAndIsDir(packageRoot)) { if (log != null) { log(classpathElementIdx, "Found package root: " + packageRootPrefix, log); } @@ -233,7 +233,7 @@ public ByteBuffer read() throws IOException { throw new IOException( "Resource is already open -- cannot open it again without first calling close()"); } - pathSlice = new PathSlice(resourcePath, nestedJarHandler, /* log = */ null); + pathSlice = new PathSlice(resourcePath, nestedJarHandler); length = pathSlice.sliceLength; byteBuffer = pathSlice.read(); return byteBuffer; @@ -250,7 +250,7 @@ ClassfileReader openClassfile() throws IOException { "Resource is already open -- cannot open it again without first calling close()"); } // Classfile won't be compressed, so wrap it in a new PathSlice and then open it - pathSlice = new PathSlice(resourcePath, nestedJarHandler, /* log = */ null); + pathSlice = new PathSlice(resourcePath, nestedJarHandler); length = pathSlice.sliceLength; return new ClassfileReader(pathSlice); } @@ -265,7 +265,7 @@ public InputStream open() throws IOException { throw new IOException( "Resource is already open -- cannot open it again without first calling close()"); } - pathSlice = new PathSlice(resourcePath, nestedJarHandler, /* log = */ null); + pathSlice = new PathSlice(resourcePath, nestedJarHandler); inputStream = pathSlice.open(); length = pathSlice.sliceLength; return inputStream; @@ -275,7 +275,7 @@ public InputStream open() throws IOException { public byte[] load() throws IOException { read(); try (Resource res = this) { // Close this after use - pathSlice = new PathSlice(resourcePath, nestedJarHandler, /* log = */ null); + pathSlice = new PathSlice(resourcePath, nestedJarHandler); final byte[] bytes = pathSlice.load(); length = bytes.length; return bytes; 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 dbcdf4741..304faf8b5 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java @@ -456,19 +456,21 @@ private void readCentralDirectory(final NestedJarHandler nestedJarHandler, final if (eocdPos < 0 && slice.sliceLength > 22 + 32) { // If EOCD signature was not found, read the last 64kB of file to RAM in a single chunk // so that we can scan back through it at higher speed to locate the EOCD signature - final int bytesToRead = (int) Math.min(slice.sliceLength, 22L + (1 << 16)); + final int bytesToRead = (int) Math.min(slice.sliceLength, 65536); final byte[] eocdBytes = new byte[bytesToRead]; final long readStartOff = slice.sliceLength - bytesToRead; if (reader.read(readStartOff, eocdBytes, 0, bytesToRead) < bytesToRead) { // Should not happen throw new IOException("Zipfile is truncated"); } - final RandomAccessReader eocdReader = new ArraySlice(eocdBytes, /* isDeflatedZipEntry = */ false, - /* inflatedLengthHint = */ 0L, nestedJarHandler).randomAccessReader(); - for (long i = eocdBytes.length - 22L; i >= 0L; --i) { - if (eocdReader.readInt(i) == 0x06054b50) { - eocdPos = i + readStartOff; - break; + try (final ArraySlice arraySlice = new ArraySlice(eocdBytes, /* isDeflatedZipEntry = */ false, + /* inflatedLengthHint = */ 0L, nestedJarHandler)) { + final RandomAccessReader eocdReader = arraySlice.randomAccessReader(); + for (long i = eocdBytes.length - 22L; i >= 0L; --i) { + if (eocdReader.readInt(i) == 0x06054b50) { + eocdPos = i + readStartOff; + break; + } } } } 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 05afe1ca5..b06781e7c 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/PhysicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/PhysicalZipFile.java @@ -108,7 +108,7 @@ class PhysicalZipFile { this.path = path; this.pathStr = FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, path.toString()); - this.slice = new PathSlice(path, nestedJarHandler, log); + this.slice = new PathSlice(path, nestedJarHandler); } /** 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 5936cc8bb..321e23109 100644 --- a/src/main/java/nonapi/io/github/classgraph/fileslice/PathSlice.java +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/PathSlice.java @@ -43,7 +43,6 @@ import nonapi.io.github.classgraph.fileslice.reader.RandomAccessFileChannelReader; import nonapi.io.github.classgraph.fileslice.reader.RandomAccessReader; import nonapi.io.github.classgraph.utils.FileUtils; -import nonapi.io.github.classgraph.utils.LogNode; /** A {@link Path} slice. */ public class PathSlice extends Slice implements Closeable { @@ -105,13 +104,11 @@ private PathSlice(final PathSlice parentSlice, final long offset, final long len * zip entry. * @param nestedJarHandler * the nested jar handler - * @param log - * the log * @throws IOException * if the file cannot be opened. */ public PathSlice(final Path path, final boolean isDeflatedZipEntry, final long inflatedLengthHint, - final NestedJarHandler nestedJarHandler, final LogNode log) throws IOException { + final NestedJarHandler nestedJarHandler) throws IOException { super(0L, isDeflatedZipEntry, inflatedLengthHint, nestedJarHandler); // Make sure the File is readable and is a regular file @@ -136,14 +133,11 @@ public PathSlice(final Path path, final boolean isDeflatedZipEntry, final long i * the path * @param nestedJarHandler * the nested jar handler - * @param log - * the log * @throws IOException * if the file cannot be opened. */ - public PathSlice(final Path path, final NestedJarHandler nestedJarHandler, final LogNode log) - throws IOException { - this(path, /* isDeflatedZipEntry = */ false, /* inflatedSizeHint = */ 0L, nestedJarHandler, log); + public PathSlice(final Path path, final NestedJarHandler nestedJarHandler) throws IOException { + this(path, /* isDeflatedZipEntry = */ false, /* inflatedSizeHint = */ 0L, nestedJarHandler); } /** From 283432312e6e1d5d05a9d2f273c67bbcd6797f3d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 4 Jun 2020 21:51:50 -0600 Subject: [PATCH 0820/1778] Add unit test (#431) --- .../issues/issue431/Issue431Test.java | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 src/test/java/io/github/classgraph/issues/issue431/Issue431Test.java diff --git a/src/test/java/io/github/classgraph/issues/issue431/Issue431Test.java b/src/test/java/io/github/classgraph/issues/issue431/Issue431Test.java new file mode 100644 index 000000000..9e28ce7dd --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue431/Issue431Test.java @@ -0,0 +1,97 @@ +/* + * 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.issues.issue431; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Objects; + +import org.junit.jupiter.api.Test; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ClassInfo; +import io.github.classgraph.ScanResult; + +/** + * Issue431Test. + */ +public class Issue431Test { + /** Class X */ + public static class X { + /** a */ + static final int a = 1; + /** b */ + static final long b = 2L; + /** c */ + static final short c = (short) 3; + /** d */ + static final char d = 'd'; + /** e */ + static final boolean e = true; + /** f */ + static final byte f = (byte) 10; + /** g */ + static final float g = 1.0f; + /** h */ + static final double h = 1.0d; + } + + /** + * Test field equality. + * + * @param fieldName + * The field name + * @param classInfo1 + * The first ClassInfo + * @param classInfo2 + * The second ClassInfo + */ + private void testFieldEquality(final String fieldName, final ClassInfo classInfo1, final ClassInfo classInfo2) { + assertThat(Objects.equals(classInfo1.getFieldInfo(fieldName).getConstantInitializerValue(), + classInfo2.getFieldInfo(fieldName).getConstantInitializerValue())).isTrue(); + } + + /** Test serializing and deserializing primitive types. */ + @Test + public void primitiveTypeSerialization() { + final ClassGraph classGraph = new ClassGraph().whitelistPackages(Issue431Test.class.getPackage().getName()) + .enableAllInfo(); + try (ScanResult scanResult1 = classGraph.scan()) { + final ClassInfo classInfo1 = scanResult1.getClassInfo(X.class.getName()); + assertThat(classInfo1).isNotNull(); + final String jsonResult = scanResult1.toJSON(2); + final ScanResult scanResult2 = ScanResult.fromJSON(jsonResult); + final ClassInfo classInfo2 = scanResult2.getClassInfo(X.class.getName()); + assertThat(classInfo2).isNotNull(); + for (char fieldName = 'a'; fieldName <= 'h'; fieldName++) { + testFieldEquality("" + fieldName, classInfo1, classInfo2); + } + } + } +} From 93c149b915a7c1503a7fc5d004d8d19a88d41ae2 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 5 Jun 2020 00:53:55 -0600 Subject: [PATCH 0821/1778] Fix recognition of 9-digit numbers as Longs (should be 10 digits) (#431) --- .../java/nonapi/io/github/classgraph/json/JSONParser.java | 6 +++--- .../io/github/classgraph/issues/issue431/Issue431Test.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) 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 b4ba08cea..f9cf8f7ef 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/JSONParser.java +++ b/src/main/java/nonapi/io/github/classgraph/json/JSONParser.java @@ -270,10 +270,10 @@ private Number parseNumber() throws ParseException { final String numberStr = getSubstring(startIdx, endIdx); if (hasFractionalPart || hasExponentPart) { return Double.valueOf(numberStr); - } else if (numIntegralDigits < 9) { + } else if (numIntegralDigits < 10) { return Integer.valueOf(numberStr); - } else if (numIntegralDigits == 9) { - // For 9-digit numbers, could be int or long + } else if (numIntegralDigits == 10) { + // For 10-digit numbers, could be int or long final long longVal = Long.parseLong(numberStr); if (longVal >= Integer.MIN_VALUE && longVal <= Integer.MAX_VALUE) { return (int) longVal; diff --git a/src/test/java/io/github/classgraph/issues/issue431/Issue431Test.java b/src/test/java/io/github/classgraph/issues/issue431/Issue431Test.java index 9e28ce7dd..ab74a69b6 100644 --- a/src/test/java/io/github/classgraph/issues/issue431/Issue431Test.java +++ b/src/test/java/io/github/classgraph/issues/issue431/Issue431Test.java @@ -45,7 +45,7 @@ public class Issue431Test { /** Class X */ public static class X { /** a */ - static final int a = 1; + static final int a = Integer.MAX_VALUE; /** b */ static final long b = 2L; /** c */ From 378892fa042bedeb275b0eee027a94f54b26990a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 5 Jun 2020 00:55:25 -0600 Subject: [PATCH 0822/1778] [maven-release-plugin] prepare release classgraph-4.8.81 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index b9abf679a..6d0d1d0ab 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.81-SNAPSHOT + 4.8.81 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.74 + classgraph-4.8.81 From 67305dcfd5c91f25c672e81a1a14542a6ef69aaa Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 5 Jun 2020 00:55:33 -0600 Subject: [PATCH 0823/1778] [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 6d0d1d0ab..59bccfbdb 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.81 + 4.8.82-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.81 + classgraph-4.8.74 From 936e4caf88f9223ab1fdc1e90db107c424a715e6 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 5 Jun 2020 01:43:32 -0600 Subject: [PATCH 0824/1778] Further fix for deserialization (#431) --- .../nonapi/io/github/classgraph/json/JSONDeserializer.java | 2 +- .../io/github/classgraph/issues/issue431/Issue431Test.java | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) 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 884469dd4..ace423441 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/JSONDeserializer.java +++ b/src/main/java/nonapi/io/github/classgraph/json/JSONDeserializer.java @@ -149,7 +149,7 @@ private static Object jsonBasicValueToObject(final Object jsonVal, final Type ex throw new IllegalArgumentException("Expected float; got " + jsonVal.getClass().getName()); } final double doubleValue = (Double) jsonVal; - if (doubleValue < Float.MIN_VALUE || doubleValue > Float.MAX_VALUE) { + if (doubleValue < -Float.MAX_VALUE || doubleValue > Float.MAX_VALUE) { throw new IllegalArgumentException("Expected float; got out-of-range value " + doubleValue); } return (float) doubleValue; diff --git a/src/test/java/io/github/classgraph/issues/issue431/Issue431Test.java b/src/test/java/io/github/classgraph/issues/issue431/Issue431Test.java index ab74a69b6..47359e314 100644 --- a/src/test/java/io/github/classgraph/issues/issue431/Issue431Test.java +++ b/src/test/java/io/github/classgraph/issues/issue431/Issue431Test.java @@ -59,7 +59,9 @@ public static class X { /** g */ static final float g = 1.0f; /** h */ - static final double h = 1.0d; + static final float h = 0.0f; + /** i */ + static final double i = 1.0d; } /** @@ -89,7 +91,7 @@ public void primitiveTypeSerialization() { final ScanResult scanResult2 = ScanResult.fromJSON(jsonResult); final ClassInfo classInfo2 = scanResult2.getClassInfo(X.class.getName()); assertThat(classInfo2).isNotNull(); - for (char fieldName = 'a'; fieldName <= 'h'; fieldName++) { + for (char fieldName = 'a'; fieldName <= 'i'; fieldName++) { testFieldEquality("" + fieldName, classInfo1, classInfo2); } } From f45bb8ef9ecc039eb76694a451dd9749c54749fa Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 5 Jun 2020 11:41:49 -0600 Subject: [PATCH 0825/1778] Fix Javascript roundtrip discrepancy (#431) --- .../java/nonapi/io/github/classgraph/json/JSONParser.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 f9cf8f7ef..e7aab663a 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/JSONParser.java +++ b/src/main/java/nonapi/io/github/classgraph/json/JSONParser.java @@ -142,7 +142,8 @@ private CharSequence parseString() throws ParseException { while (hasMore()) { final char c = getc(); if (c == '\\') { - switch (getc()) { + final char c2 = getc(); + switch (c2) { case 'b': buf.append('\b'); break; @@ -162,7 +163,7 @@ private CharSequence parseString() throws ParseException { case '"': case '/': case '\\': - buf.append(c); + buf.append(c2); break; case 'u': int charVal = 0; From 29785a0a3afb757c0cd7411eefb0ea370efbfcc7 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 5 Jun 2020 11:43:44 -0600 Subject: [PATCH 0826/1778] Remove tag --- pom.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/pom.xml b/pom.xml index 59bccfbdb..fbde8424c 100644 --- a/pom.xml +++ b/pom.xml @@ -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.74 From 8b1050de52a8b4dd909ef987385e85608c1fec25 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 5 Jun 2020 11:44:32 -0600 Subject: [PATCH 0827/1778] [maven-release-plugin] prepare release classgraph-4.8.82 --- pom.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index fbde8424c..79b79fa20 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.82-SNAPSHOT + 4.8.82 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.82 + https://github.com/classgraph/classgraph/issues From c2a715dd2c07825bd8470a082f00b9c404e291ca Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 5 Jun 2020 11:44:40 -0600 Subject: [PATCH 0828/1778] [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 79b79fa20..86f324058 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.82 + 4.8.83-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.82 + HEAD From 32b4fffdef2071b594d3d070b5631593fcf69e73 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 5 Jun 2020 17:34:35 -0600 Subject: [PATCH 0829/1778] Don't override classpath when restoring ScanResult from JSON (#431) --- src/main/java/io/github/classgraph/ScanResult.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index 6801cab72..35f8fbaae 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -1308,10 +1308,6 @@ public static ScanResult fromJSON(final String json) { // and scans classpath element paths (needed for classloading), but does not scan the actual classfiles final ClassGraph classGraph = new ClassGraph(); classGraph.scanSpec = deserialized.scanSpec; - if (classGraph.scanSpec.overrideClasspath == null) { - // Use the same classpath as before, if classpath was not overridden - classGraph.overrideClasspath(deserialized.classpath); - } final ScanResult scanResult; try (AutoCloseableExecutorService executorService = new AutoCloseableExecutorService( ClassGraph.DEFAULT_NUM_WORKER_THREADS)) { From 3203c980081e20b09e31be40087a3c908302459b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 5 Jun 2020 17:38:21 -0600 Subject: [PATCH 0830/1778] [maven-release-plugin] prepare release classgraph-4.8.83 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 86f324058..51dcb94bb 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.83-SNAPSHOT + 4.8.83 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.83 From 5d6dbc3340108fb0d26f521412cdede09bb6b6c5 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 5 Jun 2020 17:38:28 -0600 Subject: [PATCH 0831/1778] [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 51dcb94bb..7c5e6f007 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.83 + 4.8.84-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.83 + HEAD From 0ab46c966ca05a9db0d7e3bab77cd3b06a61a604 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 6 Jun 2020 06:49:38 -0600 Subject: [PATCH 0832/1778] Update wiki links --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 63148bc14..0207ad79b 100644 --- a/README.md +++ b/README.md @@ -75,11 +75,11 @@ ClassGraph provides a number of important capabilities to the JVM ecosystem: * ClassGraph has the ability to build a model in memory of the entire relatedness graph of all classes, annotations, interfaces, methods and fields that are visible to the JVM. This graph can be [queried in a wide range of ways](https://github.com/classgraph/classgraph/wiki/Code-examples), enabling some degree of *metaprogramming* in JVM languages -- the ability to write code that analyzes or responds to the properties of other code. * ClassGraph reads the classfile bytecode format directly, so it can read all information about classes without loading or initializing them. * ClassGraph is fully compatible with the new JPMS module system (Project Jigsaw / JDK 9+), i.e. it can scan both the traditional classpath and the module path. However, the code is also fully backwards compatible with JDK 7 and JDK 8 (i.e. the code is compiled in Java 7 compatibility mode, and all interaction with the module system is implemented via reflection for backwards compatibility). -* ClassGraph scans the classpath or module path using [carefully optimized multithreaded code](https://github.com/classgraph/classgraph/wiki/How-fast-is-ClassGraph%3F) for the shortest possible scan times, and it runs as close as possible to I/O bandwidth limits, even on a fast SSD. +* ClassGraph scans the classpath or module path using [carefully optimized multithreaded code](https://github.com/classgraph/classgraph/wiki/How-fast-is-ClassGraph) for the shortest possible scan times, and it runs as close as possible to I/O bandwidth limits, even on a fast SSD. * ClassGraph handles more [classpath specification mechanisms](https://github.com/classgraph/classgraph/wiki/Classpath-specification-mechanisms) found in the wild than any other classpath scanner, making code that depends upon ClassGraph maximally portable. * ClassGraph can scan the classpath and module path either at runtime or [at build time](https://github.com/classgraph/classgraph/wiki/Build-Time-Scanning) (e.g. to implement annotation processing for Android). * ClassGraph can [find classes that are duplicated or defined more than once in the classpath or module path](https://github.com/classgraph/classgraph/wiki/Code-examples#find-all-duplicate-class-definitions-in-the-classpath-or-module-path), which can help find the cause of strange class resolution behaviors. -* ClassGraph can [create GraphViz visualizations of the class graph structure](https://github.com/classgraph/classgraph/wiki/API:-ClassInfo#generating-a-graphviz-dot-file-for-class-graph-visualization), which can help with code understanding: (click to enlarge | [see graph legend here](https://github.com/classgraph/classgraph/blob/master/src/test/java/com/xyz/classgraph-fig-legend.png)) +* ClassGraph can [create GraphViz visualizations of the class graph structure](https://github.com/classgraph/classgraph/wiki/ClassInfo-API#generating-a-graphviz-dot-file-for-class-graph-visualization), which can help with code understanding: (click to enlarge | [see graph legend here](https://github.com/classgraph/classgraph/blob/master/src/test/java/com/xyz/classgraph-fig-legend.png))

Class graph visualization From 304ff97b68f44d33afef352d9cde86b53a97fa0a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 9 Jun 2020 05:30:28 -0600 Subject: [PATCH 0833/1778] Replace deprecated method call --- .../classgraph/test/ClassGraphTest.java | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/test/java/io/github/classgraph/test/ClassGraphTest.java b/src/test/java/io/github/classgraph/test/ClassGraphTest.java index e74813dda..094e4eeb2 100644 --- a/src/test/java/io/github/classgraph/test/ClassGraphTest.java +++ b/src/test/java/io/github/classgraph/test/ClassGraphTest.java @@ -30,6 +30,7 @@ import static org.assertj.core.api.Assertions.assertThat; +import java.io.IOException; import java.util.HashSet; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; @@ -39,7 +40,7 @@ import io.github.classgraph.ClassGraph; import io.github.classgraph.FieldInfo; import io.github.classgraph.Resource; -import io.github.classgraph.ResourceList.ByteArrayConsumer; +import io.github.classgraph.ResourceList.ByteArrayConsumerThrowsIOException; import io.github.classgraph.ScanResult; import io.github.classgraph.test.blacklisted.BlacklistedAnnotation; import io.github.classgraph.test.blacklisted.BlacklistedSubclass; @@ -362,12 +363,17 @@ public void testVisibleIfNotBlacklisted() { public void scanFilePattern() { final AtomicBoolean readFileContents = new AtomicBoolean(false); try (ScanResult scanResult = new ClassGraph().whitelistPathsNonRecursive("").scan()) { - scanResult.getResourcesWithLeafName("file-content-test.txt").forEachByteArray(new ByteArrayConsumer() { - @Override - public void accept(final Resource res, final byte[] arr) { - readFileContents.set(new String(arr).equals("File contents")); - } - }); + try { + scanResult.getResourcesWithLeafName("file-content-test.txt") + .forEachByteArrayThrowingIOException(new ByteArrayConsumerThrowsIOException() { + @Override + public void accept(Resource resource, byte[] byteArray) throws IOException { + readFileContents.set(new String(byteArray).equals("File contents")); + } + }); + } catch (IOException e) { + throw new RuntimeException(e); + } assertThat(readFileContents.get()).isTrue(); } } From 61426321be8c945e2569c3ad16966da9e5e1008c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 9 Jun 2020 05:42:08 -0600 Subject: [PATCH 0834/1778] Fix URL scheme prefix handling (#434) --- .../classgraph/utils/FastPathResolver.java | 54 ++++++++++--------- .../issues/issue46/Issue46Test.java | 3 +- 2 files changed, 31 insertions(+), 26 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 b80851085..436543ede 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FastPathResolver.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FastPathResolver.java @@ -205,47 +205,33 @@ public static String resolve(final String resolveBasePath, final String relative startIdx = 4; isFileOrJarURL = true; } - if (relativePath.regionMatches(true, 0, "http://", 0, 7)) { + if (relativePath.regionMatches(true, startIdx, "http://", 0, 7)) { // Detect http:// - startIdx = 7; + 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, 0, "https://", 0, 8)) { + } else if (relativePath.regionMatches(true, startIdx, "https://", 0, 8)) { // Detect https:// - startIdx = 8; + startIdx += 8; prefix = "https://"; isAbsolutePath = true; - } else if (relativePath.regionMatches(true, 0, "jrt:", 0, 5)) { + } else if (relativePath.regionMatches(true, startIdx, "jrt:", 0, 5)) { // Detect jrt: - startIdx = 4; + startIdx += 4; prefix = "jrt:"; isAbsolutePath = true; - } else if (relativePath.regionMatches(true, 0, "file:", 0, 5)) { + } else if (relativePath.regionMatches(true, startIdx, "file:", 0, 5)) { // Strip off any "file:" prefix from relative path - startIdx = 5; - if (WINDOWS) { - if (relativePath.startsWith("\\\\\\\\", startIdx) || relativePath.startsWith("////", startIdx)) { - // Windows UNC URL - startIdx += 4; - prefix = "//"; - isAbsolutePath = true; - } else { - if (relativePath.startsWith("\\\\", startIdx)) { - startIdx += 2; - } - } - } - if (relativePath.startsWith("///", startIdx)) { - startIdx += 2; - } + startIdx += 5; isFileOrJarURL = true; } else { // Preserve the number of slashes on custom URL schemes (#420) - final Matcher m2 = schemeTwoSlashMatcher.matcher(relativePath); + 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(); @@ -254,7 +240,7 @@ public static String resolve(final String resolveBasePath, final String relative // as a directory relative to the current directory. isAbsolutePath = true; } else { - final Matcher m1 = schemeOneSlashMatcher.matcher(relativePath); + final Matcher m1 = schemeOneSlashMatcher.matcher(relPath); if (m1.find()) { final String m1Match = m1.group(); startIdx += m1Match.length(); @@ -263,6 +249,24 @@ public static String resolve(final String resolveBasePath, final String relative } } } + 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)) { + startIdx += 2; + } + } // Handle Windows paths starting with a drive designation as an absolute path if (WINDOWS) { 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 bad44d667..a76cb62fa 100644 --- a/src/test/java/io/github/classgraph/issues/issue46/Issue46Test.java +++ b/src/test/java/io/github/classgraph/issues/issue46/Issue46Test.java @@ -44,7 +44,8 @@ public class Issue46Test { */ @Test public void issue46Test() { - final String jarPath = Issue46Test.class.getClassLoader().getResource("nested-jars-level1.zip").getPath() + 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()) { assertThat(scanResult.getAllClasses().getNames()).containsOnly("com.test.Test"); From 3f8a52f062c367d6c4e841c277dc9d5621915779 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 9 Jun 2020 05:42:18 -0600 Subject: [PATCH 0835/1778] Source > Format --- src/test/java/io/github/classgraph/test/ClassGraphTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/io/github/classgraph/test/ClassGraphTest.java b/src/test/java/io/github/classgraph/test/ClassGraphTest.java index 094e4eeb2..2d524eaa9 100644 --- a/src/test/java/io/github/classgraph/test/ClassGraphTest.java +++ b/src/test/java/io/github/classgraph/test/ClassGraphTest.java @@ -367,11 +367,11 @@ public void scanFilePattern() { scanResult.getResourcesWithLeafName("file-content-test.txt") .forEachByteArrayThrowingIOException(new ByteArrayConsumerThrowsIOException() { @Override - public void accept(Resource resource, byte[] byteArray) throws IOException { + public void accept(final Resource resource, final byte[] byteArray) throws IOException { readFileContents.set(new String(byteArray).equals("File contents")); } }); - } catch (IOException e) { + } catch (final IOException e) { throw new RuntimeException(e); } assertThat(readFileContents.get()).isTrue(); From 8f62ac7d7827131a285212370d26ecf203f0afda Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 9 Jun 2020 05:49:20 -0600 Subject: [PATCH 0836/1778] Replace deprecated method --- .../classgraph/issues/issue255/Issue255Test.java | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/test/java/io/github/classgraph/issues/issue255/Issue255Test.java b/src/test/java/io/github/classgraph/issues/issue255/Issue255Test.java index a417f9210..eab0b948f 100644 --- a/src/test/java/io/github/classgraph/issues/issue255/Issue255Test.java +++ b/src/test/java/io/github/classgraph/issues/issue255/Issue255Test.java @@ -30,12 +30,12 @@ import static org.assertj.core.api.Assertions.assertThat; +import java.io.IOException; + import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; -import io.github.classgraph.Resource; import io.github.classgraph.ResourceList; -import io.github.classgraph.ResourceList.ByteArrayConsumer; import io.github.classgraph.ScanResult; /** @@ -46,19 +46,15 @@ public class Issue255Test { * Issue 255 test. */ @Test - public void issue255Test() { + public void issue255Test() throws IOException { final String dirPath = Issue255Test.class.getClassLoader().getResource("issue255").getPath() + "/test%20percent%20encoding"; try (ScanResult scanResult = new ClassGraph().overrideClasspath(dirPath).scan()) { final ResourceList resources = scanResult.getAllResources(); assertThat(resources.size()).isEqualTo(1); - resources.forEachByteArray(new ByteArrayConsumer() { - @Override - public void accept(final Resource resource, final byte[] byteArray) { - assertThat(new String(byteArray)).isEqualTo("Issue 255"); - } - }); + resources.forEachByteArrayThrowingIOException( + (resource, byteArray) -> assertThat(new String(byteArray)).isEqualTo("Issue 255")); } } } From 2d99caf91126cd589c33a200f9d76b777ca71c39 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 9 Jun 2020 05:50:41 -0600 Subject: [PATCH 0837/1778] Replace deprecated method --- .../classgraph/features/MultiReleaseJarTest.java | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/test/java/io/github/classgraph/features/MultiReleaseJarTest.java b/src/test/java/io/github/classgraph/features/MultiReleaseJarTest.java index f1955ea20..cbe10df88 100644 --- a/src/test/java/io/github/classgraph/features/MultiReleaseJarTest.java +++ b/src/test/java/io/github/classgraph/features/MultiReleaseJarTest.java @@ -11,9 +11,7 @@ import io.github.classgraph.ClassGraph; import io.github.classgraph.ClassInfo; -import io.github.classgraph.Resource; import io.github.classgraph.ResourceList; -import io.github.classgraph.ResourceList.ByteArrayConsumer; import io.github.classgraph.ScanResult; import nonapi.io.github.classgraph.utils.VersionFinder; @@ -57,12 +55,8 @@ public void multiReleaseJar() throws Exception { final ResourceList resources = scanResult.getResourcesWithPath("resource.txt"); assertThat(resources.size()).isEqualTo(1); - resources.forEachByteArray(new ByteArrayConsumer() { - @Override - public void accept(final Resource resource, final byte[] byteArray) { - assertThat(new String(byteArray).trim()).isEqualTo("9"); - } - }); + resources.forEachByteArrayThrowingIOException( + (resource, byteArray) -> assertThat(new String(byteArray).trim()).isEqualTo("9")); } } } From 8fc46cf6eb8f445f4295b9583774531afdbb8246 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 9 Jun 2020 06:00:44 -0600 Subject: [PATCH 0838/1778] Fix `jrt:` URLs (#433) --- src/main/java/io/github/classgraph/Resource.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/classgraph/Resource.java b/src/main/java/io/github/classgraph/Resource.java index ec4db36ee..0263904ad 100644 --- a/src/main/java/io/github/classgraph/Resource.java +++ b/src/main/java/io/github/classgraph/Resource.java @@ -124,8 +124,10 @@ public URI getURI() { // Check if this is a directory-based module (location URI will end in "/") final boolean isDir = locationURIStr.endsWith("/"); try { - return new URI((isDir || locationURIStr.startsWith("jar:") ? "" : "jar:") + locationURIStr - + (isDir ? "" : "!/") + URLPathEncoder.encodePath(resourcePath)); + return new URI( + (isDir || locationURIStr.startsWith("jar:") || locationURIStr.startsWith("jrt:") ? "" : "jar:") + + locationURIStr + (isDir ? "" : locationURIStr.startsWith("jrt:") ? "/" : "!/") + + URLPathEncoder.encodePath(resourcePath)); } catch (final URISyntaxException e) { throw new IllegalArgumentException("Could not form URL for classpath element: " + locationURIStr + " ; path: " + resourcePath + " : " + e); From f6de6c004c7e957c8353c2abfeee65902ab9d192 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 9 Jun 2020 06:02:14 -0600 Subject: [PATCH 0839/1778] [maven-release-plugin] prepare release classgraph-4.8.84 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 7c5e6f007..405295f75 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.84-SNAPSHOT + 4.8.84 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.84 From 5f07f35c17d1011e4deb37c2ae2e973a7deba930 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 9 Jun 2020 06:02:22 -0600 Subject: [PATCH 0840/1778] [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 405295f75..36b9527ff 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.84 + 4.8.85-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.84 + HEAD From 20597b2fdb2268a5e7dbc98933f2a4cd43496b1e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 9 Jun 2020 06:20:54 -0600 Subject: [PATCH 0841/1778] Set `fail-fast:false` --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bea7d9498..47dfd71b2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,6 +12,7 @@ jobs: build: runs-on: ${{ matrix.os }} strategy: + fail-fast: false matrix: os: [ ubuntu-latest, windows-latest, macos-latest ] java: [ '1.8.0', '11.0.x', '12.0.x', '13.0.x' ] From 7c45b264404f8a45e321570a9ab3836af69b5e34 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 10 Jun 2020 12:58:37 -0600 Subject: [PATCH 0842/1778] Get rid of unnecessary path "BOOT-INF/lib-provided/" --- .../classloaderhandler/ClassLoaderHandlerRegistry.java | 3 ++- 1 file changed, 2 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 5b57b2a7b..57b82a284 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java @@ -84,7 +84,8 @@ public class ClassLoaderHandlerRegistry { */ public static final String[] AUTOMATIC_LIB_DIR_PREFIXES = { // Spring-Boot - "BOOT-INF/lib/", "BOOT-INF/lib-provided/", + // https://docs.spring.io/spring-boot/docs/2.3.0.RELEASE/reference/html/appendix-executable-jar-format.html + "BOOT-INF/lib/", // Tomcat "WEB-INF/lib/", "WEB-INF/lib-provided/", // OSGi From 58d4457abc5461fc6b198b3b216cb345d94b9a9d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 10 Jun 2020 14:36:35 -0600 Subject: [PATCH 0843/1778] Strip automatic package root suffix if present (#435) --- .../java/io/github/classgraph/Scanner.java | 2 +- .../classgraph/classpath/ClasspathOrder.java | 89 ++++++++++++++----- 2 files changed, 70 insertions(+), 21 deletions(-) diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 95b2d5909..7d8f89217 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -473,7 +473,7 @@ public ClasspathElement newInstance(final ClasspathElementAndClassLoader classpa } } else { - // classpathEntryObj is a string + // Convert classpathEntryObj to a string classpathEntryPathStr = classpathEntryObj.toString(); } // Fall through for file paths 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 fab2aa997..8c666edef 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java @@ -32,8 +32,11 @@ import java.lang.reflect.Array; import java.net.MalformedURLException; import java.net.URI; +import java.net.URISyntaxException; import java.net.URL; +import java.nio.file.InvalidPathException; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -41,6 +44,7 @@ import java.util.Set; import io.github.classgraph.ClassGraph.ClasspathElementFilter; +import nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandlerRegistry; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.FastPathResolver; import nonapi.io.github.classgraph.utils.FileUtils; @@ -58,11 +62,22 @@ public class ClasspathOrder { /** The classpath order. Keys are instances of {@link String} or {@link URL}. */ 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<>(); + + static { + for (final String prefix : ClassLoaderHandlerRegistry.AUTOMATIC_PACKAGE_ROOT_PREFIXES) { + AUTOMATIC_PACKAGE_ROOT_SUFFIXES.add("!/" + prefix.substring(0, prefix.length() - 1)); + } + } + /** * A classpath element and the {@link ClassLoader} it was obtained from. */ public static class ClasspathElementAndClassLoader { - /** The classpath element root (a {@link String} or {@link URL} or {@link Path}). */ + /** + * The classpath element root (a {@link String} path, {@link Path}, {@link URL} or {@link URI}). + */ public final Object classpathElementRoot; /** The classpath element package root, prefix, e.g. "BOOT-INF/classes" or "". */ @@ -75,7 +90,7 @@ public static class ClasspathElementAndClassLoader { * Constructor for directory or {@link Path} classpath entries. * * @param classpathElementRoot - * the classpath element root (a {@link String} or {@link URL} or {@link Path}). + * the classpath element root (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. @@ -120,6 +135,11 @@ public boolean equals(final Object obj) { && Objects.equals(this.classpathElementRoot, other.classpathElementRoot) && Objects.equals(this.classLoader, other.classLoader); } + + @Override + public String toString() { + return classpathElementRoot + " [" + classLoader + "]"; + } } /** @@ -173,7 +193,7 @@ private boolean filter(final String classpathElementPath) { * * @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.CURR_DIR_PATH, path) * @param classLoader * the classloader * @return true, if added and unique @@ -190,9 +210,8 @@ boolean addSystemClasspathEntry(final String pathEntry, final ClassLoader classL * Add a classpath entry. * * @param pathElement - * the {@link String} path, {@link URL} or {@link URI} of the classpath element. If a string, the - * path string should already have been run through FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, - * path) + * the {@link String} path, {@link File}, {@link Path}, {@link URL} or {@link URI} of the classpath + * element. * @param pathElementStr * the path element in string format * @param classLoader @@ -203,22 +222,51 @@ boolean addSystemClasspathEntry(final String pathEntry, final ClassLoader classL */ private boolean addClasspathEntry(final Object pathElement, final String pathElementStr, final ClassLoader classLoader, final ScanSpec scanSpec) { - if (pathElement instanceof URL || pathElement instanceof URI) { - // Assume that any custom URLs or URIs passed in are not in lib or ext dir, and are not system jars - if (classpathEntryUniqueResolvedPaths.add(pathElementStr)) { - order.add(new ClasspathElementAndClassLoader(pathElement, classLoader)); + // Check if classpath element path ends with an automatic package root. If so, strip it off to + // eliminate duplication, since automatic package roots are detected automatically (#435) + String pathElementStrWithoutSuffix = pathElementStr; + boolean hasSuffix = false; + for (final String suffix : AUTOMATIC_PACKAGE_ROOT_SUFFIXES) { + if (pathElementStr.endsWith(suffix)) { + // Strip off automatic package root suffix + pathElementStrWithoutSuffix = pathElementStr.substring(0, + pathElementStr.length() - suffix.length()); + hasSuffix = true; + break; + } + } + if (pathElement instanceof URL || pathElement instanceof URI || pathElement instanceof Path + || pathElement instanceof File) { + Object pathElementWithoutSuffix = pathElement; + if (hasSuffix) { + try { + pathElementWithoutSuffix = pathElement instanceof URL ? new URL(pathElementStrWithoutSuffix) + : pathElement instanceof URI ? new URI(pathElementStrWithoutSuffix) + : pathElement instanceof Path ? Paths.get(pathElementStrWithoutSuffix) + // For File, just use path string + : pathElementStrWithoutSuffix; + } catch (MalformedURLException | URISyntaxException | InvalidPathException e) { + return false; + } + } + // Deduplicate classpath elements + if (classpathEntryUniqueResolvedPaths.add(pathElementStrWithoutSuffix)) { + // Record classpath element in classpath order + order.add(new ClasspathElementAndClassLoader(pathElementWithoutSuffix, classLoader)); return true; } } else { + final String pathElementStrResolved = FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, + pathElementStrWithoutSuffix); if (scanSpec.overrideClasspath == null // - && (SystemJarFinder.getJreLibOrExtJars().contains(pathElementStr) - || pathElementStr.equals(SystemJarFinder.getJreRtJarPath()))) { + && (SystemJarFinder.getJreLibOrExtJars().contains(pathElementStrResolved) + || pathElementStrResolved.equals(SystemJarFinder.getJreRtJarPath()))) { // JRE lib and ext jars are handled separately, so reject them as duplicates if they are // returned by a system classloader return false; } - if (classpathEntryUniqueResolvedPaths.add(pathElementStr)) { - order.add(new ClasspathElementAndClassLoader(pathElementStr, classLoader)); + if (classpathEntryUniqueResolvedPaths.add(pathElementStrResolved)) { + order.add(new ClasspathElementAndClassLoader(pathElementStrResolved, classLoader)); return true; } } @@ -246,7 +294,7 @@ public boolean addClasspathEntry(final Object pathElement, final ClassLoader cla if (pathElement == null) { return false; } - final String pathElementStr = pathElement.toString(); + final String pathElementStr = FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, pathElement.toString()); if (pathElementStr.isEmpty()) { return false; } @@ -448,10 +496,10 @@ public boolean addClasspathPathStr(final String pathStr, final ClassLoader class /** * Add classpath entries from an object obtained from reflection. The object may be a {@link URL}, a - * {@link URI}, a {@link File} or a {@link String} (containing a single classpath element path, or several paths - * separated with File.pathSeparator), a List or other Iterable, or an array object. In the case of Iterables - * and arrays, the elements may be any type whose {@code toString()} method returns a path or URL string - * (including the {@code URL} and {@code Path} types). + * {@link URI}, a {@link File}, a {@link Path} or a {@link String} (containing a single classpath element path, + * or several paths separated with File.pathSeparator), a List or other Iterable, or an array object. In the + * case of Iterables and arrays, the elements may be any type whose {@code toString()} method returns a path or + * URL string (including the {@code URL} and {@code Path} types). * * @param pathObject * the object containing a classpath string or strings. @@ -467,7 +515,8 @@ public boolean addClasspathEntryObject(final Object pathObject, final ClassLoade final ScanSpec scanSpec, final LogNode log) { boolean valid = false; if (pathObject != null) { - if (pathObject instanceof URL || pathObject instanceof URI) { + if (pathObject instanceof URL || pathObject instanceof URI || pathObject instanceof Path + || pathObject instanceof File) { valid |= addClasspathEntry(pathObject, classLoader, scanSpec, log); } else if (pathObject instanceof Iterable) { for (final Object elt : (Iterable) pathObject) { From 1362b24cc8bc780adf51bfd065db6c7b88f5ce2a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 10 Jun 2020 14:37:46 -0600 Subject: [PATCH 0844/1778] [maven-release-plugin] prepare release classgraph-4.8.85 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 36b9527ff..7295566b9 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.85-SNAPSHOT + 4.8.85 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.85 From 4e8ae6482ac57210bfdf263b8952304f86e551da Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 10 Jun 2020 14:37:53 -0600 Subject: [PATCH 0845/1778] [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 7295566b9..f789b5c15 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.85 + 4.8.86-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.85 + HEAD From 1f81a3d58f7e888005fde98d537ad691d26404d8 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 12 Jun 2020 05:34:09 -0600 Subject: [PATCH 0846/1778] Whitelist -> Accept; Blacklist -> Reject --- pom.xml | 2 +- .../java/io/github/classgraph/ClassGraph.java | 476 +++++++++++++----- .../java/io/github/classgraph/ClassInfo.java | 127 ++--- .../io/github/classgraph/ClassInfoList.java | 2 +- .../java/io/github/classgraph/Classfile.java | 34 +- .../github/classgraph/ClasspathElement.java | 103 ++-- .../classgraph/ClasspathElementFileDir.java | 48 +- .../classgraph/ClasspathElementModule.java | 36 +- .../classgraph/ClasspathElementPathDir.java | 48 +- .../classgraph/ClasspathElementZip.java | 44 +- .../classgraph/ObjectTypedValueWrapper.java | 16 +- .../java/io/github/classgraph/Resource.java | 4 +- .../java/io/github/classgraph/ScanResult.java | 169 ++++--- .../java/io/github/classgraph/Scanner.java | 66 ++- .../classgraph/classpath/ClasspathFinder.java | 8 +- .../fastzipfilereader/ZipFileSlice.java | 21 +- ...{WhiteBlackList.java => AcceptReject.java} | 416 +++++++-------- .../github/classgraph/scanspec/ScanSpec.java | 182 +++---- .../java/ClassGraphGraphVizGenerator.java | 2 +- src/test/java/DefaultPackageTest.java | 20 +- .../java/DisableRecursiveScanningTest.java | 10 +- .../com/xyz/GenerateClassGraphFigDotFile.java | 2 +- src/test/java/com/xyz/MetaAnnotationTest.java | 2 +- src/test/java/com/xyz/ScanEverything.java | 2 +- .../features/AnnotationEqualityTest.java | 2 +- ...ationParamWithPrimitiveTypedArrayTest.java | 3 +- .../features/DeclaredVsNonDeclaredTest.java | 8 +- .../MethodParameterAnnotationsTest.java | 2 +- .../features/MultiReleaseJarTest.java | 6 +- .../issues/DeclaredVsNonDeclaredTest.java | 4 +- .../issues/GenericInnerClassTypedField.java | 3 +- .../github/classgraph/issues/IssuesTest.java | 17 +- .../issues/ResolveTypeVariableTest.java | 2 +- .../TestGetUniqueClasspathElements.java | 2 +- .../issues/issue100/Issue100Test.java | 8 +- .../issues/issue101/Issue101Test.java | 6 +- .../issues/issue107/Issue107Test.java | 2 +- .../issues/issue140/Issue140Test.java | 2 +- .../issues/issue146/Issue146Test.java | 2 +- .../issues/issue148/Issue148Test.java | 2 +- .../issues/issue151/Issue151Test.java | 2 +- .../issues/issue152/Issue152Test.java | 2 +- .../issues/issue153/Issue153Test.java | 2 +- .../issues/issue167/Issue167Test.java | 4 +- .../issues/issue171/Issue171Test.java | 2 +- .../issues/issue175/Issue175Test.java | 18 +- .../issues/issue193/Issue193Test.java | 2 +- .../issues/issue209/Issue209Test.java | 2 +- .../issues/issue216/Issue216Test.java | 2 +- .../issues/issue223/Issue223Test.java | 2 +- .../issues/issue238/Issue238Test.java | 2 +- .../issues/issue245/Issue245Test.java | 2 +- .../issues/issue246/Issue246Test.java | 2 +- .../issues/issue260/Issue260Test.java | 2 +- .../issues/issue261/Issue261Test.java | 5 +- ...lassLoadingWorksWithParentLastLoaders.java | 2 +- .../issues/issue277/Issue227Test.java | 12 +- .../issues/issue303/Issue303Test.java | 4 +- .../classgraph/issues/issue310/Issue310.java | 2 +- .../classgraph/issues/issue314/Issue314.java | 2 +- .../classgraph/issues/issue318/Issue318.java | 2 +- .../classgraph/issues/issue329/Issue329.java | 2 +- .../classgraph/issues/issue339/Issue339.java | 2 +- .../classgraph/issues/issue345/Issue345.java | 16 +- .../classgraph/issues/issue348/Issue348.java | 4 +- .../classgraph/issues/issue350/Issue350.java | 4 +- .../classgraph/issues/issue352/Issue352.java | 14 +- .../classgraph/issues/issue355/Issue355.java | 2 +- .../issues/issue368/Issue368Test.java | 2 +- .../issues/issue37/Issue37Test.java | 2 +- .../issues/issue370/Issue370Test.java | 2 +- .../issues/issue38/Issue38Test.java | 2 +- .../issues/issue407/Issue407Test.java | 2 +- .../issues/issue420/Issue420Test.java | 4 +- .../issues/issue431/Issue431Test.java | 2 +- .../issues/issue74/Issue74Test.java | 2 +- .../issues/issue78/Issue78Test.java | 2 +- .../issues/issue83/Issue83Test.java | 12 +- .../classgraph/issues/issue93/Issue93.java | 4 +- .../issues/issue99/Issue99Test.java | 10 +- .../json/JSONSerializationTest.java | 2 +- .../classgraph/test/ClassGraphTest.java | 232 +++++---- .../github/classgraph/test/ClassInfoTest.java | 24 +- .../classgraph/test/accepted/Accepted.java | 12 + .../test/accepted/AcceptedInterface.java | 9 + .../github/classgraph/test/accepted/Cls.java | 7 + .../{whitelisted => accepted}/ClsSub.java | 2 +- .../{whitelisted => accepted}/ClsSubSub.java | 2 +- .../HasFieldWithTypeCls.java | 2 +- .../test/{whitelisted => accepted}/Iface.java | 2 +- .../{whitelisted => accepted}/IfaceSub.java | 2 +- .../IfaceSubSub.java | 2 +- .../test/{whitelisted => accepted}/Impl1.java | 2 +- .../{whitelisted => accepted}/Impl1Sub.java | 2 +- .../Impl1SubSub.java | 2 +- .../test/{whitelisted => accepted}/Impl2.java | 2 +- .../{whitelisted => accepted}/Impl2Sub.java | 2 +- .../Impl2SubSub.java | 2 +- .../StaticField.java | 2 +- .../accepted/rejectedsub/RejectedSub.java | 7 + .../blacklisted/BlacklistedInterface.java | 7 - .../test/blacklisted/BlacklistedSubclass.java | 9 - .../blacklisted/BlacklistedSubinterface.java | 9 - .../blacklisted/BlacklistedSuperclass.java | 7 - .../AnnotationClassRefTest.java | 2 +- .../FieldAndMethodAnnotationTest.java | 6 +- .../test/fieldinfo/FieldInfoTest.java | 6 +- .../test/internal/InternalExternalTest.java | 16 +- .../MethodAnnotationTest.java | 4 +- .../TestMethodMetaAnnotation.java | 6 +- .../test/methodinfo/MethodInfoTest.java | 10 +- ...cyForFunctionParameterAnnotationsTest.java | 2 +- .../RejectedAnnotation.java} | 6 +- .../test/rejected/RejectedInterface.java | 7 + .../test/rejected/RejectedSubclass.java | 9 + .../test/rejected/RejectedSubinterface.java | 9 + .../test/rejected/RejectedSuperclass.java | 7 + .../classgraph/test/whitelisted/Cls.java | 7 - .../test/whitelisted/Whitelisted.java | 12 - .../whitelisted/WhitelistedInterface.java | 9 - .../blacklistedsub/BlacklistedSub.java | 7 - 121 files changed, 1379 insertions(+), 1173 deletions(-) rename src/main/java/nonapi/io/github/classgraph/scanspec/{WhiteBlackList.java => AcceptReject.java} (52%) create mode 100644 src/test/java/io/github/classgraph/test/accepted/Accepted.java create mode 100644 src/test/java/io/github/classgraph/test/accepted/AcceptedInterface.java create mode 100644 src/test/java/io/github/classgraph/test/accepted/Cls.java rename src/test/java/io/github/classgraph/test/{whitelisted => accepted}/ClsSub.java (54%) rename src/test/java/io/github/classgraph/test/{whitelisted => accepted}/ClsSubSub.java (58%) rename src/test/java/io/github/classgraph/test/{whitelisted => accepted}/HasFieldWithTypeCls.java (96%) rename src/test/java/io/github/classgraph/test/{whitelisted => accepted}/Iface.java (56%) rename src/test/java/io/github/classgraph/test/{whitelisted => accepted}/IfaceSub.java (62%) rename src/test/java/io/github/classgraph/test/{whitelisted => accepted}/IfaceSubSub.java (65%) rename src/test/java/io/github/classgraph/test/{whitelisted => accepted}/Impl1.java (58%) rename src/test/java/io/github/classgraph/test/{whitelisted => accepted}/Impl1Sub.java (56%) rename src/test/java/io/github/classgraph/test/{whitelisted => accepted}/Impl1SubSub.java (60%) rename src/test/java/io/github/classgraph/test/{whitelisted => accepted}/Impl2.java (55%) rename src/test/java/io/github/classgraph/test/{whitelisted => accepted}/Impl2Sub.java (56%) rename src/test/java/io/github/classgraph/test/{whitelisted => accepted}/Impl2SubSub.java (66%) rename src/test/java/io/github/classgraph/test/{whitelisted => accepted}/StaticField.java (93%) create mode 100644 src/test/java/io/github/classgraph/test/accepted/rejectedsub/RejectedSub.java delete mode 100644 src/test/java/io/github/classgraph/test/blacklisted/BlacklistedInterface.java delete mode 100644 src/test/java/io/github/classgraph/test/blacklisted/BlacklistedSubclass.java delete mode 100644 src/test/java/io/github/classgraph/test/blacklisted/BlacklistedSubinterface.java delete mode 100644 src/test/java/io/github/classgraph/test/blacklisted/BlacklistedSuperclass.java rename src/test/java/io/github/classgraph/test/{blacklisted/BlacklistedAnnotation.java => rejected/RejectedAnnotation.java} (64%) create mode 100644 src/test/java/io/github/classgraph/test/rejected/RejectedInterface.java create mode 100644 src/test/java/io/github/classgraph/test/rejected/RejectedSubclass.java create mode 100644 src/test/java/io/github/classgraph/test/rejected/RejectedSubinterface.java create mode 100644 src/test/java/io/github/classgraph/test/rejected/RejectedSuperclass.java delete mode 100644 src/test/java/io/github/classgraph/test/whitelisted/Cls.java delete mode 100644 src/test/java/io/github/classgraph/test/whitelisted/Whitelisted.java delete mode 100644 src/test/java/io/github/classgraph/test/whitelisted/WhitelistedInterface.java delete mode 100644 src/test/java/io/github/classgraph/test/whitelisted/blacklistedsub/BlacklistedSub.java diff --git a/pom.xml b/pom.xml index f789b5c15..5ae1406af 100644 --- a/pom.xml +++ b/pom.xml @@ -233,7 +233,7 @@ - + org.apache.maven.plugins maven-enforcer-plugin diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index 44f5eaca0..1a46b6b9a 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -48,8 +48,8 @@ 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.scanspec.AcceptReject; import nonapi.io.github.classgraph.scanspec.ScanSpec; -import nonapi.io.github.classgraph.scanspec.WhiteBlackList; import nonapi.io.github.classgraph.utils.JarUtils; import nonapi.io.github.classgraph.utils.LogNode; import nonapi.io.github.classgraph.utils.VersionFinder; @@ -360,9 +360,9 @@ public ClassGraph disableModuleScanning() { // ------------------------------------------------------------------------------------------------------------- /** - * Causes ClassGraph to return classes that are not in the whitelisted packages, but that are directly referred - * to by classes within whitelisted packages as a superclass, implemented interface or annotation. - * (Automatically calls {@link #enableClassInfo()}.) + * Causes ClassGraph to return classes that are not in the accepted packages, but that are directly referred to + * by classes within accepted packages as a superclass, implemented interface or annotation. (Automatically + * calls {@link #enableClassInfo()}.) * * @return this (for method chaining). */ @@ -598,7 +598,7 @@ public ClassGraph ignoreParentModuleLayers() { * Scan one or more specific packages and their sub-packages. * *

- * N.B. Automatically calls {@link #enableClassInfo()} -- call {@link #whitelistPaths(String...)} instead if you + * N.B. Automatically calls {@link #enableClassInfo()} -- call {@link #acceptPaths(String...)} instead if you * only need to scan resources. * * @param packageNames @@ -606,35 +606,48 @@ public ClassGraph ignoreParentModuleLayers() { * wildcard ({@code '*'}). * @return this (for method chaining). */ - public ClassGraph whitelistPackages(final String... packageNames) { + public ClassGraph acceptPackages(final String... packageNames) { enableClassInfo(); for (final String packageName : packageNames) { - final String packageNameNormalized = WhiteBlackList.normalizePackageOrClassName(packageName); + final String packageNameNormalized = AcceptReject.normalizePackageOrClassName(packageName); if (packageNameNormalized.startsWith("!") || packageNameNormalized.startsWith("-")) { throw new IllegalArgumentException( - "This style of whitelisting/blacklisting is no longer supported: " + packageNameNormalized); + "This style of accepting/rejecting is no longer supported: " + packageNameNormalized); } - // Whitelist package - scanSpec.packageWhiteBlackList.addToWhitelist(packageNameNormalized); - final String path = WhiteBlackList.packageNameToPath(packageNameNormalized); - scanSpec.pathWhiteBlackList.addToWhitelist(path + "/"); + // Accept package + scanSpec.packageAcceptReject.addToAccept(packageNameNormalized); + final String path = AcceptReject.packageNameToPath(packageNameNormalized); + scanSpec.pathAcceptReject.addToAccept(path + "/"); if (packageNameNormalized.isEmpty()) { - scanSpec.pathWhiteBlackList.addToWhitelist(""); + scanSpec.pathAcceptReject.addToAccept(""); } if (!packageNameNormalized.contains("*")) { - // Whitelist sub-packages + // Accept sub-packages if (packageNameNormalized.isEmpty()) { - scanSpec.packagePrefixWhiteBlackList.addToWhitelist(""); - scanSpec.pathPrefixWhiteBlackList.addToWhitelist(""); + scanSpec.packagePrefixAcceptReject.addToAccept(""); + scanSpec.pathPrefixAcceptReject.addToAccept(""); } else { - scanSpec.packagePrefixWhiteBlackList.addToWhitelist(packageNameNormalized + "."); - scanSpec.pathPrefixWhiteBlackList.addToWhitelist(path + "/"); + scanSpec.packagePrefixAcceptReject.addToAccept(packageNameNormalized + "."); + scanSpec.pathPrefixAcceptReject.addToAccept(path + "/"); } } } return this; } + /** + * @deprecated Use {@link #acceptPackages(String...)} instead. + * + * @param packageNames + * The fully-qualified names of packages to scan (using '.' as a separator). May include a glob + * wildcard ({@code '*'}). + * @return this (for method chaining). + */ + @Deprecated + public ClassGraph whitelistPackages(final String... packageNames) { + return acceptPackages(packageNames); + } + /** * Scan one or more specific paths, and their sub-directories or nested paths. * @@ -643,36 +656,49 @@ public ClassGraph whitelistPackages(final String... packageNames) { * separator). May include a glob wildcard ({@code '*'}). * @return this (for method chaining). */ - public ClassGraph whitelistPaths(final String... paths) { + public ClassGraph acceptPaths(final String... paths) { for (final String path : paths) { - final String pathNormalized = WhiteBlackList.normalizePath(path); - // Whitelist path - final String packageName = WhiteBlackList.pathToPackageName(pathNormalized); - scanSpec.packageWhiteBlackList.addToWhitelist(packageName); - scanSpec.pathWhiteBlackList.addToWhitelist(pathNormalized + "/"); + final String pathNormalized = AcceptReject.normalizePath(path); + // Accept path + final String packageName = AcceptReject.pathToPackageName(pathNormalized); + scanSpec.packageAcceptReject.addToAccept(packageName); + scanSpec.pathAcceptReject.addToAccept(pathNormalized + "/"); if (pathNormalized.isEmpty()) { - scanSpec.pathWhiteBlackList.addToWhitelist(""); + scanSpec.pathAcceptReject.addToAccept(""); } if (!pathNormalized.contains("*")) { - // Whitelist sub-directories / nested paths + // Accept sub-directories / nested paths if (pathNormalized.isEmpty()) { - scanSpec.packagePrefixWhiteBlackList.addToWhitelist(""); - scanSpec.pathPrefixWhiteBlackList.addToWhitelist(""); + scanSpec.packagePrefixAcceptReject.addToAccept(""); + scanSpec.pathPrefixAcceptReject.addToAccept(""); } else { - scanSpec.packagePrefixWhiteBlackList.addToWhitelist(packageName + "."); - scanSpec.pathPrefixWhiteBlackList.addToWhitelist(pathNormalized + "/"); + scanSpec.packagePrefixAcceptReject.addToAccept(packageName + "."); + scanSpec.pathPrefixAcceptReject.addToAccept(pathNormalized + "/"); } } } return this; } + /** + * @deprecated Use {@link #acceptPaths(String...)} instead. + * + * @param paths + * The paths to scan, relative to the package root of the classpath element (with '/' as a + * separator). May include a glob wildcard ({@code '*'}). + * @return this (for method chaining). + */ + @Deprecated + public ClassGraph whitelistPaths(final String... paths) { + return acceptPaths(paths); + } + /** * Scan one or more specific packages, without recursively scanning sub-packages unless they are themselves - * whitelisted. + * accepted. * *

- * N.B. Automatically calls {@link #enableClassInfo()} -- call {@link #whitelistPathsNonRecursive(String...)} + * N.B. Automatically calls {@link #enableClassInfo()} -- call {@link #acceptPathsNonRecursive(String...)} * instead if you only need to scan resources. * *

@@ -685,27 +711,40 @@ public ClassGraph whitelistPaths(final String... paths) { * * @return this (for method chaining). */ - public ClassGraph whitelistPackagesNonRecursive(final String... packageNames) { + public ClassGraph acceptPackagesNonRecursive(final String... packageNames) { enableClassInfo(); for (final String packageName : packageNames) { - final String packageNameNormalized = WhiteBlackList.normalizePackageOrClassName(packageName); + final String packageNameNormalized = AcceptReject.normalizePackageOrClassName(packageName); if (packageNameNormalized.contains("*")) { throw new IllegalArgumentException("Cannot use a glob wildcard here: " + packageNameNormalized); } - // Whitelist package, but not sub-packages - scanSpec.packageWhiteBlackList.addToWhitelist(packageNameNormalized); - scanSpec.pathWhiteBlackList - .addToWhitelist(WhiteBlackList.packageNameToPath(packageNameNormalized) + "/"); + // Accept package, but not sub-packages + scanSpec.packageAcceptReject.addToAccept(packageNameNormalized); + scanSpec.pathAcceptReject.addToAccept(AcceptReject.packageNameToPath(packageNameNormalized) + "/"); if (packageNameNormalized.isEmpty()) { - scanSpec.pathWhiteBlackList.addToWhitelist(""); + scanSpec.pathAcceptReject.addToAccept(""); } } return this; } + /** + * @deprecated Use {@link #acceptPackagesNonRecursive(String...)} instead. + * + * @param packageNames + * The fully-qualified names of packages to scan (with '.' as a separator). May not include a glob + * wildcard ({@code '*'}). + * + * @return this (for method chaining). + */ + @Deprecated + public ClassGraph whitelistPackagesNonRecursive(final String... packageNames) { + return acceptPackagesNonRecursive(packageNames); + } + /** * Scan one or more specific paths, without recursively scanning sub-directories or nested paths unless they are - * themselves whitelisted. + * themselves accepted. * *

* This may be particularly useful for scanning the package root ("") without recursively scanning everything in @@ -716,85 +755,123 @@ public ClassGraph whitelistPackagesNonRecursive(final String... packageNames) { * separator). May not include a glob wildcard ({@code '*'}). * @return this (for method chaining). */ - public ClassGraph whitelistPathsNonRecursive(final String... paths) { + public ClassGraph acceptPathsNonRecursive(final String... paths) { for (final String path : paths) { if (path.contains("*")) { throw new IllegalArgumentException("Cannot use a glob wildcard here: " + path); } - final String pathNormalized = WhiteBlackList.normalizePath(path); - // Whitelist path, but not sub-directories / nested paths - scanSpec.packageWhiteBlackList.addToWhitelist(WhiteBlackList.pathToPackageName(pathNormalized)); - scanSpec.pathWhiteBlackList.addToWhitelist(pathNormalized + "/"); + final String pathNormalized = AcceptReject.normalizePath(path); + // Accept path, but not sub-directories / nested paths + scanSpec.packageAcceptReject.addToAccept(AcceptReject.pathToPackageName(pathNormalized)); + scanSpec.pathAcceptReject.addToAccept(pathNormalized + "/"); if (pathNormalized.isEmpty()) { - scanSpec.pathWhiteBlackList.addToWhitelist(""); + scanSpec.pathAcceptReject.addToAccept(""); } } return this; } + /** + * @deprecated Use {@link #acceptPathsNonRecursive(String...)} instead. + * + * @param paths + * The paths to scan, relative to the package root of the classpath element (with '/' as a + * separator). May not include a glob wildcard ({@code '*'}). + * @return this (for method chaining). + */ + @Deprecated + public ClassGraph whitelistPathsNonRecursive(final String... paths) { + return acceptPathsNonRecursive(paths); + } + /** * Prevent the scanning of one or more specific packages and their sub-packages. * *

- * N.B. Automatically calls {@link #enableClassInfo()} -- call {@link #blacklistPaths(String...)} instead if you + * N.B. Automatically calls {@link #enableClassInfo()} -- call {@link #rejectPaths(String...)} instead if you * only need to scan resources. * * @param packageNames - * The fully-qualified names of packages to blacklist (with '.' as a separator). May include a glob + * The fully-qualified names of packages to reject (with '.' as a separator). May include a glob * wildcard ({@code '*'}). * @return this (for method chaining). */ - public ClassGraph blacklistPackages(final String... packageNames) { + public ClassGraph rejectPackages(final String... packageNames) { enableClassInfo(); for (final String packageName : packageNames) { - final String packageNameNormalized = WhiteBlackList.normalizePackageOrClassName(packageName); + final String packageNameNormalized = AcceptReject.normalizePackageOrClassName(packageName); if (packageNameNormalized.isEmpty()) { throw new IllegalArgumentException( - "Blacklisting the root package (\"\") will cause nothing to be scanned"); + "Rejecting the root package (\"\") will cause nothing to be scanned"); } - // Blacklisting always prevents further recursion, no need to blacklist sub-packages - scanSpec.packageWhiteBlackList.addToBlacklist(packageNameNormalized); - final String path = WhiteBlackList.packageNameToPath(packageNameNormalized); - scanSpec.pathWhiteBlackList.addToBlacklist(path + "/"); + // Rejecting always prevents further recursion, no need to reject sub-packages + scanSpec.packageAcceptReject.addToReject(packageNameNormalized); + final String path = AcceptReject.packageNameToPath(packageNameNormalized); + scanSpec.pathAcceptReject.addToReject(path + "/"); if (!packageNameNormalized.contains("*")) { - // Blacklist sub-packages (zipfile entries can occur in any order) - scanSpec.packagePrefixWhiteBlackList.addToBlacklist(packageNameNormalized + "."); - scanSpec.pathPrefixWhiteBlackList.addToBlacklist(path + "/"); + // Reject sub-packages (zipfile entries can occur in any order) + scanSpec.packagePrefixAcceptReject.addToReject(packageNameNormalized + "."); + scanSpec.pathPrefixAcceptReject.addToReject(path + "/"); } } return this; } + /** + * @deprecated Use {@link #rejectPackages(String...)} instead. + * + * @param packageNames + * The fully-qualified names of packages to reject (with '.' as a separator). May include a glob + * wildcard ({@code '*'}). + * @return this (for method chaining). + */ + @Deprecated + public ClassGraph blacklistPackages(final String... packageNames) { + return rejectPackages(packageNames); + } + /** * Prevent the scanning of one or more specific paths and their sub-directories / nested paths. * * @param paths - * The paths to blacklist (with '/' as a separator). May include a glob wildcard ({@code '*'}). + * The paths to reject (with '/' as a separator). May include a glob wildcard ({@code '*'}). * @return this (for method chaining). */ - public ClassGraph blacklistPaths(final String... paths) { + public ClassGraph rejectPaths(final String... paths) { for (final String path : paths) { - final String pathNormalized = WhiteBlackList.normalizePath(path); + final String pathNormalized = AcceptReject.normalizePath(path); if (pathNormalized.isEmpty()) { throw new IllegalArgumentException( - "Blacklisting the root package (\"\") will cause nothing to be scanned"); + "Rejecting the root package (\"\") will cause nothing to be scanned"); } - // Blacklisting always prevents further recursion, no need to blacklist sub-directories / nested paths - final String packageName = WhiteBlackList.pathToPackageName(pathNormalized); - scanSpec.packageWhiteBlackList.addToBlacklist(packageName); - scanSpec.pathWhiteBlackList.addToBlacklist(pathNormalized + "/"); + // Rejecting always prevents further recursion, no need to reject sub-directories / nested paths + final String packageName = AcceptReject.pathToPackageName(pathNormalized); + scanSpec.packageAcceptReject.addToReject(packageName); + scanSpec.pathAcceptReject.addToReject(pathNormalized + "/"); if (!pathNormalized.contains("*")) { - // Blacklist sub-directories / nested paths - scanSpec.packagePrefixWhiteBlackList.addToBlacklist(packageName + "."); - scanSpec.pathPrefixWhiteBlackList.addToBlacklist(pathNormalized + "/"); + // Reject sub-directories / nested paths + scanSpec.packagePrefixAcceptReject.addToReject(packageName + "."); + scanSpec.pathPrefixAcceptReject.addToReject(pathNormalized + "/"); } } return this; } + /** + * @deprecated Use {@link #rejectPaths(String...)} instead. + * + * @param paths + * The paths to reject (with '/' as a separator). May include a glob wildcard ({@code '*'}). + * @return this (for method chaining). + */ + @Deprecated + public ClassGraph blacklistPaths(final String... paths) { + return rejectPaths(paths); + } + /** * Scan one or more specific classes, without scanning other classes in the same package unless the package is - * itself whitelisted. + * itself accepted. * *

* N.B. Automatically calls {@link #enableClassInfo()}. @@ -805,122 +882,173 @@ public ClassGraph blacklistPaths(final String... paths) { * wildcard ({@code '*'}). * @return this (for method chaining). */ - public ClassGraph whitelistClasses(final String... classNames) { + 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 = WhiteBlackList.normalizePackageOrClassName(className); - // Whitelist the class itself - scanSpec.classWhiteBlackList.addToWhitelist(classNameNormalized); - scanSpec.classfilePathWhiteBlackList - .addToWhitelist(WhiteBlackList.classNameToClassfilePath(classNameNormalized)); + final String classNameNormalized = AcceptReject.normalizePackageOrClassName(className); + // Accept the class itself + scanSpec.classAcceptReject.addToAccept(classNameNormalized); + scanSpec.classfilePathAcceptReject + .addToAccept(AcceptReject.classNameToClassfilePath(classNameNormalized)); final String packageName = PackageInfo.getParentPackageName(classNameNormalized); // Record the package containing the class, so we can recurse to this point even if the package - // is not itself whitelisted - scanSpec.classPackageWhiteBlackList.addToWhitelist(packageName); - scanSpec.classPackagePathWhiteBlackList - .addToWhitelist(WhiteBlackList.packageNameToPath(packageName) + "/"); + // is not itself accepted + scanSpec.classPackageAcceptReject.addToAccept(packageName); + scanSpec.classPackagePathAcceptReject.addToAccept(AcceptReject.packageNameToPath(packageName) + "/"); } return this; } /** - * Specifically blacklist one or more specific classes, preventing them from being scanned even if they are in a - * whitelisted package. + * @deprecated 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 '*'}). + * @return this (for method chaining). + */ + @Deprecated + public ClassGraph whitelistClasses(final String... classNames) { + return acceptClasses(classNames); + } + + /** + * Specifically reject one or more specific classes, preventing them from being scanned even if they are in a + * accepted package. * *

* N.B. Automatically calls {@link #enableClassInfo()}. * * @param classNames - * The fully-qualified names of classes to blacklist (using '.' as a separator). May not include a - * glob wildcard ({@code '*'}). + * The fully-qualified names of classes to reject (using '.' as a separator). May not include a glob + * wildcard ({@code '*'}). * @return this (for method chaining). */ - public ClassGraph blacklistClasses(final String... classNames) { + 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 = WhiteBlackList.normalizePackageOrClassName(className); - scanSpec.classWhiteBlackList.addToBlacklist(classNameNormalized); - scanSpec.classfilePathWhiteBlackList - .addToBlacklist(WhiteBlackList.classNameToClassfilePath(classNameNormalized)); + final String classNameNormalized = AcceptReject.normalizePackageOrClassName(className); + scanSpec.classAcceptReject.addToReject(classNameNormalized); + scanSpec.classfilePathAcceptReject + .addToReject(AcceptReject.classNameToClassfilePath(classNameNormalized)); } return this; } /** - * Whitelist one or more jars. This will cause only the whitelisted jars to be scanned. + * @deprecated 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 '*'}). + * @return this (for method chaining). + */ + @Deprecated + public ClassGraph blacklistClasses(final String... classNames) { + return rejectClasses(classNames); + } + + /** + * Accept one or more jars. This will cause only the accepted jars to be scanned. * * @param jarLeafNames * The leafnames of the jars that should be scanned (e.g. {@code "mylib.jar"}). May contain a * wildcard glob ({@code "mylib-*.jar"}). * @return this (for method chaining). */ - public ClassGraph whitelistJars(final String... jarLeafNames) { + public ClassGraph acceptJars(final String... jarLeafNames) { for (final String jarLeafName : jarLeafNames) { final String leafName = JarUtils.leafName(jarLeafName); if (!leafName.equals(jarLeafName)) { - throw new IllegalArgumentException("Can only whitelist jars by leafname: " + jarLeafName); + throw new IllegalArgumentException("Can only accept jars by leafname: " + jarLeafName); } - scanSpec.jarWhiteBlackList.addToWhitelist(leafName); + scanSpec.jarAcceptReject.addToAccept(leafName); } return this; } /** - * Blacklist one or more jars, preventing them from being scanned. + * @deprecated Use {@link #acceptJars(String...)} instead. + * + * @param jarLeafNames + * The leafnames of the jars that should be scanned (e.g. {@code "mylib.jar"}). May contain a + * wildcard glob ({@code "mylib-*.jar"}). + * @return this (for method chaining). + */ + @Deprecated + public ClassGraph whitelistJars(final String... jarLeafNames) { + return acceptJars(jarLeafNames); + } + + /** + * Reject one or more jars, preventing them from being scanned. * * @param jarLeafNames * The leafnames of the jars that should be scanned (e.g. {@code "badlib.jar"}). May contain a * wildcard glob ({@code "badlib-*.jar"}). * @return this (for method chaining). */ - public ClassGraph blacklistJars(final String... jarLeafNames) { + public ClassGraph rejectJars(final String... jarLeafNames) { for (final String jarLeafName : jarLeafNames) { final String leafName = JarUtils.leafName(jarLeafName); if (!leafName.equals(jarLeafName)) { - throw new IllegalArgumentException("Can only blacklist jars by leafname: " + jarLeafName); + throw new IllegalArgumentException("Can only reject jars by leafname: " + jarLeafName); } - scanSpec.jarWhiteBlackList.addToBlacklist(leafName); + scanSpec.jarAcceptReject.addToReject(leafName); } return this; } /** - * Add lib or ext jars to whitelist or blacklist. + * @deprecated Use {@link #rejectJars(String...)} instead. + * + * @param jarLeafNames + * The leafnames of the jars that should be scanned (e.g. {@code "badlib.jar"}). May contain a + * wildcard glob ({@code "badlib-*.jar"}). + * @return this (for method chaining). + */ + @Deprecated + public ClassGraph blacklistJars(final String... jarLeafNames) { + return rejectJars(jarLeafNames); + } + + /** + * Add lib or ext jars to accept or reject. * - * @param whitelist - * if true, add to whitelist, otherwise add to blacklist. + * @param accept + * if true, add to accept, otherwise add to reject. * @param jarLeafNames - * the jar leaf names to whitelist + * the jar leaf names to accept */ - private void whitelistOrBlacklistLibOrExtJars(final boolean whitelist, final String... jarLeafNames) { + private void acceptOrRejectLibOrExtJars(final boolean accept, final String... jarLeafNames) { if (jarLeafNames.length == 0) { - // If no jar leafnames are given, whitelist or blacklist all lib or ext jars + // If no jar leafnames are given, accept or reject all lib or ext jars for (final String libOrExtJar : SystemJarFinder.getJreLibOrExtJars()) { - whitelistOrBlacklistLibOrExtJars(whitelist, JarUtils.leafName(libOrExtJar)); + acceptOrRejectLibOrExtJars(accept, JarUtils.leafName(libOrExtJar)); } } else { for (final String jarLeafName : jarLeafNames) { final String leafName = JarUtils.leafName(jarLeafName); if (!leafName.equals(jarLeafName)) { - throw new IllegalArgumentException("Can only " + (whitelist ? "whitelist" : "blacklist") - + " jars by leafname: " + jarLeafName); + throw new IllegalArgumentException( + "Can only " + (accept ? "accept" : "reject") + " jars by leafname: " + jarLeafName); } if (jarLeafName.contains("*")) { // Compare wildcarded pattern against all jars in lib and ext dirs - final Pattern pattern = WhiteBlackList.globToPattern(jarLeafName); + final Pattern pattern = AcceptReject.globToPattern(jarLeafName); boolean found = false; for (final String libOrExtJarPath : SystemJarFinder.getJreLibOrExtJars()) { final String libOrExtJarLeafName = JarUtils.leafName(libOrExtJarPath); if (pattern.matcher(libOrExtJarLeafName).matches()) { // Check for "*" in filename to prevent infinite recursion (shouldn't happen) if (!libOrExtJarLeafName.contains("*")) { - whitelistOrBlacklistLibOrExtJars(whitelist, libOrExtJarLeafName); + acceptOrRejectLibOrExtJars(accept, libOrExtJarLeafName); } found = true; } @@ -929,18 +1057,18 @@ private void whitelistOrBlacklistLibOrExtJars(final boolean whitelist, final Str topLevelLog.log("Could not find lib or ext jar matching wildcard: " + jarLeafName); } } else { - // No wildcards, just whitelist or blacklist the named jar, if present + // No wildcards, just accept or reject the named jar, if present boolean found = false; for (final String libOrExtJarPath : SystemJarFinder.getJreLibOrExtJars()) { final String libOrExtJarLeafName = JarUtils.leafName(libOrExtJarPath); if (jarLeafName.equals(libOrExtJarLeafName)) { - if (whitelist) { - scanSpec.libOrExtJarWhiteBlackList.addToWhitelist(jarLeafName); + if (accept) { + scanSpec.libOrExtJarAcceptReject.addToAccept(jarLeafName); } else { - scanSpec.libOrExtJarWhiteBlackList.addToBlacklist(jarLeafName); + scanSpec.libOrExtJarAcceptReject.addToReject(jarLeafName); } if (topLevelLog != null) { - topLevelLog.log((whitelist ? "Whitelisting" : "Blacklisting") + " lib or ext jar: " + topLevelLog.log((accept ? "Accepting" : "Rejecting") + " lib or ext jar: " + libOrExtJarPath); } found = true; @@ -956,94 +1084,172 @@ private void whitelistOrBlacklistLibOrExtJars(final boolean whitelist, final Str } /** - * Whitelist one or more jars in a JRE/JDK "lib/" or "ext/" directory (these directories are not scanned unless + * Accept one or more jars in a JRE/JDK "lib/" or "ext/" directory (these directories are not scanned unless * {@link #enableSystemJarsAndModules()} is called, by association with the JRE/JDK). * * @param jarLeafNames * The leafnames of the lib/ext jar(s) that should be scanned (e.g. {@code "mylib.jar"}). May contain * a wildcard glob ({@code '*'}). Note that if you call this method with no parameters, all JRE/JDK - * "lib/" or "ext/" jars will be whitelisted. + * "lib/" or "ext/" jars will be accepted. * @return this (for method chaining). */ + public ClassGraph acceptLibOrExtJars(final String... jarLeafNames) { + acceptOrRejectLibOrExtJars(/* accept = */ true, jarLeafNames); + return this; + } + + /** + * @deprecated Use {@link #acceptLibOrExtJars(String...)} instead. + * + * @param jarLeafNames + * The leafnames of the lib/ext jar(s) that should be scanned (e.g. {@code "mylib.jar"}). May contain + * a wildcard glob ({@code '*'}). Note that if you call this method with no parameters, all JRE/JDK + * "lib/" or "ext/" jars will be accepted. + * @return this (for method chaining). + */ + @Deprecated public ClassGraph whitelistLibOrExtJars(final String... jarLeafNames) { - whitelistOrBlacklistLibOrExtJars(/* whitelist = */ true, jarLeafNames); + return acceptLibOrExtJars(jarLeafNames); + } + + /** + * Reject one or more jars in a JRE/JDK "lib/" or "ext/" directory, preventing them from being scanned. + * + * @param jarLeafNames + * The leafnames of the lib/ext jar(s) that should not be scanned (e.g. + * {@code "jre/lib/badlib.jar"}). May contain a wildcard glob ({@code '*'}). If you call this method + * with no parameters, all JRE/JDK {@code "lib/"} or {@code "ext/"} jars will be rejected. + * @return this (for method chaining). + */ + public ClassGraph rejectLibOrExtJars(final String... jarLeafNames) { + acceptOrRejectLibOrExtJars(/* accept = */ false, jarLeafNames); return this; } /** - * Blacklist one or more jars in a JRE/JDK "lib/" or "ext/" directory, preventing them from being scanned. + * @deprecated Use {@link #rejectLibOrExtJars(String...)} instead. * * @param jarLeafNames * The leafnames of the lib/ext jar(s) that should not be scanned (e.g. * {@code "jre/lib/badlib.jar"}). May contain a wildcard glob ({@code '*'}). If you call this method - * with no parameters, all JRE/JDK {@code "lib/"} or {@code "ext/"} jars will be blacklisted. + * with no parameters, all JRE/JDK {@code "lib/"} or {@code "ext/"} jars will be rejected. * @return this (for method chaining). */ + @Deprecated public ClassGraph blacklistLibOrExtJars(final String... jarLeafNames) { - whitelistOrBlacklistLibOrExtJars(/* whitelist = */ false, jarLeafNames); + return rejectLibOrExtJars(jarLeafNames); + } + + /** + * Accept one or more modules for scanning. + * + * @param moduleNames + * The names of the modules that should be scanned. May contain a wildcard glob ({@code '*'}). + * @return this (for method chaining). + */ + public ClassGraph acceptModules(final String... moduleNames) { + for (final String moduleName : moduleNames) { + scanSpec.moduleAcceptReject.addToAccept(AcceptReject.normalizePackageOrClassName(moduleName)); + } return this; } /** - * Whitelist one or more modules to scan. + * @deprecated Use {@link #acceptModules(String...)} instead. * * @param moduleNames * The names of the modules that should be scanned. May contain a wildcard glob ({@code '*'}). * @return this (for method chaining). */ + @Deprecated public ClassGraph whitelistModules(final String... moduleNames) { + return acceptModules(moduleNames); + } + + /** + * Reject one or more modules, preventing them from being scanned. + * + * @param moduleNames + * The names of the modules that should not be scanned. May contain a wildcard glob ({@code '*'}). + * @return this (for method chaining). + */ + public ClassGraph rejectModules(final String... moduleNames) { for (final String moduleName : moduleNames) { - scanSpec.moduleWhiteBlackList.addToWhitelist(WhiteBlackList.normalizePackageOrClassName(moduleName)); + scanSpec.moduleAcceptReject.addToReject(AcceptReject.normalizePackageOrClassName(moduleName)); } return this; } /** - * Blacklist one or more modules, preventing them from being scanned. + * @deprecated Use {@link #rejectModules(String...)} instead. * * @param moduleNames * The names of the modules that should not be scanned. May contain a wildcard glob ({@code '*'}). * @return this (for method chaining). */ + @Deprecated public ClassGraph blacklistModules(final String... moduleNames) { - for (final String moduleName : moduleNames) { - scanSpec.moduleWhiteBlackList.addToBlacklist(WhiteBlackList.normalizePackageOrClassName(moduleName)); + return rejectModules(moduleNames); + } + + /** + * Accept classpath elements based on resource paths. Only classpath elements that contain resources with paths + * matching the accept will be scanned. + * + * @param resourcePaths + * The resource paths, any of which must be present in a classpath element for the classpath element + * to be scanned. May contain a wildcard glob ({@code '*'}). + * @return this (for method chaining). + */ + public ClassGraph acceptClasspathElementsContainingResourcePath(final String... resourcePaths) { + for (final String resourcePath : resourcePaths) { + final String resourcePathNormalized = AcceptReject.normalizePath(resourcePath); + scanSpec.classpathElementResourcePathAcceptReject.addToAccept(resourcePathNormalized); } return this; } /** - * Whitelist classpath elements based on resource paths. Only classpath elements that contain resources with - * paths matching the whitelist will be scanned. + * @deprecated Use {@link #acceptClasspathElementsContainingResourcePath(String...)} instead. * * @param resourcePaths * The resource paths, any of which must be present in a classpath element for the classpath element * to be scanned. May contain a wildcard glob ({@code '*'}). * @return this (for method chaining). */ + @Deprecated public ClassGraph whitelistClasspathElementsContainingResourcePath(final String... resourcePaths) { + return acceptClasspathElementsContainingResourcePath(resourcePaths); + } + + /** + * Reject classpath elements based on resource paths. Classpath elements that contain resources with paths + * matching the reject will not be scanned. + * + * @param resourcePaths + * The resource paths which cause a classpath not to be scanned if any are present in a classpath + * element for the classpath element. May contain a wildcard glob ({@code '*'}). + * @return this (for method chaining). + */ + public ClassGraph rejectClasspathElementsContainingResourcePath(final String... resourcePaths) { for (final String resourcePath : resourcePaths) { - final String resourcePathNormalized = WhiteBlackList.normalizePath(resourcePath); - scanSpec.classpathElementResourcePathWhiteBlackList.addToWhitelist(resourcePathNormalized); + final String resourcePathNormalized = AcceptReject.normalizePath(resourcePath); + scanSpec.classpathElementResourcePathAcceptReject.addToReject(resourcePathNormalized); } return this; } /** - * Blacklist classpath elements based on resource paths. Classpath elements that contain resources with paths - * matching the blacklist will not be scanned. + * @deprecated Use {@link #rejectClasspathElementsContainingResourcePath(String...)} instead. * * @param resourcePaths * The resource paths which cause a classpath not to be scanned if any are present in a classpath * element for the classpath element. May contain a wildcard glob ({@code '*'}). * @return this (for method chaining). */ + @Deprecated public ClassGraph blacklistClasspathElementsContainingResourcePath(final String... resourcePaths) { - for (final String resourcePath : resourcePaths) { - final String resourcePathNormalized = WhiteBlackList.normalizePath(resourcePath); - scanSpec.classpathElementResourcePathWhiteBlackList.addToBlacklist(resourcePathNormalized); - } - return this; + return rejectClasspathElementsContainingResourcePath(resourcePaths); } /** diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index 58a2b1eac..e320cfe91 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -90,11 +90,11 @@ public class ClassInfo extends ScanResultObject implements Comparable /** * If true, this class is only being referenced by another class' classfile as a superclass / implemented - * interface / annotation, but this class is not itself a whitelisted (non-blacklisted) class, or in a - * whitelisted (non-blacklisted) package. + * interface / annotation, but this class is not itself an accepted (non-rejected) class, or in a accepted + * (non-rejected) package. * * If false, this classfile was matched during scanning (i.e. its classfile contents read), i.e. this class is a - * whitelisted (and non-blacklisted) class in a whitelisted (and non-blacklisted) package. + * accepted (and non-rejected) class in an accepted (and non-rejected) package. */ protected boolean isExternalClass = true; @@ -678,7 +678,7 @@ static ClassInfo addScannedClass(final String className, final int classModifier // Mark the class as scanned classInfo.isScannedClass = true; - // Mark the class as non-external if it is a whitelisted class + // Mark the class as non-external if it is an accepted class classInfo.isExternalClass = isExternalClass; // Remember which classpath element (zipfile / classpath root directory / module) the class was found in @@ -720,14 +720,14 @@ private enum ClassType { * the classes * @param scanSpec * the scan spec - * @param strictWhitelist - * If true, exclude class if it is is external, blacklisted, or a system class. + * @param strictAccept + * If true, exclude class if it is is external, rejected, or a system class. * @param classTypes * the class types * @return the filtered classes. */ private static Set filterClassInfo(final Collection classes, final ScanSpec scanSpec, - final boolean strictWhitelist, final ClassType... classTypes) { + final boolean strictAccept, final ClassType... classTypes) { if (classes == null) { return Collections. emptySet(); } @@ -776,13 +776,13 @@ private static Set filterClassInfo(final Collection classe || includeAnnotations && classInfo.isAnnotation() // || includeEnums && classInfo.isEnum() // || includeRecords && classInfo.isRecord()) // - // Always check blacklist - && !scanSpec.classOrPackageIsBlacklisted(classInfo.name) // - // Always return whitelisted classes, or external classes if enableExternalClasses is true + // Always check reject + && !scanSpec.classOrPackageIsRejected(classInfo.name) // + // Always return accepted classes, or external classes if enableExternalClasses is true && (!classInfo.isExternalClass || scanSpec.enableExternalClasses - // Return external (non-whitelisted) classes if viewing class hierarchy "upwards" - || !strictWhitelist)) { - // Class passed strict whitelist criteria + // Return external (non-accepted) classes if viewing class hierarchy "upwards" + || !strictAccept)) { + // Class passed strict accept criteria classInfoSetFiltered.add(classInfo); } } @@ -822,13 +822,13 @@ private ReachableAndDirectlyRelatedClasses(final Set reachableClasses * * @param relType * the rel type - * @param strictWhitelist - * the strict whitelist + * @param strictAccept + * the strict accept criterion * @param classTypes * the class types * @return the reachable and directly related classes */ - private ReachableAndDirectlyRelatedClasses filterClassInfo(final RelType relType, final boolean strictWhitelist, + private ReachableAndDirectlyRelatedClasses filterClassInfo(final RelType relType, final boolean strictAccept, final ClassType... classTypes) { Set directlyRelatedClasses = this.relatedClasses.get(relType); if (directlyRelatedClasses == null) { @@ -843,7 +843,7 @@ private ReachableAndDirectlyRelatedClasses filterClassInfo(final RelType relType // For method and field annotations, need to change the RelType when finding meta-annotations for (final ClassInfo annotation : directlyRelatedClasses) { reachableClasses.addAll( - annotation.filterClassInfo(RelType.CLASS_ANNOTATIONS, strictWhitelist).reachableClasses); + annotation.filterClassInfo(RelType.CLASS_ANNOTATIONS, strictAccept).reachableClasses); } } else if (relType == RelType.CLASSES_WITH_METHOD_ANNOTATION || relType == RelType.CLASSES_WITH_NONPRIVATE_METHOD_ANNOTATION @@ -853,8 +853,8 @@ private ReachableAndDirectlyRelatedClasses filterClassInfo(final RelType relType || relType == RelType.CLASSES_WITH_NONPRIVATE_FIELD_ANNOTATION) { // If looking for meta-annotated methods or fields, need to find all meta-annotated annotations, then // look for the methods or fields that they annotate - for (final ClassInfo subAnnotation : this.filterClassInfo(RelType.CLASSES_WITH_ANNOTATION, - strictWhitelist, ClassType.ANNOTATION).reachableClasses) { + for (final ClassInfo subAnnotation : this.filterClassInfo(RelType.CLASSES_WITH_ANNOTATION, strictAccept, + ClassType.ANNOTATION).reachableClasses) { final Set annotatedClasses = subAnnotation.relatedClasses.get(relType); if (annotatedClasses != null) { reachableClasses.addAll(annotatedClasses); @@ -902,8 +902,8 @@ private ReachableAndDirectlyRelatedClasses filterClassInfo(final RelType relType } return new ReachableAndDirectlyRelatedClasses( - filterClassInfo(reachableClasses, scanResult.scanSpec, strictWhitelist, classTypes), - filterClassInfo(directlyRelatedClasses, scanResult.scanSpec, strictWhitelist, classTypes)); + filterClassInfo(reachableClasses, scanResult.scanSpec, strictAccept, classTypes), + filterClassInfo(directlyRelatedClasses, scanResult.scanSpec, strictAccept, classTypes)); } @@ -920,7 +920,7 @@ private ReachableAndDirectlyRelatedClasses filterClassInfo(final RelType relType */ static ClassInfoList getAllClasses(final Collection classes, final ScanSpec scanSpec) { return new ClassInfoList( - ClassInfo.filterClassInfo(classes, scanSpec, /* strictWhitelist = */ true, ClassType.ALL), + ClassInfo.filterClassInfo(classes, scanSpec, /* strictAccept = */ true, ClassType.ALL), /* sortByName = */ true); } @@ -935,7 +935,7 @@ static ClassInfoList getAllClasses(final Collection classes, final Sc */ static ClassInfoList getAllEnums(final Collection classes, final ScanSpec scanSpec) { return new ClassInfoList( - ClassInfo.filterClassInfo(classes, scanSpec, /* strictWhitelist = */ true, ClassType.ENUM), + ClassInfo.filterClassInfo(classes, scanSpec, /* strictAccept = */ true, ClassType.ENUM), /* sortByName = */ true); } @@ -950,7 +950,7 @@ static ClassInfoList getAllEnums(final Collection classes, final Scan */ static ClassInfoList getAllRecords(final Collection classes, final ScanSpec scanSpec) { return new ClassInfoList( - ClassInfo.filterClassInfo(classes, scanSpec, /* strictWhitelist = */ true, ClassType.RECORD), + ClassInfo.filterClassInfo(classes, scanSpec, /* strictAccept = */ true, ClassType.RECORD), /* sortByName = */ true); } @@ -964,8 +964,9 @@ static ClassInfoList getAllRecords(final Collection classes, final Sc * @return A list of all standard classes found during the scan, or the empty list if none. */ static ClassInfoList getAllStandardClasses(final Collection classes, final ScanSpec scanSpec) { - return new ClassInfoList(ClassInfo.filterClassInfo(classes, scanSpec, /* strictWhitelist = */ true, - ClassType.STANDARD_CLASS), /* sortByName = */ true); + return new ClassInfoList( + ClassInfo.filterClassInfo(classes, scanSpec, /* strictAccept = */ true, ClassType.STANDARD_CLASS), + /* sortByName = */ true); } /** @@ -979,7 +980,7 @@ static ClassInfoList getAllStandardClasses(final Collection classes, */ static ClassInfoList getAllImplementedInterfaceClasses(final Collection classes, final ScanSpec scanSpec) { - return new ClassInfoList(ClassInfo.filterClassInfo(classes, scanSpec, /* strictWhitelist = */ true, + return new ClassInfoList(ClassInfo.filterClassInfo(classes, scanSpec, /* strictAccept = */ true, ClassType.IMPLEMENTED_INTERFACE), /* sortByName = */ true); } @@ -995,7 +996,7 @@ static ClassInfoList getAllImplementedInterfaceClasses(final Collection classes, final ScanSpec scanSpec) { return new ClassInfoList( - ClassInfo.filterClassInfo(classes, scanSpec, /* strictWhitelist = */ true, ClassType.ANNOTATION), + ClassInfo.filterClassInfo(classes, scanSpec, /* strictAccept = */ true, ClassType.ANNOTATION), /* sortByName = */ true); } @@ -1007,11 +1008,11 @@ static ClassInfoList getAllAnnotationClasses(final Collection classes * the classes * @param scanSpec * the scan spec - * @return A list of all whitelisted 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. */ static ClassInfoList getAllInterfacesOrAnnotationClasses(final Collection classes, final ScanSpec scanSpec) { - return new ClassInfoList(ClassInfo.filterClassInfo(classes, scanSpec, /* strictWhitelist = */ true, + return new ClassInfoList(ClassInfo.filterClassInfo(classes, scanSpec, /* strictAccept = */ true, ClassType.INTERFACE_OR_ANNOTATION), /* sortByName = */ true); } @@ -1082,8 +1083,8 @@ public String getPackageName() { /** * Checks if this is an external class. * - * @return true if this class is an external class, i.e. was referenced by a whitelisted class as a superclass, - * interface, or annotation, but is not itself a whitelisted class. + * @return true if this class is an external class, i.e. was referenced by an accepted class as a superclass, + * interface, or annotation, but is not itself an accepted class. */ public boolean isExternalClass() { return isExternalClass; @@ -1093,7 +1094,7 @@ public boolean isExternalClass() { * Get the minor version of the classfile format for this class' classfile. * * @return The minor version of the classfile format for this class' classfile, or 0 if this {@link ClassInfo} - * object is a placeholder for a referenced class that was not found or not whitelisted during the scan. + * object is a placeholder for a referenced class that was not found or not accepted during the scan. */ public int getClassfileMinorVersion() { return classfileMinorVersion; @@ -1103,7 +1104,7 @@ public int getClassfileMinorVersion() { * Get the major version of the classfile format for this class' classfile. * * @return The major version of the classfile format for this class' classfile, or 0 if this {@link ClassInfo} - * object is a placeholder for a referenced class that was not found or not whitelisted during the scan. + * object is a placeholder for a referenced class that was not found or not accepted during the scan. */ public int getClassfileMajorVersion() { return classfileMajorVersion; @@ -1523,7 +1524,7 @@ public ClassInfoList getSubclasses() { return scanResult.getAllClasses(); } else { return new ClassInfoList( - this.filterClassInfo(RelType.SUBCLASSES, /* strictWhitelist = */ !isExternalClass), + this.filterClassInfo(RelType.SUBCLASSES, /* strictAccept = */ !isExternalClass), /* sortByName = */ true); } } @@ -1536,7 +1537,7 @@ public ClassInfoList getSubclasses() { * @return the list of all superclasses of this class, or the empty list if none. */ public ClassInfoList getSuperclasses() { - return new ClassInfoList(this.filterClassInfo(RelType.SUPERCLASSES, /* strictWhitelist = */ false), + return new ClassInfoList(this.filterClassInfo(RelType.SUPERCLASSES, /* strictAccept = */ false), /* sortByName = */ false); } @@ -1571,7 +1572,7 @@ public ClassInfo getSuperclass() { */ public ClassInfoList getOuterClasses() { return new ClassInfoList( - this.filterClassInfo(RelType.CONTAINED_WITHIN_OUTER_CLASS, /* strictWhitelist = */ false), + this.filterClassInfo(RelType.CONTAINED_WITHIN_OUTER_CLASS, /* strictAccept = */ false), /* sortByName = */ false); } @@ -1581,7 +1582,7 @@ public ClassInfoList getOuterClasses() { * @return A list of the inner classes contained within this class, or the empty list if none. */ public ClassInfoList getInnerClasses() { - return new ClassInfoList(this.filterClassInfo(RelType.CONTAINS_INNER_CLASS, /* strictWhitelist = */ false), + return new ClassInfoList(this.filterClassInfo(RelType.CONTAINS_INNER_CLASS, /* strictAccept = */ false), /* sortByName = */ true); } @@ -1610,12 +1611,12 @@ public String getFullyQualifiedDefiningMethodName() { public ClassInfoList getInterfaces() { // Classes also implement the interfaces of their superclasses final ReachableAndDirectlyRelatedClasses implementedInterfaces = this - .filterClassInfo(RelType.IMPLEMENTED_INTERFACES, /* strictWhitelist = */ false); + .filterClassInfo(RelType.IMPLEMENTED_INTERFACES, /* strictAccept = */ false); final Set allInterfaces = new LinkedHashSet<>(implementedInterfaces.reachableClasses); for (final ClassInfo superclass : this.filterClassInfo(RelType.SUPERCLASSES, - /* strictWhitelist = */ false).reachableClasses) { - final Set superclassImplementedInterfaces = superclass.filterClassInfo( - RelType.IMPLEMENTED_INTERFACES, /* strictWhitelist = */ false).reachableClasses; + /* strictAccept = */ false).reachableClasses) { + final Set superclassImplementedInterfaces = superclass + .filterClassInfo(RelType.IMPLEMENTED_INTERFACES, /* strictAccept = */ false).reachableClasses; allInterfaces.addAll(superclassImplementedInterfaces); } return new ClassInfoList(allInterfaces, implementedInterfaces.directlyRelatedClasses, @@ -1634,11 +1635,11 @@ public ClassInfoList getClassesImplementing() { } // Subclasses of implementing classes also implement the interface final ReachableAndDirectlyRelatedClasses implementingClasses = this - .filterClassInfo(RelType.CLASSES_IMPLEMENTING, /* strictWhitelist = */ !isExternalClass); + .filterClassInfo(RelType.CLASSES_IMPLEMENTING, /* strictAccept = */ !isExternalClass); final Set allImplementingClasses = new LinkedHashSet<>(implementingClasses.reachableClasses); for (final ClassInfo implementingClass : implementingClasses.reachableClasses) { final Set implementingSubclasses = implementingClass.filterClassInfo(RelType.SUBCLASSES, - /* strictWhitelist = */ !implementingClass.isExternalClass).reachableClasses; + /* strictAccept = */ !implementingClass.isExternalClass).reachableClasses; allImplementingClasses.addAll(implementingSubclasses); } return new ClassInfoList(allImplementingClasses, implementingClasses.directlyRelatedClasses, @@ -1668,12 +1669,12 @@ public ClassInfoList getAnnotations() { // Get all annotations on this class final ReachableAndDirectlyRelatedClasses annotationClasses = this.filterClassInfo(RelType.CLASS_ANNOTATIONS, - /* strictWhitelist = */ false); + /* 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, - /* strictWhitelist = */ false).reachableClasses) { + /* 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) { @@ -1716,7 +1717,7 @@ private ClassInfoList getFieldOrMethodAnnotations(final RelType relType) { + "Info() and " + "#enableAnnotationInfo() before #scan()"); } final ReachableAndDirectlyRelatedClasses fieldOrMethodAnnotations = this.filterClassInfo(relType, - /* strictWhitelist = */ false, ClassType.ANNOTATION); + /* strictAccept = */ false, ClassType.ANNOTATION); final Set fieldOrMethodAnnotationsAndMetaAnnotations = new LinkedHashSet<>( fieldOrMethodAnnotations.reachableClasses); return new ClassInfoList(fieldOrMethodAnnotationsAndMetaAnnotations, @@ -1745,9 +1746,9 @@ private ClassInfoList getClassesWithFieldOrMethodAnnotation(final RelType relTyp + "Info() and " + "#enableAnnotationInfo() before #scan()"); } final ReachableAndDirectlyRelatedClasses classesWithDirectlyAnnotatedFieldsOrMethods = this - .filterClassInfo(relType, /* strictWhitelist = */ !isExternalClass); + .filterClassInfo(relType, /* strictAccept = */ !isExternalClass); final ReachableAndDirectlyRelatedClasses annotationsWithThisMetaAnnotation = this.filterClassInfo( - RelType.CLASSES_WITH_ANNOTATION, /* strictWhitelist = */ !isExternalClass, ClassType.ANNOTATION); + RelType.CLASSES_WITH_ANNOTATION, /* strictAccept = */ !isExternalClass, ClassType.ANNOTATION); if (annotationsWithThisMetaAnnotation.reachableClasses.isEmpty()) { // This annotation does not meta-annotate another annotation that annotates a method return new ClassInfoList(classesWithDirectlyAnnotatedFieldsOrMethods, /* sortByName = */ true); @@ -1759,7 +1760,7 @@ private ClassInfoList getClassesWithFieldOrMethodAnnotation(final RelType relTyp for (final ClassInfo metaAnnotatedAnnotation : annotationsWithThisMetaAnnotation.reachableClasses) { allClassesWithAnnotatedOrMetaAnnotatedFieldsOrMethods .addAll(metaAnnotatedAnnotation.filterClassInfo(relType, - /* strictWhitelist = */ !metaAnnotatedAnnotation.isExternalClass).reachableClasses); + /* strictAccept = */ !metaAnnotatedAnnotation.isExternalClass).reachableClasses); } return new ClassInfoList(allClassesWithAnnotatedOrMetaAnnotatedFieldsOrMethods, classesWithDirectlyAnnotatedFieldsOrMethods.directlyRelatedClasses, /* sortByName = */ true); @@ -1867,7 +1868,7 @@ public ClassInfoList getClassesWithAnnotation() { // Get classes that have this annotation final ReachableAndDirectlyRelatedClasses classesWithAnnotation = this - .filterClassInfo(RelType.CLASSES_WITH_ANNOTATION, /* strictWhitelist = */ !isExternalClass); + .filterClassInfo(RelType.CLASSES_WITH_ANNOTATION, /* strictAccept = */ !isExternalClass); if (isInherited) { // If this is an inherited annotation, add into the result all subclasses of the annotated classes. @@ -1892,7 +1893,7 @@ public ClassInfoList getClassesWithAnnotation() { */ ClassInfoList getClassesWithAnnotationDirectOnly() { return new ClassInfoList( - this.filterClassInfo(RelType.CLASSES_WITH_ANNOTATION, /* strictWhitelist = */ !isExternalClass), + this.filterClassInfo(RelType.CLASSES_WITH_ANNOTATION, /* strictAccept = */ !isExternalClass), /* sortByName = */ true); } @@ -2355,8 +2356,9 @@ public ClassInfoList getClassesWithMethodParameterAnnotation() { * the requested method annotation, or the empty list if none. */ ClassInfoList getClassesWithMethodAnnotationDirectOnly() { - return new ClassInfoList(this.filterClassInfo(RelType.CLASSES_WITH_METHOD_ANNOTATION, - /* strictWhitelist = */ !isExternalClass), /* sortByName = */ true); + return new ClassInfoList( + this.filterClassInfo(RelType.CLASSES_WITH_METHOD_ANNOTATION, /* strictAccept = */ !isExternalClass), + /* sortByName = */ true); } /** @@ -2367,7 +2369,7 @@ ClassInfoList getClassesWithMethodAnnotationDirectOnly() { */ ClassInfoList getClassesWithMethodParameterAnnotationDirectOnly() { return new ClassInfoList(this.filterClassInfo(RelType.CLASSES_WITH_METHOD_PARAMETER_ANNOTATION, - /* strictWhitelist = */ !isExternalClass), /* sortByName = */ true); + /* strictAccept = */ !isExternalClass), /* sortByName = */ true); } // ------------------------------------------------------------------------------------------------------------- @@ -2558,8 +2560,9 @@ public ClassInfoList getClassesWithFieldAnnotation() { * the requested method annotation, or the empty list if none. */ ClassInfoList getClassesWithFieldAnnotationDirectOnly() { - return new ClassInfoList(this.filterClassInfo(RelType.CLASSES_WITH_FIELD_ANNOTATION, - /* strictWhitelist = */ !isExternalClass), /* sortByName = */ true); + return new ClassInfoList( + this.filterClassInfo(RelType.CLASSES_WITH_FIELD_ANNOTATION, /* strictAccept = */ !isExternalClass), + /* sortByName = */ true); } // ------------------------------------------------------------------------------------------------------------- @@ -2669,8 +2672,8 @@ public ModuleRef getModuleRef() { * The {@link Resource} for the classfile of this class. * * @return The {@link Resource} for the classfile of this class. Returns null if the classfile for this class - * was not actually read during the scan, e.g. because this class was not itself whitelisted, but was - * referenced by a whitelisted class. + * was not actually read during the scan, e.g. because this class was not itself accepted, but was + * referenced by an accepted class. */ public Resource getResource() { return classfileResource; @@ -2901,8 +2904,8 @@ void setReferencedClasses(final ClassInfoList refdClasses) { * @return A {@link ClassInfoList} of {@link ClassInfo} objects for all classes referenced by this class. 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-whitelisted classes to appear in the result. + * {@link ClassGraph#enableExternalClasses()} before {@link ClassGraph#scan()} if you want non-accepted + * classes to appear in the result. */ public ClassInfoList getClassDependencies() { if (!scanResult.scanSpec.enableInterClassDependencies) { @@ -2987,7 +2990,7 @@ protected String toString(final boolean typeNameOnly) { buf.append(" extends ").append(superclass.toString(/* typeNameOnly = */ true)); } final Set interfaces = this.filterClassInfo(RelType.IMPLEMENTED_INTERFACES, - /* strictWhitelist = */ false).directlyRelatedClasses; + /* strictAccept = */ false).directlyRelatedClasses; if (!interfaces.isEmpty()) { buf.append(isInterface() ? " extends " : " implements "); boolean first = true; diff --git a/src/main/java/io/github/classgraph/ClassInfoList.java b/src/main/java/io/github/classgraph/ClassInfoList.java index d9f88d990..8295c9160 100644 --- a/src/main/java/io/github/classgraph/ClassInfoList.java +++ b/src/main/java/io/github/classgraph/ClassInfoList.java @@ -559,7 +559,7 @@ public boolean accept(final ClassInfo ci) { * The GraphViz layout width in inches. * @param includeExternalClasses * If true, and if {@link ClassGraph#enableExternalClasses()} was called before scanning, show - * "external classes" (non-whitelisted classes) within the dependency graph. + * "external classes" (non-accepted classes) within the dependency graph. * @return the GraphViz file contents. * @throws IllegalArgumentException * if this {@link ClassInfoList} is empty or {@link ClassGraph#enableInterClassDependencies()} was diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index fd6acd6eb..5c1fdcb31 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -97,7 +97,7 @@ class Classfile { /** Whether this class is an annotation. */ private boolean isAnnotation; - /** The superclass name. (can be null if no superclass, or if superclass is blacklisted.) */ + /** The superclass name. (can be null if no superclass, or if superclass is rejected.) */ private String superclassName; /** The implemented interfaces. */ @@ -127,11 +127,11 @@ class Classfile { /** The type signature. */ private String typeSignature; - /** The names of whitelisted classes found in the classpath while scanning paths within classpath elements. */ - private final Set whitelistedClassNamesFound; + /** The names of accepted classes found in the classpath while scanning paths within classpath elements. */ + private final Set acceptedClassNamesFound; /** - * The names of external (non-whitelisted) classes scheduled for extended scanning (where scanning is extended + * The names of external (non-accepted) classes scheduled for extended scanning (where scanning is extended * upwards to superclasses, interfaces and annotations). */ private final Set classNamesScheduledForExtendedScanning; @@ -276,14 +276,14 @@ private void scheduleScanningIfExternalClass(final String className, final Strin final LogNode log) { // Don't scan Object if (className != null && !className.equals("java.lang.Object") - // Don't schedule a class for scanning that was already found to be whitelisted - && !whitelistedClassNamesFound.contains(className) + // Don't schedule a class for scanning that was already found to be accepted + && !acceptedClassNamesFound.contains(className) // Only schedule each external class once for scanning, across all threads && classNamesScheduledForExtendedScanning.add(className)) { - if (scanSpec.classWhiteBlackList.isBlacklisted(className)) { + if (scanSpec.classAcceptReject.isRejected(className)) { if (log != null) { log.log("Cannot extend scanning upwards to external " + relationship + " " + className - + ", since it is blacklisted"); + + ", since it is rejected"); } } else { // Search for the named class' classfile among classpath elements, in classpath order (this is O(N) @@ -328,7 +328,7 @@ private void scheduleScanningIfExternalClass(final String className, final Strin } else { if (log != null) { log.log("External " + relationship + " " + className + " was not found in " - + "non-blacklisted packages -- cannot extend scanning to this class"); + + "non-rejected packages -- cannot extend scanning to this class"); } } } @@ -1597,11 +1597,11 @@ private void readClassAttributes() throws IOException, ClassfileFormatException * the classpath element * @param classpathOrder * the classpath order - * @param whitelistedClassNamesFound - * the names of whitelisted classes found in the classpath while scanning paths within classpath + * @param acceptedClassNamesFound + * the names of accepted classes found in the classpath while scanning paths within classpath * elements. * @param classNamesScheduledForExtendedScanning - * the names of external (non-whitelisted) classes scheduled for extended scanning (where scanning is + * the names of external (non-accepted) classes scheduled for extended scanning (where scanning is * extended upwards to superclasses, interfaces and annotations). * @param relativePath * the relative path @@ -1626,7 +1626,7 @@ private void readClassAttributes() throws IOException, ClassfileFormatException * false) */ Classfile(final ClasspathElement classpathElement, final List classpathOrder, - final Set whitelistedClassNamesFound, final Set classNamesScheduledForExtendedScanning, + final Set acceptedClassNamesFound, final Set classNamesScheduledForExtendedScanning, final String relativePath, final Resource classfileResource, final boolean isExternalClass, final ConcurrentHashMap stringInternMap, final WorkQueue workQueue, final ScanSpec scanSpec, final LogNode log) @@ -1634,7 +1634,7 @@ private void readClassAttributes() throws IOException, ClassfileFormatException this.classpathElement = classpathElement; this.classpathOrder = classpathOrder; this.relativePath = relativePath; - this.whitelistedClassNamesFound = whitelistedClassNamesFound; + this.acceptedClassNamesFound = acceptedClassNamesFound; this.classNamesScheduledForExtendedScanning = classNamesScheduledForExtendedScanning; this.classfileResource = classfileResource; this.isExternalClass = isExternalClass; @@ -1730,10 +1730,10 @@ private void readClassAttributes() throws IOException, ClassfileFormatException } } - // Check if any superclasses, interfaces or annotations are external (non-whitelisted) classes + // Check if any superclasses, interfaces or annotations are external (non-accepted) classes // that need to be scheduled for scanning, so that all of the "upwards" direction of the class - // graph is scanned for any whitelisted class, even if the superclasses / interfaces / annotations - // are not themselves whitelisted. + // graph is scanned for any accepted class, even if the superclasses / interfaces / annotations + // are not themselves accepted. if (scanSpec.extendScanningUpwardsToExternalClasses) { extendScanningUpwards(subLog); // If any external classes were found, schedule them for scanning diff --git a/src/main/java/io/github/classgraph/ClasspathElement.java b/src/main/java/io/github/classgraph/ClasspathElement.java index 38a57cd4b..f49e2991c 100644 --- a/src/main/java/io/github/classgraph/ClasspathElement.java +++ b/src/main/java/io/github/classgraph/ClasspathElement.java @@ -68,8 +68,8 @@ abstract class ClasspathElement { */ boolean skipClasspathElement; - /** True if classpath element contains a specifically-whitelisted resource path. */ - boolean containsSpecificallyWhitelistedClasspathElementResourcePath; + /** True if classpath element contains a specifically-accepted resource path. */ + boolean containsSpecificallyAcceptedClasspathElementResourcePath; /** * The child classpath elements, keyed by the order of the child classpath element within the Class-Path entry @@ -84,16 +84,16 @@ abstract class ClasspathElement { List childClasspathElementsOrdered; /** - * Resources found within this classpath element that were whitelisted and not blacklisted. (Only written by one + * Resources found within this classpath element that were accepted and not rejected. (Only written by one * thread, so doesn't need to be a concurrent list.) */ - protected final List whitelistedResources = new ArrayList<>(); + protected final List acceptedResources = new ArrayList<>(); /** - * The list of all classfiles found within this classpath element that were whitelisted and not blacklisted. - * (Only written by one thread, so doesn't need to be a concurrent list.) + * The list of all classfiles found within this classpath element that were accepted and not rejected. (Only + * written by one thread, so doesn't need to be a concurrent list.) */ - protected List whitelistedClassfileResources = new ArrayList<>(); + protected List acceptedClassfileResources = new ArrayList<>(); /** The map from File to last modified timestamp, if scanFiles is true. */ protected final Map fileToLastModified = new ConcurrentHashMap<>(); @@ -145,35 +145,34 @@ ClassLoader getClassLoader() { * @return the num classfile matches */ int getNumClassfileMatches() { - return whitelistedClassfileResources == null ? 0 : whitelistedClassfileResources.size(); + return acceptedClassfileResources == null ? 0 : acceptedClassfileResources.size(); } // ------------------------------------------------------------------------------------------------------------- /** - * Check relativePath against classpathElementResourcePathWhiteBlackList. + * Check relativePath against classpathElementResourcePathAcceptReject. * * @param relativePath * the relative path * @param log * the log */ - protected void checkResourcePathWhiteBlackList(final String relativePath, final LogNode log) { - // Whitelist/blacklist classpath elements based on file resource paths - if (!scanSpec.classpathElementResourcePathWhiteBlackList.whitelistAndBlacklistAreEmpty()) { - if (scanSpec.classpathElementResourcePathWhiteBlackList.isBlacklisted(relativePath)) { + protected void 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 blacklisted classpath element resource path, stopping scanning: " - + relativePath); + log.log("Reached rejected classpath element resource path, stopping scanning: " + relativePath); } skipClasspathElement = true; return; } - if (scanSpec.classpathElementResourcePathWhiteBlackList.isSpecificallyWhitelisted(relativePath)) { + if (scanSpec.classpathElementResourcePathAcceptReject.isSpecificallyAccepted(relativePath)) { if (log != null) { - log.log("Reached specifically whitelisted classpath element resource path: " + relativePath); + log.log("Reached specifically accepted classpath element resource path: " + relativePath); } - containsSpecificallyWhitelistedClasspathElementResourcePath = true; + containsSpecificallyAcceptedClasspathElementResourcePath = true; } } } @@ -197,10 +196,10 @@ void maskClassfiles(final int classpathIdx, final Set classpathRelativeP // but actually there is no restriction for paths within a zipfile to be unique, and in fact // zipfiles in the wild do contain the same classfiles multiple times with the same exact path, // e.g.: xmlbeans-2.6.0.jar!org/apache/xmlbeans/xml/stream/Location.class - final List whitelistedClassfileResourcesFiltered = new ArrayList<>( - whitelistedClassfileResources.size()); + final List acceptedClassfileResourcesFiltered = new ArrayList<>( + acceptedClassfileResources.size()); boolean foundMasked = false; - for (final Resource res : whitelistedClassfileResources) { + for (final Resource res : acceptedClassfileResources) { final String pathRelativeToPackageRoot = res.getPath(); // Don't mask module-info.class or package-info.class, these are read for every module/package, // and they don't result in a ClassInfo object, so there will be no duplicate ClassInfo objects @@ -219,14 +218,14 @@ void maskClassfiles(final int classpathIdx, final Set classpathRelativeP + JarUtils.classfilePathToClassName(pathRelativeToPackageRoot) + " found at " + res); } } else { - whitelistedClassfileResourcesFiltered.add(res); + acceptedClassfileResourcesFiltered.add(res); } } if (foundMasked) { // Remove masked (duplicated) paths. N.B. this replaces the concurrent collection with a non-concurrent // collection, but this is the last time the collection is changed during a scan, and this method is // run from a single thread. - whitelistedClassfileResources = whitelistedClassfileResourcesFiltered; + acceptedClassfileResources = acceptedClassfileResourcesFiltered; } } @@ -245,45 +244,45 @@ void maskClassfiles(final int classpathIdx, final Set classpathRelativeP * @param log * the log */ - protected void addWhitelistedResource(final Resource resource, final ScanSpecPathMatch parentMatchStatus, + protected void addAcceptedResource(final Resource resource, final ScanSpecPathMatch parentMatchStatus, final boolean isClassfileOnly, final LogNode log) { final String path = resource.getPath(); final boolean isClassFile = FileUtils.isClassfile(path); - boolean isWhitelisted = false; + boolean isAccepted = false; if (isClassFile) { - // Check classfile scanning is enabled, and classfile is not specifically blacklisted - if (scanSpec.enableClassInfo && !scanSpec.classfilePathWhiteBlackList.isBlacklisted(path)) { - // ClassInfo is enabled, and found a whitelisted classfile - whitelistedClassfileResources.add(resource); - isWhitelisted = true; + // Check classfile scanning is enabled, and classfile is not specifically rejected + if (scanSpec.enableClassInfo && !scanSpec.classfilePathAcceptReject.isRejected(path)) { + // ClassInfo is enabled, and found an accepted classfile + acceptedClassfileResources.add(resource); + isAccepted = true; } } else { - // Resources are always whitelisted if found in whitelisted directories - isWhitelisted = true; + // Resources are always accepted if found in accepted directories + isAccepted = true; } if (!isClassfileOnly) { - // Add resource to list of whitelisted resources, whether for a classfile or non-classfile resource - whitelistedResources.add(resource); + // Add resource to list of accepted resources, whether for a classfile or non-classfile resource + acceptedResources.add(resource); } // Write to log if enabled, and as long as classfile scanning is not disabled, and this is not - // a blacklisted classfile - if (log != null && isWhitelisted) { + // a rejected classfile + if (log != null && isAccepted) { final String type = isClassFile ? "classfile" : "resource"; String logStr; switch (parentMatchStatus) { - case HAS_WHITELISTED_PATH_PREFIX: - logStr = "Found " + type + " within subpackage of whitelisted package: "; + case HAS_ACCEPTED_PATH_PREFIX: + logStr = "Found " + type + " within subpackage of accepted package: "; break; - case AT_WHITELISTED_PATH: - logStr = "Found " + type + " within whitelisted package: "; + case AT_ACCEPTED_PATH: + logStr = "Found " + type + " within accepted package: "; break; - case AT_WHITELISTED_CLASS_PACKAGE: - logStr = "Found specifically-whitelisted " + type + ": "; + case AT_ACCEPTED_CLASS_PACKAGE: + logStr = "Found specifically-accepted " + type + ": "; break; default: - logStr = "Found whitelisted " + type + ": "; + logStr = "Found accepted " + type + ": "; break; } // Precede log entry sort key with "0:file:" so that file entries come before dir entries for @@ -304,13 +303,13 @@ protected void addWhitelistedResource(final Resource resource, final ScanSpecPat */ protected void finishScanPaths(final LogNode log) { if (log != null) { - if (whitelistedResources.isEmpty() && whitelistedClassfileResources.isEmpty()) { - log.log(scanSpec.enableClassInfo ? "No whitelisted classfiles or resources found" - : "Classfile scanning is disabled, and no whitelisted resources found"); - } else if (whitelistedResources.isEmpty()) { - log.log("No whitelisted resources found"); - } else if (whitelistedClassfileResources.isEmpty()) { - log.log(scanSpec.enableClassInfo ? "No whitelisted classfiles found" + if (acceptedResources.isEmpty() && acceptedClassfileResources.isEmpty()) { + log.log(scanSpec.enableClassInfo ? "No accepted classfiles or resources found" + : "Classfile scanning is disabled, and no accepted resources found"); + } else if (acceptedResources.isEmpty()) { + log.log("No accepted resources found"); + } else if (acceptedClassfileResources.isEmpty()) { + log.log(scanSpec.enableClassInfo ? "No accepted classfiles found" : "Classfile scanning is disabled"); } } @@ -355,8 +354,8 @@ abstract void open(final WorkQueue workQueue, final LogN throws InterruptedException; /** - * Scan paths in the classpath element for whitelist/blacklist criteria, creating Resource objects for - * whitelisted and non-blacklisted resources and classfiles. + * Scan paths in the classpath element for accept/reject criteria, creating Resource objects for accepted and + * non-rejected resources and classfiles. * * @param log * the log diff --git a/src/main/java/io/github/classgraph/ClasspathElementFileDir.java b/src/main/java/io/github/classgraph/ClasspathElementFileDir.java index 4e48d1ca1..10ae15d59 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementFileDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementFileDir.java @@ -364,27 +364,27 @@ private void scanDirRecursively(final File dir, final LogNode log) { return; } - // Whitelist/blacklist classpath elements based on dir resource paths - checkResourcePathWhiteBlackList(dirRelativePath, log); + // Accept/reject classpath elements based on dir resource paths + checkResourcePathAcceptReject(dirRelativePath, log); if (skipClasspathElement) { return; } - final ScanSpecPathMatch parentMatchStatus = scanSpec.dirWhitelistMatchStatus(dirRelativePath); - if (parentMatchStatus == ScanSpecPathMatch.HAS_BLACKLISTED_PATH_PREFIX) { - // Reached a non-whitelisted or blacklisted path -- stop the recursive scan + 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 blacklisted directory, stopping recursive scan: " + dirRelativePath); + log.log("Reached rejected directory, stopping recursive scan: " + dirRelativePath); } return; } - if (parentMatchStatus == ScanSpecPathMatch.NOT_WITHIN_WHITELISTED_PATH) { - // Reached a non-whitelisted and non-blacklisted path -- stop the recursive scan + 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 (addWhitelistedResources() precedes log entry with "0:") + // Log dirs after files (addAcceptedResources() precedes log entry with "0:") : log.log("1:" + canonicalPath, "Scanning directory: " + dir + (dir.getPath().equals(canonicalPath) ? "" : " ; canonical path: " + canonicalPath)); @@ -400,8 +400,8 @@ private void scanDirRecursively(final File dir, final LogNode log) { // 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 a whitelisted path - if (parentMatchStatus != ScanSpecPathMatch.ANCESTOR_OF_WHITELISTED_PATH) { + // 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 @@ -416,36 +416,36 @@ private void scanDirRecursively(final File dir, final LogNode log) { continue; } - // Whitelist/blacklist classpath elements based on file resource paths - checkResourcePathWhiteBlackList(fileInDirRelativePath, subLog); + // Accept/reject classpath elements based on file resource paths + checkResourcePathAcceptReject(fileInDirRelativePath, subLog); if (skipClasspathElement) { return; } - // If relative path is whitelisted - if (parentMatchStatus == ScanSpecPathMatch.HAS_WHITELISTED_PATH_PREFIX - || parentMatchStatus == ScanSpecPathMatch.AT_WHITELISTED_PATH - || (parentMatchStatus == ScanSpecPathMatch.AT_WHITELISTED_CLASS_PACKAGE - && scanSpec.classfileIsSpecificallyWhitelisted(fileInDirRelativePath))) { - // Resource is whitelisted + // 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); - addWhitelistedResource(resource, parentMatchStatus, /* isClassfileOnly = */ false, subLog); + addAcceptedResource(resource, parentMatchStatus, /* isClassfileOnly = */ false, subLog); // Save last modified time fileToLastModified.put(fileInDir, fileInDir.lastModified()); } else { if (subLog != null) { - subLog.log("Skipping non-whitelisted file: " + fileInDirRelativePath); + 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 whitelist + // 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); - addWhitelistedResource(resource, parentMatchStatus, /* isClassfileOnly = */ true, subLog); + addAcceptedResource(resource, parentMatchStatus, /* isClassfileOnly = */ true, subLog); fileToLastModified.put(fileInDir, fileInDir.lastModified()); break; } @@ -455,7 +455,7 @@ private void scanDirRecursively(final File dir, final LogNode log) { for (final File fileInDir : filesInDir) { if (fileInDir.isDirectory()) { scanDirRecursively(fileInDir, subLog); - // If a blacklisted classpath element resource path was found, it will set skipClasspathElement + // If a rejected classpath element resource path was found, it will set skipClasspathElement if (skipClasspathElement) { if (subLog != null) { subLog.addElapsedTime(); diff --git a/src/main/java/io/github/classgraph/ClasspathElementModule.java b/src/main/java/io/github/classgraph/ClasspathElementModule.java index 11b3c8e5d..897a4b535 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementModule.java +++ b/src/main/java/io/github/classgraph/ClasspathElementModule.java @@ -275,7 +275,7 @@ void scanPaths(final LogNode log) { try (RecycleOnClose moduleReaderProxyRecycleOnClose // = moduleReaderProxyRecycler.acquireRecycleOnClose()) { - // Look for whitelisted files in the module. + // Look for accepted files in the module. List resourceRelativePaths; try { resourceRelativePaths = moduleReaderProxyRecycleOnClose.get().list(); @@ -322,8 +322,8 @@ void scanPaths(final LogNode log) { continue; } - // Whitelist/blacklist classpath elements based on file resource paths - checkResourcePathWhiteBlackList(relativePath, log); + // Accept/reject classpath elements based on file resource paths + checkResourcePathAcceptReject(relativePath, log); if (skipClasspathElement) { return; } @@ -336,34 +336,34 @@ void scanPaths(final LogNode log) { final boolean parentRelativePathChanged = !parentRelativePath.equals(prevParentRelativePath); final ScanSpecPathMatch parentMatchStatus = // prevParentRelativePath == null || parentRelativePathChanged - ? scanSpec.dirWhitelistMatchStatus(parentRelativePath) + ? scanSpec.dirAcceptMatchStatus(parentRelativePath) : prevParentMatchStatus; prevParentRelativePath = parentRelativePath; prevParentMatchStatus = parentMatchStatus; - if (parentMatchStatus == ScanSpecPathMatch.HAS_BLACKLISTED_PATH_PREFIX) { - // The parent dir or one of its ancestral dirs is blacklisted + if (parentMatchStatus == ScanSpecPathMatch.HAS_REJECTED_PATH_PREFIX) { + // The parent dir or one of its ancestral dirs is rejected if (subLog != null) { - subLog.log("Skipping blacklisted path: " + relativePath); + subLog.log("Skipping rejected path: " + relativePath); } continue; } - // Found non-blacklisted relative path + // Found non-rejected relative path if (allResourcePaths.add(relativePath)) { - // If resource is whitelisted - if (parentMatchStatus == ScanSpecPathMatch.HAS_WHITELISTED_PATH_PREFIX - || parentMatchStatus == ScanSpecPathMatch.AT_WHITELISTED_PATH - || (parentMatchStatus == ScanSpecPathMatch.AT_WHITELISTED_CLASS_PACKAGE - && scanSpec.classfileIsSpecificallyWhitelisted(relativePath))) { - // Add whitelisted resource - addWhitelistedResource(newResource(relativePath), parentMatchStatus, + // If resource is accepted + if (parentMatchStatus == ScanSpecPathMatch.HAS_ACCEPTED_PATH_PREFIX + || parentMatchStatus == ScanSpecPathMatch.AT_ACCEPTED_PATH + || (parentMatchStatus == ScanSpecPathMatch.AT_ACCEPTED_CLASS_PACKAGE + && scanSpec.classfileIsSpecificallyAccepted(relativePath))) { + // Add accepted resource + addAcceptedResource(newResource(relativePath), parentMatchStatus, /* isClassfileOnly = */ false, subLog); } else if (scanSpec.enableClassInfo && relativePath.equals("module-info.class")) { - // Add module descriptor as a whitelisted classfile resource, so that it is scanned, + // Add module descriptor as an accepted classfile resource, so that it is scanned, // but don't add it to the list of resources in the ScanResult, since it is not - // in a whitelisted package (#352) - addWhitelistedResource(newResource(relativePath), parentMatchStatus, + // in an accepted package (#352) + addAcceptedResource(newResource(relativePath), parentMatchStatus, /* isClassfileOnly = */ true, subLog); } } diff --git a/src/main/java/io/github/classgraph/ClasspathElementPathDir.java b/src/main/java/io/github/classgraph/ClasspathElementPathDir.java index a6fdc67de..85830c51e 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementPathDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementPathDir.java @@ -372,27 +372,27 @@ private void scanPathRecursively(final Path path, final LogNode log) { return; } - // Whitelist/blacklist classpath elements based on dir resource paths - checkResourcePathWhiteBlackList(dirRelativePathStr, log); + // Accept/reject classpath elements based on dir resource paths + checkResourcePathAcceptReject(dirRelativePathStr, log); if (skipClasspathElement) { return; } - final ScanSpecPathMatch parentMatchStatus = scanSpec.dirWhitelistMatchStatus(dirRelativePathStr); - if (parentMatchStatus == ScanSpecPathMatch.HAS_BLACKLISTED_PATH_PREFIX) { - // Reached a non-whitelisted or blacklisted path -- stop the recursive scan + final ScanSpecPathMatch parentMatchStatus = scanSpec.dirAcceptMatchStatus(dirRelativePathStr); + if (parentMatchStatus == ScanSpecPathMatch.HAS_REJECTED_PATH_PREFIX) { + // Reached a non-accepted or rejected path -- stop the recursive scan if (log != null) { - log.log("Reached blacklisted directory, stopping recursive scan: " + dirRelativePathStr); + log.log("Reached rejected directory, stopping recursive scan: " + dirRelativePathStr); } return; } - if (parentMatchStatus == ScanSpecPathMatch.NOT_WITHIN_WHITELISTED_PATH) { - // Reached a non-whitelisted and non-blacklisted path -- stop the recursive scan + 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 (addWhitelistedResources() precedes log entry with "0:") + // Log dirs after files (addAcceptedResources() precedes log entry with "0:") : log.log("1:" + canonicalPath, "Scanning Path: " + FastPathResolver.resolve(path.toString()) + (path.equals(canonicalPath) ? "" @@ -415,8 +415,8 @@ private void scanPathRecursively(final Path path, final LogNode log) { // 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 a whitelisted path - if (parentMatchStatus != ScanSpecPathMatch.ANCESTOR_OF_WHITELISTED_PATH) { + // 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) { // Process files in dir before recursing @@ -430,20 +430,20 @@ private void scanPathRecursively(final Path path, final LogNode log) { continue; } - // Whitelist/blacklist classpath elements based on file resource paths - checkResourcePathWhiteBlackList(subPathRelativeStr, subLog); + // Accept/reject classpath elements based on file resource paths + checkResourcePathAcceptReject(subPathRelativeStr, subLog); if (skipClasspathElement) { return; } - // If relative path is whitelisted - if (parentMatchStatus == ScanSpecPathMatch.HAS_WHITELISTED_PATH_PREFIX - || parentMatchStatus == ScanSpecPathMatch.AT_WHITELISTED_PATH - || (parentMatchStatus == ScanSpecPathMatch.AT_WHITELISTED_CLASS_PACKAGE - && scanSpec.classfileIsSpecificallyWhitelisted(subPathRelativeStr))) { - // Resource is whitelisted + // 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(subPathRelativeStr))) { + // Resource is accepted final Resource resource = newResource(subPath, nestedJarHandler); - addWhitelistedResource(resource, parentMatchStatus, /* isClassfileOnly = */ false, subLog); + addAcceptedResource(resource, parentMatchStatus, /* isClassfileOnly = */ false, subLog); // Save last modified time try { @@ -453,17 +453,17 @@ private void scanPathRecursively(final Path path, final LogNode log) { } } else { if (subLog != null) { - subLog.log("Skipping non-whitelisted file: " + subPathRelative); + subLog.log("Skipping non-accepted file: " + subPathRelative); } } } } } else if (scanSpec.enableClassInfo && dirRelativePathStr.equals("/")) { - // Always check for module descriptor in package root, even if package root isn't in whitelist + // 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); - addWhitelistedResource(resource, parentMatchStatus, /* isClassfileOnly = */ true, subLog); + addAcceptedResource(resource, parentMatchStatus, /* isClassfileOnly = */ true, subLog); try { fileToLastModified.put(subPath.toFile(), subPath.toFile().lastModified()); } catch (final UnsupportedOperationException e) { @@ -478,7 +478,7 @@ private void scanPathRecursively(final Path path, final LogNode log) { try { if (Files.isDirectory(subPath)) { scanPathRecursively(subPath, subLog); - // If a blacklisted classpath element resource path was found, it will set skipClasspathElement + // If a rejected classpath element resource path was found, it will set skipClasspathElement if (skipClasspathElement) { if (subLog != null) { subLog.addElapsedTime(); diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index 21483ae07..807901dfc 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -72,7 +72,7 @@ class ClasspathElementZip extends ClasspathElement { 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-blacklisted zip entries. */ + /** A map from relative path to {@link Resource} for non-rejected zip entries. */ private final ConcurrentHashMap relativePathToResource = new ConcurrentHashMap<>(); /** The nested jar handler. */ private final NestedJarHandler nestedJarHandler; @@ -125,9 +125,9 @@ void open(final WorkQueue workQueue, final LogNode log) final int plingIdx = rawPath.indexOf('!'); final String outermostZipFilePathResolved = FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, plingIdx < 0 ? rawPath : rawPath.substring(0, plingIdx)); - if (!scanSpec.jarWhiteBlackList.isWhitelistedAndNotBlacklisted(outermostZipFilePathResolved)) { + if (!scanSpec.jarAcceptReject.isAcceptedAndNotRejected(outermostZipFilePathResolved)) { if (subLog != null) { - subLog.log("Skipping jarfile that is blacklisted or not whitelisted: " + rawPath); + subLog.log("Skipping jarfile that is rejected or not accepted: " + rawPath); } skipClasspathElement = true; return; @@ -166,7 +166,7 @@ void open(final WorkQueue workQueue, final LogNode log) } if (!scanSpec.enableSystemJarsAndModules && logicalZipFile.isJREJar) { - // Found a blacklisted JRE jar that was not caught by filtering for rt.jar in ClasspathFinder + // Found a rejected JRE jar that was not caught by filtering for rt.jar in ClasspathFinder // (the isJREJar value was set by detecting JRE headers in the jar's manifest file) if (subLog != null) { subLog.log("Ignoring JRE jar: " + rawPath); @@ -175,9 +175,9 @@ void open(final WorkQueue workQueue, final LogNode log) return; } - if (!logicalZipFile.isWhitelistedAndNotBlacklisted(scanSpec.jarWhiteBlackList)) { + if (!logicalZipFile.isAcceptedAndNotRejected(scanSpec.jarAcceptReject)) { if (subLog != null) { - subLog.log("Skipping jarfile that is blacklisted or not whitelisted: " + rawPath); + subLog.log("Skipping jarfile that is rejected or not accepted: " + rawPath); } skipClasspathElement = true; return; @@ -551,8 +551,8 @@ void scanPaths(final LogNode log) { } } - // Whitelist/blacklist classpath elements based on file resource paths - checkResourcePathWhiteBlackList(relativePath, log); + // Accept/reject classpath elements based on file resource paths + checkResourcePathAcceptReject(relativePath, log); if (skipClasspathElement) { return; } @@ -563,15 +563,15 @@ void scanPaths(final LogNode log) { final String parentRelativePath = lastSlashIdx < 0 ? "/" : relativePath.substring(0, lastSlashIdx + 1); final boolean parentRelativePathChanged = !parentRelativePath.equals(prevParentRelativePath); final ScanSpecPathMatch parentMatchStatus = // - parentRelativePathChanged ? scanSpec.dirWhitelistMatchStatus(parentRelativePath) + parentRelativePathChanged ? scanSpec.dirAcceptMatchStatus(parentRelativePath) : prevParentMatchStatus; prevParentRelativePath = parentRelativePath; prevParentMatchStatus = parentMatchStatus; - if (parentMatchStatus == ScanSpecPathMatch.HAS_BLACKLISTED_PATH_PREFIX) { - // The parent dir or one of its ancestral dirs is blacklisted + if (parentMatchStatus == ScanSpecPathMatch.HAS_REJECTED_PATH_PREFIX) { + // The parent dir or one of its ancestral dirs is rejected if (subLog != null) { - subLog.log("Skipping blacklisted path: " + relativePath); + subLog.log("Skipping rejected path: " + relativePath); } continue; } @@ -579,18 +579,18 @@ void scanPaths(final LogNode log) { // Add the ZipEntry path as a Resource final Resource resource = newResource(zipEntry, relativePath); if (relativePathToResource.putIfAbsent(relativePath, resource) == null) { - // If resource is whitelisted - if (parentMatchStatus == ScanSpecPathMatch.HAS_WHITELISTED_PATH_PREFIX - || parentMatchStatus == ScanSpecPathMatch.AT_WHITELISTED_PATH - || (parentMatchStatus == ScanSpecPathMatch.AT_WHITELISTED_CLASS_PACKAGE - && scanSpec.classfileIsSpecificallyWhitelisted(relativePath))) { - // Resource is whitelisted - addWhitelistedResource(resource, parentMatchStatus, /* isClassfileOnly = */ false, subLog); + // If resource is accepted + if (parentMatchStatus == ScanSpecPathMatch.HAS_ACCEPTED_PATH_PREFIX + || parentMatchStatus == ScanSpecPathMatch.AT_ACCEPTED_PATH + || (parentMatchStatus == ScanSpecPathMatch.AT_ACCEPTED_CLASS_PACKAGE + && scanSpec.classfileIsSpecificallyAccepted(relativePath))) { + // Resource is accepted + addAcceptedResource(resource, parentMatchStatus, /* isClassfileOnly = */ false, subLog); } else if (scanSpec.enableClassInfo && relativePath.equals("module-info.class")) { - // Add module descriptor as a whitelisted classfile resource, so that it is scanned, + // Add module descriptor as an accepted classfile resource, so that it is scanned, // but don't add it to the list of resources in the ScanResult, since it is not - // in a whitelisted package (#352) - addWhitelistedResource(resource, parentMatchStatus, /* isClassfileOnly = */ true, subLog); + // in an accepted package (#352) + addAcceptedResource(resource, parentMatchStatus, /* isClassfileOnly = */ true, subLog); } } } diff --git a/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java b/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java index 7c6bf3c73..4254283ae 100644 --- a/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java +++ b/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java @@ -394,7 +394,7 @@ void convertWrapperArraysToPrimitiveArrays(final ClassInfo annotationClassInfo, if (elt == null) { throw new IllegalArgumentException("Illegal null value for array of element type " + targetElementTypeName + " in parameter " + paramName + " of annotation class " - + (annotationClassInfo == null ? "" + + (annotationClassInfo == null ? "" : annotationClassInfo.getName())); } intArrayValue[j] = objectArrayValue[j].integerValue; @@ -408,7 +408,7 @@ void convertWrapperArraysToPrimitiveArrays(final ClassInfo annotationClassInfo, if (elt == null) { throw new IllegalArgumentException("Illegal null value for array of element type " + targetElementTypeName + " in parameter " + paramName + " of annotation class " - + (annotationClassInfo == null ? "" + + (annotationClassInfo == null ? "" : annotationClassInfo.getName())); } longArrayValue[j] = objectArrayValue[j].longValue; @@ -422,7 +422,7 @@ void convertWrapperArraysToPrimitiveArrays(final ClassInfo annotationClassInfo, if (elt == null) { throw new IllegalArgumentException("Illegal null value for array of element type " + targetElementTypeName + " in parameter " + paramName + " of annotation class " - + (annotationClassInfo == null ? "" + + (annotationClassInfo == null ? "" : annotationClassInfo.getName())); } shortArrayValue[j] = objectArrayValue[j].shortValue; @@ -436,7 +436,7 @@ void convertWrapperArraysToPrimitiveArrays(final ClassInfo annotationClassInfo, if (elt == null) { throw new IllegalArgumentException("Illegal null value for array of element type " + targetElementTypeName + " in parameter " + paramName + " of annotation class " - + (annotationClassInfo == null ? "" + + (annotationClassInfo == null ? "" : annotationClassInfo.getName())); } charArrayValue[j] = objectArrayValue[j].characterValue; @@ -450,7 +450,7 @@ void convertWrapperArraysToPrimitiveArrays(final ClassInfo annotationClassInfo, if (elt == null) { throw new IllegalArgumentException("Illegal null value for array of element type " + targetElementTypeName + " in parameter " + paramName + " of annotation class " - + (annotationClassInfo == null ? "" + + (annotationClassInfo == null ? "" : annotationClassInfo.getName())); } floatArrayValue[j] = objectArrayValue[j].floatValue; @@ -464,7 +464,7 @@ void convertWrapperArraysToPrimitiveArrays(final ClassInfo annotationClassInfo, if (elt == null) { throw new IllegalArgumentException("Illegal null value for array of element type " + targetElementTypeName + " in parameter " + paramName + " of annotation class " - + (annotationClassInfo == null ? "" + + (annotationClassInfo == null ? "" : annotationClassInfo.getName())); } doubleArrayValue[j] = objectArrayValue[j].doubleValue; @@ -478,7 +478,7 @@ void convertWrapperArraysToPrimitiveArrays(final ClassInfo annotationClassInfo, if (elt == null) { throw new IllegalArgumentException("Illegal null value for array of element type " + targetElementTypeName + " in parameter " + paramName + " of annotation class " - + (annotationClassInfo == null ? "" + + (annotationClassInfo == null ? "" : annotationClassInfo.getName())); } booleanArrayValue[j] = objectArrayValue[j].booleanValue; @@ -492,7 +492,7 @@ void convertWrapperArraysToPrimitiveArrays(final ClassInfo annotationClassInfo, if (elt == null) { throw new IllegalArgumentException("Illegal null value for array of element type " + targetElementTypeName + " in parameter " + paramName + " of annotation class " - + (annotationClassInfo == null ? "" + + (annotationClassInfo == null ? "" : annotationClassInfo.getName())); } byteArrayValue[j] = objectArrayValue[j].byteValue; diff --git a/src/main/java/io/github/classgraph/Resource.java b/src/main/java/io/github/classgraph/Resource.java index 0263904ad..54e226c6e 100644 --- a/src/main/java/io/github/classgraph/Resource.java +++ b/src/main/java/io/github/classgraph/Resource.java @@ -47,7 +47,7 @@ import nonapi.io.github.classgraph.utils.URLPathEncoder; /** - * A classpath or module path resource (i.e. file) that was found in a whitelisted/non-blacklisted package inside a + * A classpath or module path resource (i.e. file) that was found in an accepted/non-rejected package inside a * classpath element or module. */ public abstract class Resource implements Closeable, Comparable { @@ -68,7 +68,7 @@ public abstract class Resource implements Closeable, Comparable { /** * The {@link LogNode} used to log that the resource was found when classpath element paths are scanned. In the - * case of whitelisted classfile resources, sublog entries are added when the classfile's contents are scanned. + * case of accepted classfile resources, sublog entries are added when the classfile's contents are scanned. */ LogNode scanLog; diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index 35f8fbaae..c0d032ecf 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -71,8 +71,8 @@ public final class ScanResult implements Closeable, AutoCloseable { /** 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 whitelisted packages. */ - private ResourceList allWhitelistedResourcesCached; + /** A list of all files that were found in accepted packages. */ + private ResourceList allAcceptedResourcesCached; /** The number of times {@link #getResourcesWithPath(String)} has been called. */ private final AtomicInteger getResourcesWithPathCallCount = new AtomicInteger(); @@ -80,7 +80,7 @@ public final class ScanResult implements Closeable, AutoCloseable { /** * The map from path (relative to package root) to a list of {@link Resource} elements with the matching path. */ - private Map pathToWhitelistedResourcesCached; + private Map pathToAcceptedResourcesCached; /** The map from class name to {@link ClassInfo}. */ Map classNameToClassInfo; @@ -319,7 +319,7 @@ private void indexResourcesAndClassInfo() { // Don't add self-references, or references to Object if (refdClassInfo != null && !ci.equals(refdClassInfo) && !refdClassInfo.getName().equals("java.lang.Object") - // Only add class to result if it is whitelisted, or external classes are enabled + // Only add class to result if it is accepted, or external classes are enabled && (!refdClassInfo.isExternalClass() || scanSpec.enableExternalClasses)) { refdClassInfo.setScanResult(this); refdClassesFiltered.add(refdClassInfo); @@ -455,52 +455,52 @@ public ModulePathInfo getModulePathInfo() { /** * Get the list of all resources. * - * @return A list of all resources (including classfiles and non-classfiles) found in whitelisted packages. + * @return A list of all resources (including classfiles and non-classfiles) found in accepted packages. */ public ResourceList getAllResources() { - if (allWhitelistedResourcesCached == null) { + if (allAcceptedResourcesCached == null) { // Index Resource objects by path - final ResourceList whitelistedResourcesList = new ResourceList(); + final ResourceList acceptedResourcesList = new ResourceList(); for (final ClasspathElement classpathElt : classpathOrder) { - whitelistedResourcesList.addAll(classpathElt.whitelistedResources); + acceptedResourcesList.addAll(classpathElt.acceptedResources); } // Set atomically for thread safety - allWhitelistedResourcesCached = whitelistedResourcesList; + allAcceptedResourcesCached = acceptedResourcesList; } - return allWhitelistedResourcesCached; + return allAcceptedResourcesCached; } /** * Get a map from resource path to {@link Resource} for all resources (including classfiles and non-classfiles) - * found in whitelisted packages. + * found in accepted packages. * * @return The map from resource path to {@link Resource} for all resources (including classfiles and - * non-classfiles) found in whitelisted packages. + * non-classfiles) found in accepted packages. */ public Map getAllResourcesAsMap() { - if (pathToWhitelistedResourcesCached == null) { - final Map pathToWhitelistedResourceListMap = new HashMap<>(); + if (pathToAcceptedResourcesCached == null) { + final Map pathToAcceptedResourceListMap = new HashMap<>(); for (final Resource res : getAllResources()) { - ResourceList resList = pathToWhitelistedResourceListMap.get(res.getPath()); + ResourceList resList = pathToAcceptedResourceListMap.get(res.getPath()); if (resList == null) { - pathToWhitelistedResourceListMap.put(res.getPath(), resList = new ResourceList()); + pathToAcceptedResourceListMap.put(res.getPath(), resList = new ResourceList()); } resList.add(res); } // Set atomically for thread safety - pathToWhitelistedResourcesCached = pathToWhitelistedResourceListMap; + pathToAcceptedResourcesCached = pathToAcceptedResourceListMap; } - return pathToWhitelistedResourcesCached; + return pathToAcceptedResourcesCached; } /** - * Get the list of all resources found in whitelisted packages that have the given path, relative to the package + * 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 whitelisted 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. + * @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()) { @@ -515,7 +515,7 @@ public ResourceList getResourcesWithPath(final String resourcePath) { // 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.whitelistedResources) { + for (final Resource res : classpathElt.acceptedResources) { if (res.getPath().equals(path)) { if (matchingResources == null) { matchingResources = new ResourceList(); @@ -529,20 +529,20 @@ public ResourceList getResourcesWithPath(final String resourcePath) { } /** - * Get the list of all resources found in any classpath element, whether in whitelisted packages or not (as - * long as the resource is not blacklisted), 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-whitelisted resource, particularly when scanning directory classpath elements, because recursive - * scanning terminates once there are no possible whitelisted resources below a given directory. However, - * resources in ancestral directories of whitelisted directories can be found using this method. + * 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 whitelisted packages or not (as - * long as the resource is not blacklisted), that have the given path, relative to the package root - * of the classpath element. May match several resources, up to one per classpath element. + * @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 getResourcesWithPathIgnoringWhitelist(final String resourcePath) { + public ResourceList getResourcesWithPathIgnoringAccept(final String resourcePath) { if (closed.get()) { throw new IllegalArgumentException("Cannot use a ScanResult after it has been closed"); } @@ -559,22 +559,36 @@ public ResourceList getResourcesWithPathIgnoringWhitelist(final String resourceP } /** - * Get the list of all resources found in whitelisted packages that have the requested leafname. + * @deprecated 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. + */ + @Deprecated + public ResourceList getResourcesWithPathIgnoringWhitelist(final String resourcePath) { + return getResourcesWithPathIgnoringAccept(resourcePath); + } + + /** + * 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 whitelisted packages that have the requested leafname. + * @return A list of all resources found in accepted packages that have the requested leafname. */ public ResourceList getResourcesWithLeafName(final String leafName) { if (closed.get()) { throw new IllegalArgumentException("Cannot use a ScanResult after it has been closed"); } - final ResourceList allWhitelistedResources = getAllResources(); - if (allWhitelistedResources.isEmpty()) { + final ResourceList allAcceptedResources = getAllResources(); + if (allAcceptedResources.isEmpty()) { return ResourceList.EMPTY_LIST; } else { final ResourceList filteredResources = new ResourceList(); - for (final Resource classpathResource : allWhitelistedResources) { + for (final Resource classpathResource : allAcceptedResources) { final String relativePath = classpathResource.getPath(); final int lastSlashIdx = relativePath.lastIndexOf('/'); if (relativePath.substring(lastSlashIdx + 1).equals(leafName)) { @@ -586,18 +600,18 @@ public ResourceList getResourcesWithLeafName(final String leafName) { } /** - * Get the list of all resources found in whitelisted 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 whitelisted packages that have the requested filename extension. + * @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()) { throw new IllegalArgumentException("Cannot use a ScanResult after it has been closed"); } - final ResourceList allWhitelistedResources = getAllResources(); - if (allWhitelistedResources.isEmpty()) { + final ResourceList allAcceptedResources = getAllResources(); + if (allAcceptedResources.isEmpty()) { return ResourceList.EMPTY_LIST; } else { String bareExtension = extension; @@ -605,7 +619,7 @@ public ResourceList getResourcesWithExtension(final String extension) { bareExtension = bareExtension.substring(1); } final ResourceList filteredResources = new ResourceList(); - for (final Resource classpathResource : allWhitelistedResources) { + for (final Resource classpathResource : allAcceptedResources) { final String relativePath = classpathResource.getPath(); final int lastSlashIdx = relativePath.lastIndexOf('/'); final int lastDotIdx = relativePath.lastIndexOf('.'); @@ -619,23 +633,22 @@ public ResourceList getResourcesWithExtension(final String extension) { } /** - * Get the list of all resources found in whitelisted 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 pattern. * * @param pattern * A pattern to match {@link Resource} paths with. - * @return A list of all resources found in whitelisted packages that have a path matching the requested - * pattern. + * @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()) { throw new IllegalArgumentException("Cannot use a ScanResult after it has been closed"); } - final ResourceList allWhitelistedResources = getAllResources(); - if (allWhitelistedResources.isEmpty()) { + final ResourceList allAcceptedResources = getAllResources(); + if (allAcceptedResources.isEmpty()) { return ResourceList.EMPTY_LIST; } else { final ResourceList filteredResources = new ResourceList(); - for (final Resource classpathResource : allWhitelistedResources) { + for (final Resource classpathResource : allAcceptedResources) { final String relativePath = classpathResource.getPath(); if (pattern.matcher(relativePath).matches()) { filteredResources.add(classpathResource); @@ -721,15 +734,15 @@ public PackageInfoList getPackageInfo() { // Class dependencies /** - * Get a map from the {@link ClassInfo} object for each whitelisted class to a list of the classes referenced by + * 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-whitelisted classes to appear in the result. See also {@link #getReverseClassDependencyMap()}, which - * inverts the map. + * 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 whitelisted 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 + * @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() { @@ -742,15 +755,15 @@ public Map getClassDependencyMap() { /** * Get the reverse class dependency map, i.e. a map from the {@link ClassInfo} object for each dependency class - * (whitelisted or not) to a list of the whitelisted classes that referenced that class as a dependency (i.e. - * returns a map from dependencies to dependents). Note that you need to call + * (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-whitelisted classes to appear in the result. See also {@link #getClassDependencyMap}. + * non-accepted classes to appear in the result. See also {@link #getClassDependencyMap}. * - * @return A map from a {@link ClassInfo} object for each dependency class (whitelisted or not) to a list of the - * whitelisted classes that referenced that class as a dependency (i.e. returns a map from dependencies - * to dependents). + * @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() { final Map> revMapSet = new HashMap<>(); @@ -775,7 +788,7 @@ public Map getReverseClassDependencyMap() { /** * Get the {@link ClassInfo} object for the named class, or null if no class of the requested name was found in - * a whitelisted/non-blacklisted package during the scan. + * an accepted/non-rejected package during the scan. * * @param className * The class name. @@ -794,7 +807,7 @@ public ClassInfo getClassInfo(final String className) { /** * Get all classes, interfaces and annotations found during the scan. * - * @return A list of all whitelisted 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()) { @@ -856,7 +869,7 @@ public Map getAllClassesAsMap() { /** * Get all standard (non-interface/non-annotation) classes found during the scan. * - * @return A list of all whitelisted 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()) { @@ -974,7 +987,7 @@ public ClassInfoList getClassesWithFieldAnnotation(final String fieldAnnotationN * Get all interface classes found during the scan (not including annotations, which are also technically * interfaces). See also {@link #getAllInterfacesAndAnnotations()}. * - * @return A list of all whitelisted 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 getAllInterfaces() { if (closed.get()) { @@ -1048,7 +1061,7 @@ public ClassInfoList getAllAnnotations() { * Get all interface or annotation classes found during the scan. (Annotations are technically interfaces, and * they can be implemented.) * - * @return A list of all whitelisted 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()) { @@ -1110,8 +1123,8 @@ public ClassInfoList getAnnotationsOnClass(final String className) { /** * 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 whitelist criteria -- you need to perform - * a full scan to detect those changes. + * 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. */ @@ -1132,16 +1145,16 @@ public boolean classpathContentsModifiedSinceScan() { } /** - * Find the maximum last-modified timestamp of any whitelisted file/directory/jarfile encountered during the - * scan. Checks the current timestamps, so this should increase between calls if something changes in - * whitelisted paths. Assumes both file and system timestamps were generated from clocks whose time was - * accurate. Ignores timestamps greater than the system time. + * 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 * run twice during the same runtime session. * - * @return the maximum last-modified time for whitelisted 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()) { @@ -1403,16 +1416,16 @@ public void close() { classpathOrder.clear(); classpathOrder = null; } - if (allWhitelistedResourcesCached != null) { - for (final Resource classpathResource : allWhitelistedResourcesCached) { + if (allAcceptedResourcesCached != null) { + for (final Resource classpathResource : allAcceptedResourcesCached) { classpathResource.close(); } - allWhitelistedResourcesCached.clear(); - allWhitelistedResourcesCached = null; + allAcceptedResourcesCached.clear(); + allAcceptedResourcesCached = null; } - if (pathToWhitelistedResourcesCached != null) { - pathToWhitelistedResourcesCached.clear(); - pathToWhitelistedResourcesCached = null; + if (pathToAcceptedResourcesCached != null) { + pathToAcceptedResourcesCached.clear(); + pathToAcceptedResourcesCached = null; } classGraphClassLoader = null; if (classNameToClassInfo != null) { diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 7d8f89217..2d44d19a9 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -149,9 +149,9 @@ class Scanner implements Callable { scanSpec.sortPrefixes(); scanSpec.log(topLevelLog); if (topLevelLog != null) { - if (scanSpec.pathWhiteBlackList != null - && scanSpec.packagePrefixWhiteBlackList.isSpecificallyWhitelisted("")) { - topLevelLog.log("Note: There is no need to whitelist the root package (\"\") -- not whitelisting " + if (scanSpec.pathAcceptReject != null + && scanSpec.packagePrefixAcceptReject.isSpecificallyAccepted("")) { + topLevelLog.log("Note: There is no need to accept the root package (\"\") -- not accepting " + "anything will have the same effect of causing all packages to be scanned"); } topLevelLog.log("Number of worker threads: " + numParallelTasks); @@ -188,13 +188,12 @@ class Scanner implements Callable { for (final ModuleRef systemModuleRef : systemModuleRefs) { final String moduleName = systemModuleRef.getName(); if ( - // If scanning system packages and modules is enabled and white/blacklist is empty, + // If scanning system packages and modules is enabled and accept/reject criteria are empty, // then scan all system modules (scanSpec.enableSystemJarsAndModules - && scanSpec.moduleWhiteBlackList.whitelistAndBlacklistAreEmpty()) - // Otherwise only scan specifically whitelisted system modules - || scanSpec.moduleWhiteBlackList - .isSpecificallyWhitelistedAndNotBlacklisted(moduleName)) { + && scanSpec.moduleAcceptReject.acceptAndRejectAreEmpty()) + // Otherwise only scan specifically accepted system modules + || scanSpec.moduleAcceptReject.isSpecificallyAcceptedAndNotRejected(moduleName)) { // Create a new ClasspathElementModule final ClasspathElementModule classpathElementModule = new ClasspathElementModule( systemModuleRef, defaultClassLoader, @@ -204,8 +203,8 @@ class Scanner implements Callable { classpathElementModule.open(/* ignored */ null, classpathFinderLog); } else { if (classpathFinderLog != null) { - classpathFinderLog.log( - "Skipping non-whitelisted or blacklisted system module: " + moduleName); + classpathFinderLog + .log("Skipping non-accepted or rejected system module: " + moduleName); } } } @@ -217,7 +216,7 @@ class Scanner implements Callable { if (moduleName == null) { moduleName = ""; } - if (scanSpec.moduleWhiteBlackList.isWhitelistedAndNotBlacklisted(moduleName)) { + if (scanSpec.moduleAcceptReject.isAcceptedAndNotRejected(moduleName)) { // Create a new ClasspathElementModule final ClasspathElementModule classpathElementModule = new ClasspathElementModule( nonSystemModuleRef, defaultClassLoader, @@ -227,8 +226,7 @@ class Scanner implements Callable { classpathElementModule.open(/* ignored */ null, classpathFinderLog); } else { if (classpathFinderLog != null) { - classpathFinderLog - .log("Skipping non-whitelisted or blacklisted module: " + moduleName); + classpathFinderLog.log("Skipping non-accepted or rejected module: " + moduleName); } } } @@ -648,13 +646,13 @@ private static class ClassfileScannerWorkUnitProcessor implements WorkUnitProces private final List classpathOrder; /** - * The names of whitelisted classes found in the classpath while scanning paths within classpath elements. + * The names of accepted classes found in the classpath while scanning paths within classpath elements. */ - private final Set whitelistedClassNamesFound; + private final Set acceptedClassNamesFound; /** - * The names of external (non-whitelisted) classes scheduled for extended scanning (where scanning is - * extended upwards to superclasses, interfaces and annotations). + * The names of external (non-accepted) classes scheduled for extended scanning (where scanning is extended + * upwards to superclasses, interfaces and annotations). */ private final Set classNamesScheduledForExtendedScanning = Collections .newSetFromMap(new ConcurrentHashMap()); @@ -672,18 +670,18 @@ private static class ClassfileScannerWorkUnitProcessor implements WorkUnitProces * the scan spec * @param classpathOrder * the classpath order - * @param whitelistedClassNamesFound - * the names of whitelisted classes found in the classpath while scanning paths within classpath + * @param acceptedClassNamesFound + * the names of accepted classes found in the classpath while scanning paths within classpath * elements. * @param scannedClassfiles * the {@link Classfile} objects created by scanning classfiles */ public ClassfileScannerWorkUnitProcessor(final ScanSpec scanSpec, - final List classpathOrder, final Set whitelistedClassNamesFound, + final List classpathOrder, final Set acceptedClassNamesFound, final Queue scannedClassfiles) { this.scanSpec = scanSpec; this.classpathOrder = classpathOrder; - this.whitelistedClassNamesFound = whitelistedClassNamesFound; + this.acceptedClassNamesFound = acceptedClassNamesFound; this.scannedClassfiles = scannedClassfiles; } @@ -716,7 +714,7 @@ public void processWorkUnit(final ClassfileScanWorkUnit workUnit, try { // Parse classfile binary format, creating a Classfile object final Classfile classfile = new Classfile(workUnit.classpathElement, classpathOrder, - whitelistedClassNamesFound, classNamesScheduledForExtendedScanning, + acceptedClassNamesFound, classNamesScheduledForExtendedScanning, workUnit.classfileResource.getPath(), workUnit.classfileResource, workUnit.isExternalClass, stringInternMap, workQueue, scanSpec, subLog); @@ -874,10 +872,10 @@ private void preprocessClasspathElementsByType(final List fina * the mask log */ private void maskClassfiles(final List classpathElementOrder, final LogNode maskLog) { - final Set whitelistedClasspathRelativePathsFound = new HashSet<>(); + final Set acceptedClasspathRelativePathsFound = new HashSet<>(); for (int classpathIdx = 0; classpathIdx < classpathElementOrder.size(); classpathIdx++) { final ClasspathElement classpathElement = classpathElementOrder.get(classpathIdx); - classpathElement.maskClassfiles(classpathIdx, whitelistedClasspathRelativePathsFound, maskLog); + classpathElement.maskClassfiles(classpathIdx, acceptedClasspathRelativePathsFound, maskLog); } if (maskLog != null) { maskLog.addElapsedTime(); @@ -925,16 +923,16 @@ private ScanResult performScan(final List finalClasspathEltOrd final Map packageNameToPackageInfo = new HashMap<>(); final Map moduleNameToModuleInfo = new HashMap<>(); if (scanSpec.enableClassInfo) { - // Get whitelisted classfile order + // Get accepted classfile order final List classfileScanWorkItems = new ArrayList<>(); - final Set whitelistedClassNamesFound = 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.whitelistedClassfileResources) { - // Create a set of names of all whitelisted classes found in classpath element paths, + for (final Resource resource : classpathElement.acceptedClassfileResources) { + // Create a set of names of all accepted classes found in classpath element paths, // and double-check that a class is not going to be scanned twice final String className = JarUtils.classfilePathToClassName(resource.getPath()); - if (!whitelistedClassNamesFound.add(className) && !className.equals("module-info") + if (!acceptedClassNamesFound.add(className) && !className.equals("module-info") && !className.equals("package-info") && !className.endsWith(".package-info")) { // The class should not be scheduled more than once for scanning, since classpath // masking was already applied @@ -953,7 +951,7 @@ private ScanResult performScan(final List finalClasspathEltOrd final Queue scannedClassfiles = new ConcurrentLinkedQueue<>(); final ClassfileScannerWorkUnitProcessor classfileWorkUnitProcessor = // new ClassfileScannerWorkUnitProcessor(scanSpec, finalClasspathEltOrder, - Collections.unmodifiableSet(whitelistedClassNamesFound), scannedClassfiles); + Collections.unmodifiableSet(acceptedClassNamesFound), scannedClassfiles); processWorkUnits(classfileScanWorkItems, topLevelLog == null ? null : topLevelLog.log("Scanning classfiles"), classfileWorkUnitProcessor); @@ -1070,7 +1068,7 @@ private ScanResult openClasspathElementsThenScan() throws InterruptedException, } } - // In parallel, scan paths within each classpath element, comparing them against whitelist/blacklist + // In parallel, scan paths within each classpath element, comparing them against accept/reject processWorkUnits(finalClasspathEltOrder, topLevelLog == null ? null : topLevelLog.log("Scanning classpath elements"), new WorkUnitProcessor() { @@ -1083,12 +1081,12 @@ public void processWorkUnit(final ClasspathElement classpathElement, } }); - // Filter out classpath elements that do not contain required whitelisted paths. + // Filter out classpath elements that do not contain required accepted paths. List finalClasspathEltOrderFiltered = finalClasspathEltOrder; - if (!scanSpec.classpathElementResourcePathWhiteBlackList.whitelistIsEmpty()) { + if (!scanSpec.classpathElementResourcePathAcceptReject.acceptIsEmpty()) { finalClasspathEltOrderFiltered = new ArrayList<>(finalClasspathEltOrder.size()); for (final ClasspathElement classpathElement : finalClasspathEltOrder) { - if (classpathElement.containsSpecificallyWhitelistedClasspathElementResourcePath) { + if (classpathElement.containsSpecificallyAcceptedClasspathElementResourcePath) { finalClasspathEltOrderFiltered.add(classpathElement); } } 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 7ac885dd7..5eb15b9ea 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java @@ -169,7 +169,7 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { classLoaderOrderRespectingParentDelegation = contextClassLoaders; } else if (scanSpec.overrideClassLoaders == null) { - // If system jars are not blacklisted, add JRE rt.jar to the beginning of the classpath + // If system jars are not rejected, add JRE rt.jar to the beginning of the classpath final String jreRtJar = SystemJarFinder.getJreRtJarPath(); // Add rt.jar and/or lib/ext jars to beginning of classpath, if enabled @@ -186,10 +186,10 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { + jreRtJar); } } - final boolean scanAllLibOrExtJars = !scanSpec.libOrExtJarWhiteBlackList.whitelistAndBlacklistAreEmpty(); + final boolean scanAllLibOrExtJars = !scanSpec.libOrExtJarAcceptReject.acceptAndRejectAreEmpty(); for (final String libOrExtJarPath : SystemJarFinder.getJreLibOrExtJars()) { - if (scanAllLibOrExtJars || scanSpec.libOrExtJarWhiteBlackList - .isSpecificallyWhitelistedAndNotBlacklisted(libOrExtJarPath)) { + if (scanAllLibOrExtJars + || scanSpec.libOrExtJarAcceptReject.isSpecificallyAcceptedAndNotRejected(libOrExtJarPath)) { classpathOrder.addSystemClasspathEntry(libOrExtJarPath, defaultClassLoader); if (systemJarsLog != null) { systemJarsLog.log("Found lib or ext jar: " + libOrExtJarPath); diff --git a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSlice.java b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSlice.java index 282b3dce7..009ca5719 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSlice.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/ZipFileSlice.java @@ -34,7 +34,7 @@ import java.util.Objects; import nonapi.io.github.classgraph.fileslice.Slice; -import nonapi.io.github.classgraph.scanspec.WhiteBlackList.WhiteBlackListLeafname; +import nonapi.io.github.classgraph.scanspec.AcceptReject.AcceptRejectLeafname; /** A zipfile slice (a sub-range of bytes within a PhysicalZipFile. */ public class ZipFileSlice { @@ -107,18 +107,17 @@ public class ZipFileSlice { } /** - * Check whether this zipfile slice and all of its parent slices are whitelisted and not blacklisted in the - * jarfile white/blacklist. + * Check whether this zipfile slice and all of its parent slices are accepted and not rejected in the jarfile + * accept/reject criteria. * - * @param jarWhiteBlackList - * the jar white black list - * @return true if this zipfile slice and all of its parent slices are whitelisted and not blacklisted in the - * jarfile white/blacklist. + * @param jarAcceptReject + * the jar accept/reject criteria + * @return true if this zipfile slice and all of its parent slices are accepted and not rejected in the jarfile + * accept/reject criteria. */ - public boolean isWhitelistedAndNotBlacklisted(final WhiteBlackListLeafname jarWhiteBlackList) { - return jarWhiteBlackList.isWhitelistedAndNotBlacklisted(pathWithinParentZipFileSlice) // - && (parentZipFileSlice == null - || parentZipFileSlice.isWhitelistedAndNotBlacklisted(jarWhiteBlackList)); + public boolean isAcceptedAndNotRejected(final AcceptRejectLeafname jarAcceptReject) { + return jarAcceptReject.isAcceptedAndNotRejected(pathWithinParentZipFileSlice) // + && (parentZipFileSlice == null || parentZipFileSlice.isAcceptedAndNotRejected(jarAcceptReject)); } /** diff --git a/src/main/java/nonapi/io/github/classgraph/scanspec/WhiteBlackList.java b/src/main/java/nonapi/io/github/classgraph/scanspec/AcceptReject.java similarity index 52% rename from src/main/java/nonapi/io/github/classgraph/scanspec/WhiteBlackList.java rename to src/main/java/nonapi/io/github/classgraph/scanspec/AcceptReject.java index ad2be685f..21fa8a945 100644 --- a/src/main/java/nonapi/io/github/classgraph/scanspec/WhiteBlackList.java +++ b/src/main/java/nonapi/io/github/classgraph/scanspec/AcceptReject.java @@ -40,31 +40,31 @@ import nonapi.io.github.classgraph.utils.FileUtils; import nonapi.io.github.classgraph.utils.JarUtils; -/** A class storing whitelist or blacklist criteria. */ -public abstract class WhiteBlackList { - /** Whitelisted items (whole-string match). */ - protected Set whitelist; - /** Blacklisted items (whole-string match). */ - protected Set blacklist; - /** Whitelisted items (prefix match), as a set. */ - protected Set whitelistPrefixesSet; - /** Whitelisted items (prefix match), as a sorted list. */ - protected List whitelistPrefixes; - /** Blacklisted items (prefix match). */ - protected List blacklistPrefixes; - /** Whitelist glob strings. (Serialized to JSON, for logging purposes.) */ - protected Set whitelistGlobs; - /** Blacklist glob strings. (Serialized to JSON, for logging purposes.) */ - protected Set blacklistGlobs; - /** Whitelist regexp patterns. (Not serialized to JSON.) */ - protected transient List whitelistPatterns; - /** Blacklist regexp patterns. (Not serialized to JSON.) */ - protected transient List blacklistPatterns; +/** A class storing accept or reject criteria. */ +public abstract class AcceptReject { + /** Accepted items (whole-string match). */ + protected Set accept; + /** Rejected items (whole-string match). */ + protected Set reject; + /** Accepted items (prefix match), as a set. */ + protected Set acceptPrefixesSet; + /** Accepted items (prefix match), as a sorted list. */ + protected List acceptPrefixes; + /** Rejected items (prefix match). */ + protected List rejectPrefixes; + /** Accept glob strings. (Serialized to JSON, for logging purposes.) */ + protected Set acceptGlobs; + /** Reject glob strings. (Serialized to JSON, for logging purposes.) */ + protected Set rejectGlobs; + /** Accept regexp patterns. (Not serialized to JSON.) */ + protected transient List acceptPatterns; + /** Reject regexp patterns. (Not serialized to JSON.) */ + protected transient List rejectPatterns; /** The separator character. */ protected char separatorChar; /** Deserialization constructor. */ - public WhiteBlackList() { + public AcceptReject() { } /** @@ -73,84 +73,84 @@ public WhiteBlackList() { * @param separatorChar * the separator char */ - public WhiteBlackList(final char separatorChar) { + public AcceptReject(final char separatorChar) { this.separatorChar = separatorChar; } - /** Whitelist/blacklist for prefix strings. */ - public static class WhiteBlackListPrefix extends WhiteBlackList { + /** Accept/reject for prefix strings. */ + public static class AcceptRejectPrefix extends AcceptReject { /** Deserialization constructor. */ - public WhiteBlackListPrefix() { + public AcceptRejectPrefix() { super(); } /** - * Instantiate a new whitelist/blacklist for prefix strings. + * Instantiate a new accept/reject for prefix strings. * * @param separatorChar * the separator char */ - public WhiteBlackListPrefix(final char separatorChar) { + public AcceptRejectPrefix(final char separatorChar) { super(separatorChar); } /** - * Add to the whitelist. + * Add to the accept. * * @param str - * the string to whitelist + * the string to accept */ @Override - public void addToWhitelist(final String str) { + public void addToAccept(final String str) { if (str.contains("*")) { throw new IllegalArgumentException("Cannot use a glob wildcard here: " + str); } - if (this.whitelistPrefixesSet == null) { - this.whitelistPrefixesSet = new HashSet<>(); + if (this.acceptPrefixesSet == null) { + this.acceptPrefixesSet = new HashSet<>(); } - this.whitelistPrefixesSet.add(str); + this.acceptPrefixesSet.add(str); } /** - * Add to the blacklist. + * Add to the reject. * * @param str - * the string to blacklist + * the string to reject */ @Override - public void addToBlacklist(final String str) { + public void addToReject(final String str) { if (str.contains("*")) { throw new IllegalArgumentException("Cannot use a glob wildcard here: " + str); } - if (this.blacklistPrefixes == null) { - this.blacklistPrefixes = new ArrayList<>(); + if (this.rejectPrefixes == null) { + this.rejectPrefixes = new ArrayList<>(); } - this.blacklistPrefixes.add(str); + this.rejectPrefixes.add(str); } /** - * Check if the requested string has a whitelisted/non-blacklisted prefix. + * Check if the requested string has an accepted/non-rejected prefix. * * @param str * the string to test - * @return true if string is whitelisted and not blacklisted + * @return true if string is accepted and not rejected */ @Override - public boolean isWhitelistedAndNotBlacklisted(final String str) { - boolean isWhitelisted = whitelistPrefixes == null; - if (!isWhitelisted) { - for (final String prefix : whitelistPrefixes) { + public boolean isAcceptedAndNotRejected(final String str) { + boolean isAccepted = acceptPrefixes == null; + if (!isAccepted) { + for (final String prefix : acceptPrefixes) { if (str.startsWith(prefix)) { - isWhitelisted = true; + isAccepted = true; break; } } } - if (!isWhitelisted) { + if (!isAccepted) { return false; } - if (blacklistPrefixes != null) { - for (final String prefix : blacklistPrefixes) { + if (rejectPrefixes != null) { + for (final String prefix : rejectPrefixes) { if (str.startsWith(prefix)) { return false; } @@ -160,24 +160,24 @@ public boolean isWhitelistedAndNotBlacklisted(final String str) { } /** - * Check if the requested string has a whitelisted prefix. + * Check if the requested string has an accepted prefix. * * @param str * the string to test - * @return true if string is whitelisted + * @return true if string is accepted */ @Override - public boolean isWhitelisted(final String str) { - boolean isWhitelisted = whitelistPrefixes == null; - if (!isWhitelisted) { - for (final String prefix : whitelistPrefixes) { + public boolean isAccepted(final String str) { + boolean isAccepted = acceptPrefixes == null; + if (!isAccepted) { + for (final String prefix : acceptPrefixes) { if (str.startsWith(prefix)) { - isWhitelisted = true; + isAccepted = true; break; } } } - return isWhitelisted; + return isAccepted; } /** @@ -190,21 +190,21 @@ public boolean isWhitelisted(final String str) { * always */ @Override - public boolean whitelistHasPrefix(final String str) { + public boolean acceptHasPrefix(final String str) { throw new IllegalArgumentException("Can only find prefixes of whole strings"); } /** - * Check if the requested string has a blacklisted prefix. + * Check if the requested string has a rejected prefix. * * @param str * the string to test - * @return true if the string has a blacklisted prefix + * @return true if the string has a rejected prefix */ @Override - public boolean isBlacklisted(final String str) { - if (blacklistPrefixes != null) { - for (final String prefix : blacklistPrefixes) { + public boolean isRejected(final String str) { + if (rejectPrefixes != null) { + for (final String prefix : rejectPrefixes) { if (str.startsWith(prefix)) { return true; } @@ -214,53 +214,53 @@ public boolean isBlacklisted(final String str) { } } - /** Whitelist/blacklist for whole-strings matches. */ - public static class WhiteBlackListWholeString extends WhiteBlackList { + /** Accept/reject for whole-strings matches. */ + public static class AcceptRejectWholeString extends AcceptReject { /** Deserialization constructor. */ - public WhiteBlackListWholeString() { + public AcceptRejectWholeString() { super(); } /** - * Instantiate a new whitelist/blacklist for whole-string matches. + * Instantiate a new accept/reject for whole-string matches. * * @param separatorChar * the separator char */ - public WhiteBlackListWholeString(final char separatorChar) { + public AcceptRejectWholeString(final char separatorChar) { super(separatorChar); } /** - * Add to the whitelist. + * Add to the accept. * * @param str - * the string to whitelist + * the string to accept */ @Override - public void addToWhitelist(final String str) { + public void addToAccept(final String str) { if (str.contains("*")) { - if (this.whitelistGlobs == null) { - this.whitelistGlobs = new HashSet<>(); - this.whitelistPatterns = new ArrayList<>(); + if (this.acceptGlobs == null) { + this.acceptGlobs = new HashSet<>(); + this.acceptPatterns = new ArrayList<>(); } - this.whitelistGlobs.add(str); - this.whitelistPatterns.add(globToPattern(str)); + this.acceptGlobs.add(str); + this.acceptPatterns.add(globToPattern(str)); } else { - if (this.whitelist == null) { - this.whitelist = new HashSet<>(); + if (this.accept == null) { + this.accept = new HashSet<>(); } - this.whitelist.add(str); + this.accept.add(str); } - // For WhiteBlackListWholeString, which doesn't perform prefix matches like WhiteBlackListPrefix, - // use whitelistPrefixes to store all parent prefixes of a whitelisted path, so that - // whitelistHasPrefix() can operate efficiently on very large whitelists (#338), - // in particular where the size of the whitelist is much larger than the maximum path depth. - if (this.whitelistPrefixesSet == null) { - this.whitelistPrefixesSet = new HashSet<>(); - whitelistPrefixesSet.add(""); - whitelistPrefixesSet.add("/"); + // For AcceptRejectWholeString, which doesn't perform prefix matches like AcceptRejectPrefix, + // use acceptPrefixes to store all parent prefixes of an accepted path, so that + // acceptHasPrefix() can operate efficiently on very large accepts (#338), + // in particular where the size of the accept is much larger than the maximum path depth. + if (this.acceptPrefixesSet == null) { + this.acceptPrefixesSet = new HashSet<>(); + acceptPrefixesSet.add(""); + acceptPrefixesSet.add("/"); } final String separator = Character.toString(separatorChar); String prefix = str; @@ -283,147 +283,147 @@ public void addToWhitelist(final String str) { } // Add str itself as a prefix (this will only match a parent dir for for (; !prefix.isEmpty(); prefix = FileUtils.getParentDirPath(prefix, separatorChar)) { - whitelistPrefixesSet.add(prefix + separatorChar); + acceptPrefixesSet.add(prefix + separatorChar); } } /** - * Add to the blacklist. + * Add to the reject. * * @param str - * the string to blacklist + * the string to reject */ @Override - public void addToBlacklist(final String str) { + public void addToReject(final String str) { if (str.contains("*")) { - if (this.blacklistGlobs == null) { - this.blacklistGlobs = new HashSet<>(); - this.blacklistPatterns = new ArrayList<>(); + if (this.rejectGlobs == null) { + this.rejectGlobs = new HashSet<>(); + this.rejectPatterns = new ArrayList<>(); } - this.blacklistGlobs.add(str); - this.blacklistPatterns.add(globToPattern(str)); + this.rejectGlobs.add(str); + this.rejectPatterns.add(globToPattern(str)); } else { - if (this.blacklist == null) { - this.blacklist = new HashSet<>(); + if (this.reject == null) { + this.reject = new HashSet<>(); } - this.blacklist.add(str); + this.reject.add(str); } } /** - * Check if the requested string is whitelisted and not blacklisted. + * Check if the requested string is accepted and not rejected. * * @param str * the string to test - * @return true if the string is whitelisted and not blacklisted + * @return true if the string is accepted and not rejected */ @Override - public boolean isWhitelistedAndNotBlacklisted(final String str) { - return isWhitelisted(str) && !isBlacklisted(str); + public boolean isAcceptedAndNotRejected(final String str) { + return isAccepted(str) && !isRejected(str); } /** - * Check if the requested string is whitelisted. + * Check if the requested string is accepted. * * @param str * the string to test - * @return true if the string is whitelisted + * @return true if the string is accepted */ @Override - public boolean isWhitelisted(final String str) { - return (whitelist == null && whitelistPatterns == null) - || (whitelist != null && whitelist.contains(str)) || matchesPatternList(str, whitelistPatterns); + public boolean isAccepted(final String str) { + return (accept == null && acceptPatterns == null) || (accept != null && accept.contains(str)) + || matchesPatternList(str, acceptPatterns); } /** - * Check if the requested string is a prefix of a whitelisted string. + * Check if the requested string is a prefix of an accepted string. * * @param str * the string to test - * @return true if the string is a prefix of a whitelisted string + * @return true if the string is a prefix of an accepted string */ @Override - public boolean whitelistHasPrefix(final String str) { - if (whitelistPrefixesSet == null) { + public boolean acceptHasPrefix(final String str) { + if (acceptPrefixesSet == null) { return false; } - return whitelistPrefixesSet.contains(str); + return acceptPrefixesSet.contains(str); } /** - * Check if the requested string is blacklisted. + * Check if the requested string is rejected. * * @param str * the string to test - * @return true if the string is blacklisted + * @return true if the string is rejected */ @Override - public boolean isBlacklisted(final String str) { - return (blacklist != null && blacklist.contains(str)) || matchesPatternList(str, blacklistPatterns); + public boolean isRejected(final String str) { + return (reject != null && reject.contains(str)) || matchesPatternList(str, rejectPatterns); } } - /** Whitelist/blacklist for leaf matches. */ - public static class WhiteBlackListLeafname extends WhiteBlackListWholeString { + /** Accept/reject for leaf matches. */ + public static class AcceptRejectLeafname extends AcceptRejectWholeString { /** Deserialization constructor. */ - public WhiteBlackListLeafname() { + public AcceptRejectLeafname() { super(); } /** - * Instantiates a new whitelist/blacklist for leaf matches. + * Instantiates a new accept/reject for leaf matches. * * @param separatorChar * the separator char */ - public WhiteBlackListLeafname(final char separatorChar) { + public AcceptRejectLeafname(final char separatorChar) { super(separatorChar); } /** - * Add to the whitelist. + * Add to the accept. * * @param str - * the string to whitelist + * the string to accept */ @Override - public void addToWhitelist(final String str) { - super.addToWhitelist(JarUtils.leafName(str)); + public void addToAccept(final String str) { + super.addToAccept(JarUtils.leafName(str)); } /** - * Add to the blacklist. + * Add to the reject. * * @param str - * the string to blacklist + * the string to reject */ @Override - public void addToBlacklist(final String str) { - super.addToBlacklist(JarUtils.leafName(str)); + public void addToReject(final String str) { + super.addToReject(JarUtils.leafName(str)); } /** - * Check if the requested string is whitelisted and not blacklisted. + * Check if the requested string is accepted and not rejected. * * @param str * the string to test - * @return true if the string is whitelisted and not blacklisted + * @return true if the string is accepted and not rejected */ @Override - public boolean isWhitelistedAndNotBlacklisted(final String str) { - return super.isWhitelistedAndNotBlacklisted(JarUtils.leafName(str)); + public boolean isAcceptedAndNotRejected(final String str) { + return super.isAcceptedAndNotRejected(JarUtils.leafName(str)); } /** - * Check if the requested string is whitelisted. + * Check if the requested string is accepted. * * @param str * the string to test - * @return true if the string is whitelisted + * @return true if the string is accepted */ @Override - public boolean isWhitelisted(final String str) { - return super.isWhitelisted(JarUtils.leafName(str)); + public boolean isAccepted(final String str) { + return super.isAccepted(JarUtils.leafName(str)); } /** @@ -436,74 +436,74 @@ public boolean isWhitelisted(final String str) { * always */ @Override - public boolean whitelistHasPrefix(final String str) { + public boolean acceptHasPrefix(final String str) { throw new IllegalArgumentException("Can only find prefixes of whole strings"); } /** - * Check if the requested string is blacklisted. + * Check if the requested string is rejected. * * @param str * the string to test - * @return true if the string is blacklisted + * @return true if the string is rejected */ @Override - public boolean isBlacklisted(final String str) { - return super.isBlacklisted(JarUtils.leafName(str)); + public boolean isRejected(final String str) { + return super.isRejected(JarUtils.leafName(str)); } } /** - * Add to the whitelist. + * Add to the accept. * * @param str - * The string to whitelist. + * The string to accept. */ - public abstract void addToWhitelist(final String str); + public abstract void addToAccept(final String str); /** - * Add to the blacklist. + * Add to the reject. * * @param str - * The string to blacklist. + * The string to reject. */ - public abstract void addToBlacklist(final String str); + public abstract void addToReject(final String str); /** - * Check if a string is whitelisted and not blacklisted. + * Check if a string is accepted and not rejected. * * @param str * The string to test. - * @return true if the string is whitelisted and not blacklisted. + * @return true if the string is accepted and not rejected. */ - public abstract boolean isWhitelistedAndNotBlacklisted(final String str); + public abstract boolean isAcceptedAndNotRejected(final String str); /** - * Check if a string is whitelisted. + * Check if a string is accepted. * * @param str * The string to test. - * @return true if the string is whitelisted. + * @return true if the string is accepted. */ - public abstract boolean isWhitelisted(final String str); + public abstract boolean isAccepted(final String str); /** - * Check if a string is a prefix of a whitelisted string. + * Check if a string is a prefix of an accepted string. * * @param str * The string to test. - * @return true if the string is a prefix of a whitelisted string. + * @return true if the string is a prefix of an accepted string. */ - public abstract boolean whitelistHasPrefix(final String str); + public abstract boolean acceptHasPrefix(final String str); /** - * Check if a string is blacklisted. + * Check if a string is rejected. * * @param str * The string to test. - * @return true if the string is blacklisted. + * @return true if the string is rejected. */ - public abstract boolean isBlacklisted(final String str); + public abstract boolean isRejected(final String str); /** * Remove initial and final '/' characters, if any. @@ -597,66 +597,66 @@ private static boolean matchesPatternList(final String str, final List } /** - * Check if the whitelist is empty. + * Check if the accept is empty. * - * @return true if there were no whitelist criteria added. + * @return true if there were no accept criteria added. */ - public boolean whitelistIsEmpty() { - return whitelist == null && whitelistPrefixes == null && whitelistGlobs == null; + public boolean acceptIsEmpty() { + return accept == null && acceptPrefixes == null && acceptGlobs == null; } /** - * Check if the blacklist is empty. + * Check if the reject is empty. * - * @return true if there were no blacklist criteria added. + * @return true if there were no reject criteria added. */ - public boolean blacklistIsEmpty() { - return blacklist == null && blacklistPrefixes == null && blacklistGlobs == null; + public boolean rejectIsEmpty() { + return reject == null && rejectPrefixes == null && rejectGlobs == null; } /** - * Check if the whitelist and blacklist are empty. + * Check if the accept and reject are empty. * - * @return true if there were no whitelist or blacklist criteria added. + * @return true if there were no accept or reject criteria added. */ - public boolean whitelistAndBlacklistAreEmpty() { - return whitelistIsEmpty() && blacklistIsEmpty(); + public boolean acceptAndRejectAreEmpty() { + return acceptIsEmpty() && rejectIsEmpty(); } /** - * Check if a string is specifically whitelisted and not blacklisted. + * Check if a string is specifically accepted and not rejected. * * @param str * The string to test. - * @return true if the requested string is specifically whitelisted and not blacklisted, i.e. will not - * return true if the whitelist is empty, or if the string is blacklisted. + * @return true if the requested string is specifically accepted and not rejected, i.e. will not return + * true if the accept is empty, or if the string is rejected. */ - public boolean isSpecificallyWhitelistedAndNotBlacklisted(final String str) { - return !whitelistIsEmpty() && isWhitelistedAndNotBlacklisted(str); + public boolean isSpecificallyAcceptedAndNotRejected(final String str) { + return !acceptIsEmpty() && isAcceptedAndNotRejected(str); } /** - * Check if a string is specifically whitelisted. + * Check if a string is specifically accepted. * * @param str * The string to test. - * @return true if the requested string is specifically whitelisted, i.e. will not return true if the - * whitelist is empty. + * @return true if the requested string is specifically accepted, i.e. will not return true if the accept + * is empty. */ - public boolean isSpecificallyWhitelisted(final String str) { - return !whitelistIsEmpty() && isWhitelisted(str); + public boolean isSpecificallyAccepted(final String str) { + return !acceptIsEmpty() && isAccepted(str); } - /** Need to sort prefixes to ensure correct whitelist/blacklist evaluation (see Issue #167). */ + /** Need to sort prefixes to ensure correct accept/reject evaluation (see Issue #167). */ void sortPrefixes() { - if (whitelistPrefixesSet != null) { - whitelistPrefixes = new ArrayList<>(whitelistPrefixesSet); + if (acceptPrefixesSet != null) { + acceptPrefixes = new ArrayList<>(acceptPrefixesSet); } - if (whitelistPrefixes != null) { - CollectionUtils.sortIfNotEmpty(whitelistPrefixes); + if (acceptPrefixes != null) { + CollectionUtils.sortIfNotEmpty(acceptPrefixes); } - if (blacklistPrefixes != null) { - CollectionUtils.sortIfNotEmpty(blacklistPrefixes); + if (rejectPrefixes != null) { + CollectionUtils.sortIfNotEmpty(rejectPrefixes); } } @@ -697,44 +697,44 @@ private static void quoteList(final Collection coll, final StringBuilder @Override public String toString() { final StringBuilder buf = new StringBuilder(); - if (whitelist != null) { - buf.append("whitelist: "); - quoteList(whitelist, buf); + if (accept != null) { + buf.append("accept: "); + quoteList(accept, buf); } - if (whitelistPrefixes != null) { + if (acceptPrefixes != null) { if (buf.length() > 0) { buf.append("; "); } - buf.append("whitelistPrefixes: "); - quoteList(whitelistPrefixes, buf); + buf.append("acceptPrefixes: "); + quoteList(acceptPrefixes, buf); } - if (whitelistGlobs != null) { + if (acceptGlobs != null) { if (buf.length() > 0) { buf.append("; "); } - buf.append("whitelistGlobs: "); - quoteList(whitelistGlobs, buf); + buf.append("acceptGlobs: "); + quoteList(acceptGlobs, buf); } - if (blacklist != null) { + if (reject != null) { if (buf.length() > 0) { buf.append("; "); } - buf.append("blacklist: "); - quoteList(blacklist, buf); + buf.append("reject: "); + quoteList(reject, buf); } - if (blacklistPrefixes != null) { + if (rejectPrefixes != null) { if (buf.length() > 0) { buf.append("; "); } - buf.append("blacklistPrefixes: "); - quoteList(blacklistPrefixes, buf); + buf.append("rejectPrefixes: "); + quoteList(rejectPrefixes, buf); } - if (blacklistGlobs != null) { + if (rejectGlobs != null) { if (buf.length() > 0) { buf.append("; "); } - buf.append("blacklistGlobs: "); - quoteList(blacklistGlobs, buf); + buf.append("rejectGlobs: "); + quoteList(rejectGlobs, buf); } return buf.toString(); } 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 d91be8172..4a5382936 100644 --- a/src/main/java/nonapi/io/github/classgraph/scanspec/ScanSpec.java +++ b/src/main/java/nonapi/io/github/classgraph/scanspec/ScanSpec.java @@ -46,51 +46,51 @@ import io.github.classgraph.ClassInfo; import io.github.classgraph.ModulePathInfo; import io.github.classgraph.ScanResult; -import nonapi.io.github.classgraph.scanspec.WhiteBlackList.WhiteBlackListLeafname; -import nonapi.io.github.classgraph.scanspec.WhiteBlackList.WhiteBlackListPrefix; -import nonapi.io.github.classgraph.scanspec.WhiteBlackList.WhiteBlackListWholeString; +import nonapi.io.github.classgraph.scanspec.AcceptReject.AcceptRejectLeafname; +import nonapi.io.github.classgraph.scanspec.AcceptReject.AcceptRejectPrefix; +import nonapi.io.github.classgraph.scanspec.AcceptReject.AcceptRejectWholeString; import nonapi.io.github.classgraph.utils.LogNode; /** * The scanning specification. */ public class ScanSpec { - /** Package white/blacklist (with separator '.'). */ - public WhiteBlackListWholeString packageWhiteBlackList = new WhiteBlackListWholeString('.'); + /** Package accept/reject criteria (with separator '.'). */ + public AcceptRejectWholeString packageAcceptReject = new AcceptRejectWholeString('.'); - /** Package prefix white/blacklist, for recursive scanning (with separator '.', ending in '.'). */ - public WhiteBlackListPrefix packagePrefixWhiteBlackList = new WhiteBlackListPrefix('.'); + /** Package prefix accept/reject criteria, for recursive scanning (with separator '.', ending in '.'). */ + public AcceptRejectPrefix packagePrefixAcceptReject = new AcceptRejectPrefix('.'); - /** Path white/blacklist (with separator '/'). */ - public WhiteBlackListWholeString pathWhiteBlackList = new WhiteBlackListWholeString('/'); + /** Path accept/reject criteria (with separator '/'). */ + public AcceptRejectWholeString pathAcceptReject = new AcceptRejectWholeString('/'); - /** Path prefix white/blacklist, for recursive scanning (with separator '/', ending in '/'). */ - public WhiteBlackListPrefix pathPrefixWhiteBlackList = new WhiteBlackListPrefix('/'); + /** Path prefix accept/reject criteria, for recursive scanning (with separator '/', ending in '/'). */ + public AcceptRejectPrefix pathPrefixAcceptReject = new AcceptRejectPrefix('/'); - /** Class white/blacklist (fully-qualified class names, with separator '.'). */ - public WhiteBlackListWholeString classWhiteBlackList = new WhiteBlackListWholeString('.'); + /** Class accept/reject criteria (fully-qualified class names, with separator '.'). */ + public AcceptRejectWholeString classAcceptReject = new AcceptRejectWholeString('.'); - /** Classfile white/blacklist (path to classfiles, with separator '/', ending in ".class"). */ - public WhiteBlackListWholeString classfilePathWhiteBlackList = new WhiteBlackListWholeString('/'); + /** Classfile accept/reject criteria (path to classfiles, with separator '/', ending in ".class"). */ + public AcceptRejectWholeString classfilePathAcceptReject = new AcceptRejectWholeString('/'); - /** Package containing white/blacklisted classes (with separator '.'). */ - public WhiteBlackListWholeString classPackageWhiteBlackList = new WhiteBlackListWholeString('.'); + /** Package containing accept/reject criteriaed classes (with separator '.'). */ + public AcceptRejectWholeString classPackageAcceptReject = new AcceptRejectWholeString('.'); - /** Path to white/blacklisted classes (with separator '/'). */ - public WhiteBlackListWholeString classPackagePathWhiteBlackList = new WhiteBlackListWholeString('/'); + /** Path to accept/reject criteriaed classes (with separator '/'). */ + public AcceptRejectWholeString classPackagePathAcceptReject = new AcceptRejectWholeString('/'); - /** Module white/blacklist (with separator '.'). */ - public WhiteBlackListWholeString moduleWhiteBlackList = new WhiteBlackListWholeString('.'); + /** Module accept/reject criteria (with separator '.'). */ + public AcceptRejectWholeString moduleAcceptReject = new AcceptRejectWholeString('.'); - /** Jar white/blacklist (leafname only, ending in ".jar"). */ - public WhiteBlackListLeafname jarWhiteBlackList = new WhiteBlackListLeafname('/'); + /** Jar accept/reject criteria (leafname only, ending in ".jar"). */ + public AcceptRejectLeafname jarAcceptReject = new AcceptRejectLeafname('/'); - /** Classpath element resource path white/blacklist. */ - public WhiteBlackListWholeString classpathElementResourcePathWhiteBlackList = // - new WhiteBlackListWholeString('/'); + /** Classpath element resource path accept/reject criteria. */ + public AcceptRejectWholeString classpathElementResourcePathAcceptReject = // + new AcceptRejectWholeString('/'); - /** lib/ext jar white/blacklist (leafname only, ending in ".jar"). */ - public WhiteBlackListLeafname libOrExtJarWhiteBlackList = new WhiteBlackListLeafname('/'); + /** lib/ext jar accept/reject criteria (leafname only, ending in ".jar"). */ + public AcceptRejectLeafname libOrExtJarAcceptReject = new AcceptRejectLeafname('/'); // ------------------------------------------------------------------------------------------------------------- @@ -135,9 +135,9 @@ public class ScanSpec { public boolean enableInterClassDependencies; /** - * If true, allow external classes (classes outside of whitelisted packages) to be returned in the ScanResult, - * if they are directly referred to by a whitelisted class, as a superclass, implemented interface or - * annotation. Disabled by default. + * If true, allow external classes (classes outside of accepted packages) to be returned in the ScanResult, if + * they are directly referred to by an accepted class, as a superclass, implemented interface or annotation. + * Disabled by default. */ public boolean enableExternalClasses; @@ -267,12 +267,12 @@ public ScanSpec() { // ------------------------------------------------------------------------------------------------------------- - /** Sort prefixes to ensure correct whitelist/blacklist evaluation (see Issue #167). */ + /** Sort prefixes to ensure correct accept/reject evaluation (see Issue #167). */ public void sortPrefixes() { for (final Field field : ScanSpec.class.getDeclaredFields()) { - if (WhiteBlackList.class.isAssignableFrom(field.getType())) { + if (AcceptReject.class.isAssignableFrom(field.getType())) { try { - ((WhiteBlackList) field.get(this)).sortPrefixes(); + ((AcceptReject) field.get(this)).sortPrefixes(); } catch (final ReflectiveOperationException e) { throw ClassGraphException.newClassGraphException("Field is not accessible: " + field, e); } @@ -440,104 +440,104 @@ public void overrideModuleLayers(final Object... overrideModuleLayers) { // ------------------------------------------------------------------------------------------------------------- /** - * Whether a path is a descendant of a blacklisted path, or an ancestor or descendant of a whitelisted path. + * Whether a path is a descendant of a rejected path, or an ancestor or descendant of an accepted path. */ public enum ScanSpecPathMatch { - /** Path starts with (or is) a blacklisted path prefix. */ - HAS_BLACKLISTED_PATH_PREFIX, - /** Path starts with a whitelisted path prefix. */ - HAS_WHITELISTED_PATH_PREFIX, - /** Path is whitelisted. */ - AT_WHITELISTED_PATH, - /** Path is an ancestor of a whitelisted path. */ - ANCESTOR_OF_WHITELISTED_PATH, - /** Path is the package of a specifically-whitelisted class. */ - AT_WHITELISTED_CLASS_PACKAGE, - /** Path is not whitelisted and not blacklisted. */ - NOT_WITHIN_WHITELISTED_PATH + /** Path starts with (or is) a rejected path prefix. */ + HAS_REJECTED_PATH_PREFIX, + /** Path starts with an accepted path prefix. */ + HAS_ACCEPTED_PATH_PREFIX, + /** Path is accepted. */ + AT_ACCEPTED_PATH, + /** Path is an ancestor of an accepted path. */ + ANCESTOR_OF_ACCEPTED_PATH, + /** Path is the package of a specifically-accepted class. */ + AT_ACCEPTED_CLASS_PACKAGE, + /** Path is not accepted and not rejected. */ + NOT_WITHIN_ACCEPTED_PATH } /** - * Returns true if the given directory path is a descendant of a blacklisted path, or an ancestor or descendant - * of a whitelisted path. The path should end in "/". + * Returns true if the given directory path is a descendant of a rejected path, or an ancestor or descendant of + * an accepted path. The path should end in "/". * * @param relativePath * the relative path * @return the {@link ScanSpecPathMatch} */ - public ScanSpecPathMatch dirWhitelistMatchStatus(final String relativePath) { - // In blacklisted path - if (pathWhiteBlackList.isBlacklisted(relativePath)) { - // The directory is blacklisted. - return ScanSpecPathMatch.HAS_BLACKLISTED_PATH_PREFIX; + public ScanSpecPathMatch dirAcceptMatchStatus(final String relativePath) { + // In rejected path + if (pathAcceptReject.isRejected(relativePath)) { + // The directory is rejected. + return ScanSpecPathMatch.HAS_REJECTED_PATH_PREFIX; } - if (pathPrefixWhiteBlackList.isBlacklisted(relativePath)) { - // An prefix of this path is blacklisted. - return ScanSpecPathMatch.HAS_BLACKLISTED_PATH_PREFIX; + if (pathPrefixAcceptReject.isRejected(relativePath)) { + // An prefix of this path is rejected. + return ScanSpecPathMatch.HAS_REJECTED_PATH_PREFIX; } - if (pathWhiteBlackList.whitelistIsEmpty() && classPackagePathWhiteBlackList.whitelistIsEmpty()) { - // If there are no whitelisted packages, the root package is whitelisted - return relativePath.isEmpty() || relativePath.equals("/") ? ScanSpecPathMatch.AT_WHITELISTED_PATH - : ScanSpecPathMatch.HAS_WHITELISTED_PATH_PREFIX; + if (pathAcceptReject.acceptIsEmpty() && classPackagePathAcceptReject.acceptIsEmpty()) { + // If there are no accepted packages, the root package is accepted + return relativePath.isEmpty() || relativePath.equals("/") ? ScanSpecPathMatch.AT_ACCEPTED_PATH + : ScanSpecPathMatch.HAS_ACCEPTED_PATH_PREFIX; } - // At whitelisted path - if (pathWhiteBlackList.isSpecificallyWhitelistedAndNotBlacklisted(relativePath)) { - // Reached a whitelisted path - return ScanSpecPathMatch.AT_WHITELISTED_PATH; + // At accepted path + if (pathAcceptReject.isSpecificallyAcceptedAndNotRejected(relativePath)) { + // Reached an accepted path + return ScanSpecPathMatch.AT_ACCEPTED_PATH; } - if (classPackagePathWhiteBlackList.isSpecificallyWhitelistedAndNotBlacklisted(relativePath)) { - // Reached a package containing a specifically-whitelisted class - return ScanSpecPathMatch.AT_WHITELISTED_CLASS_PACKAGE; + if (classPackagePathAcceptReject.isSpecificallyAcceptedAndNotRejected(relativePath)) { + // Reached a package containing a specifically-accepted class + return ScanSpecPathMatch.AT_ACCEPTED_CLASS_PACKAGE; } - // Descendant of whitelisted path - if (pathPrefixWhiteBlackList.isSpecificallyWhitelisted(relativePath)) { - // Path prefix matches one in the whitelist - return ScanSpecPathMatch.HAS_WHITELISTED_PATH_PREFIX; + // Descendant of accepted path + if (pathPrefixAcceptReject.isSpecificallyAccepted(relativePath)) { + // Path prefix matches one in the accept + return ScanSpecPathMatch.HAS_ACCEPTED_PATH_PREFIX; } - // Ancestor of whitelisted path + // Ancestor of accepted path if (relativePath.equals("/")) { - // The default package is always the ancestor of whitelisted paths (need to keep recursing) - return ScanSpecPathMatch.ANCESTOR_OF_WHITELISTED_PATH; + // The default package is always the ancestor of accepted paths (need to keep recursing) + return ScanSpecPathMatch.ANCESTOR_OF_ACCEPTED_PATH; } - if (pathWhiteBlackList.whitelistHasPrefix(relativePath)) { - // relativePath is an ancestor (prefix) of a whitelisted path - return ScanSpecPathMatch.ANCESTOR_OF_WHITELISTED_PATH; + if (pathAcceptReject.acceptHasPrefix(relativePath)) { + // relativePath is an ancestor (prefix) of an accepted path + return ScanSpecPathMatch.ANCESTOR_OF_ACCEPTED_PATH; } - if (classfilePathWhiteBlackList.whitelistHasPrefix(relativePath)) { - // relativePath is an ancestor (prefix) of a whitelisted class' parent directory - return ScanSpecPathMatch.ANCESTOR_OF_WHITELISTED_PATH; + if (classfilePathAcceptReject.acceptHasPrefix(relativePath)) { + // relativePath is an ancestor (prefix) of an accepted class' parent directory + return ScanSpecPathMatch.ANCESTOR_OF_ACCEPTED_PATH; } - // Not in whitelisted path - return ScanSpecPathMatch.NOT_WITHIN_WHITELISTED_PATH; + // Not in accepted path + return ScanSpecPathMatch.NOT_WITHIN_ACCEPTED_PATH; } /** * Returns true if the given relative path (for a classfile name, including ".class") matches a - * specifically-whitelisted (and non-blacklisted) classfile's relative path. + * specifically-accepted (and non-rejected) classfile's relative path. * * @param relativePath * the relative path * @return true if the given relative path (for a classfile name, including ".class") matches a - * specifically-whitelisted (and non-blacklisted) classfile's relative path. + * specifically-accepted (and non-rejected) classfile's relative path. */ - public boolean classfileIsSpecificallyWhitelisted(final String relativePath) { - return classfilePathWhiteBlackList.isSpecificallyWhitelistedAndNotBlacklisted(relativePath); + public boolean classfileIsSpecificallyAccepted(final String relativePath) { + return classfilePathAcceptReject.isSpecificallyAcceptedAndNotRejected(relativePath); } /** - * Returns true if the class is specifically blacklisted, or is within a blacklisted package. + * Returns true if the class is specifically rejected, or is within a rejected package. * * @param className * the class name - * @return true if the class is specifically blacklisted, or is within a blacklisted package. + * @return true if the class is specifically rejected, or is within a rejected package. */ - public boolean classOrPackageIsBlacklisted(final String className) { - return classWhiteBlackList.isBlacklisted(className) || packagePrefixWhiteBlackList.isBlacklisted(className); + public boolean classOrPackageIsRejected(final String className) { + return classAcceptReject.isRejected(className) || packagePrefixAcceptReject.isRejected(className); } // ------------------------------------------------------------------------------------------------------------- diff --git a/src/test/java/ClassGraphGraphVizGenerator.java b/src/test/java/ClassGraphGraphVizGenerator.java index b0585e4a1..93640edd7 100644 --- a/src/test/java/ClassGraphGraphVizGenerator.java +++ b/src/test/java/ClassGraphGraphVizGenerator.java @@ -18,7 +18,7 @@ public class ClassGraphGraphVizGenerator { */ public static void main(final String[] args) throws IOException { try (ScanResult scanResult = new ClassGraph() // - .whitelistPackagesNonRecursive("io.github.classgraph") // + .acceptPackagesNonRecursive("io.github.classgraph") // .enableMethodInfo() // .ignoreMethodVisibility() // .enableFieldInfo() // diff --git a/src/test/java/DefaultPackageTest.java b/src/test/java/DefaultPackageTest.java index e3985b017..7b40a1ff6 100644 --- a/src/test/java/DefaultPackageTest.java +++ b/src/test/java/DefaultPackageTest.java @@ -36,41 +36,41 @@ import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; -import io.github.classgraph.test.whitelisted.Cls; -import io.github.classgraph.test.whitelisted.blacklistedsub.BlacklistedSub; +import io.github.classgraph.test.accepted.Cls; +import io.github.classgraph.test.accepted.rejectedsub.RejectedSub; /** * DefaultPackageTest. */ public class DefaultPackageTest { - /** The Constant WHITELIST_PACKAGE. */ - private static final String WHITELIST_PACKAGE = Cls.class.getPackage().getName(); + /** The Constant ACCEPT_PACKAGE. */ + private static final String ACCEPT_PACKAGE = Cls.class.getPackage().getName(); /** * Scan. */ @Test public void scan() { - try (ScanResult scanResult = new ClassGraph().whitelistPackagesNonRecursive("").scan()) { + try (ScanResult scanResult = new ClassGraph().acceptPackagesNonRecursive("").scan()) { final List allClasses = scanResult.getAllClasses().getNames(); assertThat(allClasses).contains(DefaultPackageTest.class.getName()); assertThat(allClasses).contains(ClassInDefaultPackage.class.getName()); assertThat(allClasses).doesNotContain(Cls.class.getName()); assertThat(allClasses).doesNotContain(ClassGraph.class.getName()); assertThat(allClasses).doesNotContain(String.class.getName()); - assertThat(allClasses).doesNotContain(BlacklistedSub.class.getName()); + assertThat(allClasses).doesNotContain(RejectedSub.class.getName()); } } /** - * Scan with whitelist. + * Scan with accept. */ @Test - public void scanWithWhitelist() { - try (ScanResult scanResult = new ClassGraph().whitelistPackages(WHITELIST_PACKAGE).scan()) { + public void scanWithAccept() { + try (ScanResult scanResult = new ClassGraph().acceptPackages(ACCEPT_PACKAGE).scan()) { final List allClasses = scanResult.getAllClasses().getNames(); assertThat(allClasses).doesNotContain(DefaultPackageTest.class.getName()); - assertThat(allClasses).contains(BlacklistedSub.class.getName()); + assertThat(allClasses).contains(RejectedSub.class.getName()); assertThat(allClasses).contains(Cls.class.getName()); assertThat(allClasses).doesNotContain(ClassGraph.class.getName()); assertThat(allClasses).doesNotContain(String.class.getName()); diff --git a/src/test/java/DisableRecursiveScanningTest.java b/src/test/java/DisableRecursiveScanningTest.java index c351b2cd0..8b0506b96 100644 --- a/src/test/java/DisableRecursiveScanningTest.java +++ b/src/test/java/DisableRecursiveScanningTest.java @@ -36,8 +36,8 @@ import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; -import io.github.classgraph.test.whitelisted.Cls; -import io.github.classgraph.test.whitelisted.blacklistedsub.BlacklistedSub; +import io.github.classgraph.test.accepted.Cls; +import io.github.classgraph.test.accepted.rejectedsub.RejectedSub; /** * DisableRecursiveScanningTest. @@ -51,10 +51,10 @@ public class DisableRecursiveScanningTest { */ @Test public void nonRootPackage() { - try (ScanResult scanResult = new ClassGraph().whitelistPackagesNonRecursive(PKG).scan()) { + try (ScanResult scanResult = new ClassGraph().acceptPackagesNonRecursive(PKG).scan()) { final List allClasses = scanResult.getAllClasses().getNames(); assertThat(allClasses).contains(Cls.class.getName()); - assertThat(allClasses).doesNotContain(BlacklistedSub.class.getName()); + assertThat(allClasses).doesNotContain(RejectedSub.class.getName()); } } @@ -63,7 +63,7 @@ public void nonRootPackage() { */ @Test public void rootPackage() { - try (ScanResult scanResult = new ClassGraph().whitelistPackagesNonRecursive("").scan()) { + try (ScanResult scanResult = new ClassGraph().acceptPackagesNonRecursive("").scan()) { final List allClasses = scanResult.getAllClasses().getNames(); assertThat(allClasses).contains(ClassInDefaultPackage.class.getName()); assertThat(allClasses).doesNotContain(Cls.class.getName()); diff --git a/src/test/java/com/xyz/GenerateClassGraphFigDotFile.java b/src/test/java/com/xyz/GenerateClassGraphFigDotFile.java index 083229f27..fb4ecb89e 100644 --- a/src/test/java/com/xyz/GenerateClassGraphFigDotFile.java +++ b/src/test/java/com/xyz/GenerateClassGraphFigDotFile.java @@ -15,7 +15,7 @@ public class GenerateClassGraphFigDotFile { */ public static void main(final String[] args) { try (ScanResult scanResult = new ClassGraph() // - .whitelistPackages("com.xyz.fig") // + .acceptPackages("com.xyz.fig") // .ignoreFieldVisibility() // .enableFieldInfo() // .ignoreMethodVisibility() // diff --git a/src/test/java/com/xyz/MetaAnnotationTest.java b/src/test/java/com/xyz/MetaAnnotationTest.java index f7bb8589a..b980942d6 100644 --- a/src/test/java/com/xyz/MetaAnnotationTest.java +++ b/src/test/java/com/xyz/MetaAnnotationTest.java @@ -49,7 +49,7 @@ class MetaAnnotationTest { */ @BeforeAll static void setUp() { - scanResult = new ClassGraph().whitelistPackages("com.xyz.meta").enableClassInfo().enableAnnotationInfo() + scanResult = new ClassGraph().acceptPackages("com.xyz.meta").enableClassInfo().enableAnnotationInfo() .scan(); } diff --git a/src/test/java/com/xyz/ScanEverything.java b/src/test/java/com/xyz/ScanEverything.java index 9f74d1043..327ba7405 100644 --- a/src/test/java/com/xyz/ScanEverything.java +++ b/src/test/java/com/xyz/ScanEverything.java @@ -9,7 +9,7 @@ public class ScanEverything { // final long t0 = System.nanoTime(); // try (ScanResult scanResult = new ClassGraph() // // // .verbose() // - // // .whitelistPackages("io.github") // + // // .acceptPackages("io.github") // // // .enableAllInfo() // // .enableSystemPackages() // // .disableJarScanning() // diff --git a/src/test/java/io/github/classgraph/features/AnnotationEqualityTest.java b/src/test/java/io/github/classgraph/features/AnnotationEqualityTest.java index fda22ce39..b65d005f7 100644 --- a/src/test/java/io/github/classgraph/features/AnnotationEqualityTest.java +++ b/src/test/java/io/github/classgraph/features/AnnotationEqualityTest.java @@ -84,7 +84,7 @@ private static class Y { @Test void annotationEquality() { try (ScanResult scanResult = new ClassGraph() - .whitelistPackages(AnnotationEqualityTest.class.getPackage().getName()).enableAllInfo().scan()) { + .acceptPackages(AnnotationEqualityTest.class.getPackage().getName()).enableAllInfo().scan()) { final ClassInfo classInfo = scanResult.getClassInfo(Y.class.getName()); assertThat(classInfo).isNotNull(); final Class cls = classInfo.loadClass(); diff --git a/src/test/java/io/github/classgraph/features/AnnotationParamWithPrimitiveTypedArrayTest.java b/src/test/java/io/github/classgraph/features/AnnotationParamWithPrimitiveTypedArrayTest.java index 309e20efd..195cbf394 100644 --- a/src/test/java/io/github/classgraph/features/AnnotationParamWithPrimitiveTypedArrayTest.java +++ b/src/test/java/io/github/classgraph/features/AnnotationParamWithPrimitiveTypedArrayTest.java @@ -94,8 +94,7 @@ public abstract static class AnnotatedClass { @Test public void primitiveArrayParams() { try (ScanResult scanResult = new ClassGraph().enableAllInfo() - .whitelistPackages(AnnotationParamWithPrimitiveTypedArrayTest.class.getPackage().getName()) - .scan()) { + .acceptPackages(AnnotationParamWithPrimitiveTypedArrayTest.class.getPackage().getName()).scan()) { final AnnotationInfo annotationInfo = scanResult.getClassInfo(AnnotatedClass.class.getName()) .getAnnotationInfo().get(0); final AnnotationParameterValueList annotationParams = annotationInfo.getParameterValues(); diff --git a/src/test/java/io/github/classgraph/features/DeclaredVsNonDeclaredTest.java b/src/test/java/io/github/classgraph/features/DeclaredVsNonDeclaredTest.java index 83900464f..d571b0a23 100644 --- a/src/test/java/io/github/classgraph/features/DeclaredVsNonDeclaredTest.java +++ b/src/test/java/io/github/classgraph/features/DeclaredVsNonDeclaredTest.java @@ -121,7 +121,7 @@ public abstract static class C extends A { @Test public void declaredVsNonDeclaredMethods() { try (ScanResult scanResult = new ClassGraph().enableAllInfo() - .whitelistPackages(DeclaredVsNonDeclaredTest.class.getPackage().getName()).scan()) { + .acceptPackages(DeclaredVsNonDeclaredTest.class.getPackage().getName()).scan()) { final ClassInfo A = scanResult.getClassInfo(A.class.getName()); final ClassInfo B = scanResult.getClassInfo(B.class.getName()); assertThat(B.getFieldInfo("x").getClassInfo().getName()).isEqualTo(B.class.getName()); @@ -140,7 +140,7 @@ public void declaredVsNonDeclaredMethods() { @Test public void annotationInfosShouldBeAbleToDifferentiateBetweenDirectAndReachable() { try (ScanResult scanResult = new ClassGraph().enableAllInfo() - .whitelistPackages(DeclaredVsNonDeclaredTest.class.getPackage().getName()).scan()) { + .acceptPackages(DeclaredVsNonDeclaredTest.class.getPackage().getName()).scan()) { final ClassInfo A = scanResult.getClassInfo(A.class.getName()); final ClassInfo B = scanResult.getClassInfo(B.class.getName()); final ClassInfo C = scanResult.getClassInfo(C.class.getName()); @@ -181,7 +181,7 @@ public void annotationInfosShouldBeAbleToDifferentiateBetweenDirectAndReachable( @Test public void annotationsShouldBeAbleToDifferentiateBetweenDirectAndReachable() { try (ScanResult scanResult = new ClassGraph().enableAllInfo() - .whitelistPackages(DeclaredVsNonDeclaredTest.class.getPackage().getName()).scan()) { + .acceptPackages(DeclaredVsNonDeclaredTest.class.getPackage().getName()).scan()) { final ClassInfo A = scanResult.getClassInfo(A.class.getName()); final ClassInfo B = scanResult.getClassInfo(B.class.getName()); @@ -204,7 +204,7 @@ public void annotationsShouldBeAbleToDifferentiateBetweenDirectAndReachable() { @Test public void loadFieldAndMethod() { try (ScanResult scanResult = new ClassGraph().enableAllInfo() - .whitelistPackages(DeclaredVsNonDeclaredTest.class.getPackage().getName()).scan()) { + .acceptPackages(DeclaredVsNonDeclaredTest.class.getPackage().getName()).scan()) { final ClassInfo B = scanResult.getClassInfo(B.class.getName()); assertThat(B.getFieldInfo("x").loadClassAndGetField().getName()).isEqualTo("x"); assertThat(B.getMethodInfo("y").get(0).loadClassAndGetMethod().getName()).isEqualTo("y"); diff --git a/src/test/java/io/github/classgraph/features/MethodParameterAnnotationsTest.java b/src/test/java/io/github/classgraph/features/MethodParameterAnnotationsTest.java index 5a9b201bb..f4d0d3cb4 100644 --- a/src/test/java/io/github/classgraph/features/MethodParameterAnnotationsTest.java +++ b/src/test/java/io/github/classgraph/features/MethodParameterAnnotationsTest.java @@ -62,7 +62,7 @@ private abstract static class Z { @Test public void annotationEquality() { try (ScanResult scanResult = new ClassGraph() - .whitelistPackages(MethodParameterAnnotationsTest.class.getPackage().getName()).enableAllInfo() + .acceptPackages(MethodParameterAnnotationsTest.class.getPackage().getName()).enableAllInfo() .scan()) { assertThat(scanResult.getClassInfo(Y.class.getName()).getMethodParameterAnnotations().getNames()) .containsOnly(W.class.getName()); diff --git a/src/test/java/io/github/classgraph/features/MultiReleaseJarTest.java b/src/test/java/io/github/classgraph/features/MultiReleaseJarTest.java index cbe10df88..c573b0b63 100644 --- a/src/test/java/io/github/classgraph/features/MultiReleaseJarTest.java +++ b/src/test/java/io/github/classgraph/features/MultiReleaseJarTest.java @@ -72,10 +72,10 @@ public void multiReleaseVersioningOfResources() 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 })) - .whitelistPaths("nonexistent_path").scan()) { + .overrideClassLoaders(new URLClassLoader(new URL[] { jarURL })).acceptPaths("nonexistent_path") + .scan()) { assertThat(scanResult.getResourcesWithPath("mrj/Cls.class")).isEmpty(); - assertThat(scanResult.getResourcesWithPathIgnoringWhitelist("mrj/Cls.class")).isNotEmpty(); + assertThat(scanResult.getResourcesWithPathIgnoringAccept("mrj/Cls.class")).isNotEmpty(); } } } diff --git a/src/test/java/io/github/classgraph/issues/DeclaredVsNonDeclaredTest.java b/src/test/java/io/github/classgraph/issues/DeclaredVsNonDeclaredTest.java index 9fc80f74f..e7007664d 100644 --- a/src/test/java/io/github/classgraph/issues/DeclaredVsNonDeclaredTest.java +++ b/src/test/java/io/github/classgraph/issues/DeclaredVsNonDeclaredTest.java @@ -194,7 +194,7 @@ public void publicDeclaredVsNonDeclared() { try (ScanResult scanResult = new ClassGraph().enableClassInfo() // .enableMethodInfo() // .enableFieldInfo() // - .whitelistPackages(DeclaredVsNonDeclaredTest.class.getPackage().getName()).scan()) { + .acceptPackages(DeclaredVsNonDeclaredTest.class.getPackage().getName()).scan()) { final ClassInfo superClassInfo = scanResult.getClassInfo(SuperClass.class.getName()); final ClassInfo subClassInfo = scanResult.getClassInfo(SubClass.class.getName()); compareResults(superClassInfo, subClassInfo, /* ignoreVisibility = */ false); @@ -210,7 +210,7 @@ public void publicAndPrivateDeclaredVsNonDeclared() { try (ScanResult scanResult = new ClassGraph().enableClassInfo() // .enableMethodInfo().ignoreMethodVisibility() // .enableFieldInfo().ignoreFieldVisibility() // - .whitelistPackages(DeclaredVsNonDeclaredTest.class.getPackage().getName()).scan()) { + .acceptPackages(DeclaredVsNonDeclaredTest.class.getPackage().getName()).scan()) { final ClassInfo superClassInfo = scanResult.getClassInfo(SuperClass.class.getName()); final ClassInfo subClassInfo = scanResult.getClassInfo(SubClass.class.getName()); compareResults(superClassInfo, subClassInfo, /* ignoreVisibility = */ true); diff --git a/src/test/java/io/github/classgraph/issues/GenericInnerClassTypedField.java b/src/test/java/io/github/classgraph/issues/GenericInnerClassTypedField.java index 2572eb74c..cde86a2a0 100644 --- a/src/test/java/io/github/classgraph/issues/GenericInnerClassTypedField.java +++ b/src/test/java/io/github/classgraph/issues/GenericInnerClassTypedField.java @@ -39,8 +39,7 @@ private class B { @Test public void testGenericInnerClassTypedField() { try (ScanResult scanResult = new ClassGraph() - .whitelistPackages(GenericInnerClassTypedField.class.getPackage().getName()).enableAllInfo() - .scan()) { + .acceptPackages(GenericInnerClassTypedField.class.getPackage().getName()).enableAllInfo().scan()) { final FieldInfoList fields = scanResult.getClassInfo(GenericInnerClassTypedField.class.getName()) .getFieldInfo(); final ClassRefTypeSignature classRefTypeSignature = (ClassRefTypeSignature) fields.get(0) diff --git a/src/test/java/io/github/classgraph/issues/IssuesTest.java b/src/test/java/io/github/classgraph/issues/IssuesTest.java index 2db103cf3..1ca9adecc 100644 --- a/src/test/java/io/github/classgraph/issues/IssuesTest.java +++ b/src/test/java/io/github/classgraph/issues/IssuesTest.java @@ -6,10 +6,10 @@ import io.github.classgraph.ClassGraph; import io.github.classgraph.ScanResult; +import io.github.classgraph.test.accepted.Impl1; +import io.github.classgraph.test.accepted.Impl1Sub; import io.github.classgraph.test.external.ExternalSuperclass; import io.github.classgraph.test.internal.InternalExtendsExternal; -import io.github.classgraph.test.whitelisted.Impl1; -import io.github.classgraph.test.whitelisted.Impl1Sub; /** * IssuesTest. @@ -20,8 +20,7 @@ public class IssuesTest { */ @Test public void issue70() { - try (ScanResult scanResult = new ClassGraph().whitelistPackages(Impl1.class.getPackage().getName()) - .scan()) { + try (ScanResult scanResult = new ClassGraph().acceptPackages(Impl1.class.getPackage().getName()).scan()) { assertThat(scanResult.getSubclasses(Object.class.getName()).getNames()).contains(Impl1.class.getName()); } } @@ -31,7 +30,7 @@ public void issue70() { */ @Test public void issue70EnableExternalClasses() { - try (ScanResult scanResult = new ClassGraph().whitelistPackages(Impl1.class.getPackage().getName()) + 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.getSuperclasses(Impl1Sub.class.getName()).getNames()) @@ -45,7 +44,7 @@ public void issue70EnableExternalClasses() { @Test public void extendsExternal() { try (ScanResult scanResult = new ClassGraph() - .whitelistPackages(InternalExtendsExternal.class.getPackage().getName()).scan()) { + .acceptPackages(InternalExtendsExternal.class.getPackage().getName()).scan()) { assertThat(scanResult.getSuperclasses(InternalExtendsExternal.class.getName()).getNames()) .containsOnly(ExternalSuperclass.class.getName()); } @@ -57,7 +56,7 @@ public void extendsExternal() { @Test public void extendsExternalWithEnableExternal() { try (ScanResult scanResult = new ClassGraph() - .whitelistPackages(InternalExtendsExternal.class.getPackage().getName()).enableExternalClasses() + .acceptPackages(InternalExtendsExternal.class.getPackage().getName()).enableExternalClasses() .scan()) { assertThat(scanResult.getSuperclasses(InternalExtendsExternal.class.getName()).getNames()) .containsOnly(ExternalSuperclass.class.getName()); @@ -70,7 +69,7 @@ public void extendsExternalWithEnableExternal() { @Test public void extendsExternalSubclass() { try (ScanResult scanResult = new ClassGraph() - .whitelistPackages(InternalExtendsExternal.class.getPackage().getName()).scan()) { + .acceptPackages(InternalExtendsExternal.class.getPackage().getName()).scan()) { assertThat(scanResult.getSubclasses(ExternalSuperclass.class.getName()).getNames()) .containsOnly(InternalExtendsExternal.class.getName()); } @@ -82,7 +81,7 @@ public void extendsExternalSubclass() { @Test public void nonStrictExtendsExternalSubclass() { try (ScanResult scanResult = new ClassGraph() - .whitelistPackages(InternalExtendsExternal.class.getPackage().getName()).enableExternalClasses() + .acceptPackages(InternalExtendsExternal.class.getPackage().getName()).enableExternalClasses() .scan()) { assertThat(scanResult.getSubclasses(ExternalSuperclass.class.getName()).getNames()) .containsOnly(InternalExtendsExternal.class.getName()); diff --git a/src/test/java/io/github/classgraph/issues/ResolveTypeVariableTest.java b/src/test/java/io/github/classgraph/issues/ResolveTypeVariableTest.java index c1a5d9850..6017f477f 100644 --- a/src/test/java/io/github/classgraph/issues/ResolveTypeVariableTest.java +++ b/src/test/java/io/github/classgraph/issues/ResolveTypeVariableTest.java @@ -28,7 +28,7 @@ public class ResolveTypeVariableTest> { @Test public void test() { try (ScanResult scanResult = new ClassGraph() - .whitelistPackages(ResolveTypeVariableTest.class.getPackage().getName()).enableAllInfo().scan()) { + .acceptPackages(ResolveTypeVariableTest.class.getPackage().getName()).enableAllInfo().scan()) { final FieldInfoList fields = scanResult.getClassInfo(ResolveTypeVariableTest.class.getName()) .getFieldInfo(); assertThat(((TypeVariableSignature) fields.get(0).getTypeSignature()).resolve().toString()) diff --git a/src/test/java/io/github/classgraph/issues/TestGetUniqueClasspathElements.java b/src/test/java/io/github/classgraph/issues/TestGetUniqueClasspathElements.java index 03e64408a..037b45a0d 100644 --- a/src/test/java/io/github/classgraph/issues/TestGetUniqueClasspathElements.java +++ b/src/test/java/io/github/classgraph/issues/TestGetUniqueClasspathElements.java @@ -18,7 +18,7 @@ class TestGetUniqueClasspathElements { */ @Test void testGetUniqueClasspathElements() { - final List classpathElements = new ClassGraph().whitelistPackages("com.xyz").getClasspathFiles(); + final List classpathElements = new ClassGraph().acceptPackages("com.xyz").getClasspathFiles(); assertThat(classpathElements).isNotEmpty(); } } diff --git a/src/test/java/io/github/classgraph/issues/issue100/Issue100Test.java b/src/test/java/io/github/classgraph/issues/issue100/Issue100Test.java index a8886c079..1cc96d4a5 100644 --- a/src/test/java/io/github/classgraph/issues/issue100/Issue100Test.java +++ b/src/test/java/io/github/classgraph/issues/issue100/Issue100Test.java @@ -61,7 +61,7 @@ public void issue100Test() { // earlier in classpath than "...b.jar" final ArrayList fieldNames1 = new ArrayList<>(); try (ScanResult scanResult = new ClassGraph().overrideClassLoaders(overrideClassLoader) - .whitelistPackages("issue100").blacklistJars(bJarName).enableFieldInfo().scan()) { + .acceptPackages("issue100").rejectJars(bJarName).enableFieldInfo().scan()) { for (final ClassInfo ci : scanResult.getAllClasses()) { for (final FieldInfo f : ci.getFieldInfo()) { fieldNames1.add(f.getName()); @@ -70,14 +70,14 @@ public void issue100Test() { } assertThat(fieldNames1).containsOnly("a"); - // However, if "...b.jar" is specifically whitelisted, "...a.jar" should not be visible. Originally, the + // However, if "...b.jar" is specifically accepted, "...a.jar" should not be visible. Originally, the // version of the class in "...a.jar" was supposed to mask the same class in "...b.jar" (#100). However, // this resulted in a slowdown in scan time (#117). Since classloading behavior is undefined if you override // the classpath (or in this case, the classloaders), we should only see field "b" in "...b.jar" (which is - // what we actually see through scanning the whitelisted jar, "bJarName"). + // what we actually see through scanning the accepted jar, "bJarName"). final ArrayList fieldNames2 = new ArrayList<>(); try (ScanResult scanResult = new ClassGraph().overrideClassLoaders(overrideClassLoader) - .whitelistPackages("issue100").whitelistJars(bJarName).enableFieldInfo().scan()) { + .acceptPackages("issue100").acceptJars(bJarName).enableFieldInfo().scan()) { for (final ClassInfo ci : scanResult.getAllClasses()) { for (final FieldInfo f : ci.getFieldInfo()) { fieldNames2.add(f.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 34caca80f..421326983 100644 --- a/src/test/java/io/github/classgraph/issues/issue101/Issue101Test.java +++ b/src/test/java/io/github/classgraph/issues/issue101/Issue101Test.java @@ -44,7 +44,7 @@ public class Issue101Test { */ @Test public void nonInheritedAnnotation() { - try (ScanResult scanResult = new ClassGraph().whitelistPackages(Issue101Test.class.getPackage().getName()) + try (ScanResult scanResult = new ClassGraph().acceptPackages(Issue101Test.class.getPackage().getName()) .enableAllInfo().scan()) { assertThat(scanResult.getClassesWithAnnotation(NonInheritedAnnotation.class.getName()).getNames()) .containsOnly(AnnotatedClass.class.getName()); @@ -56,7 +56,7 @@ public void nonInheritedAnnotation() { */ @Test public void inheritedMetaAnnotation() { - try (ScanResult scanResult = new ClassGraph().whitelistPackages(Issue101Test.class.getPackage().getName()) + try (ScanResult scanResult = new ClassGraph().acceptPackages(Issue101Test.class.getPackage().getName()) .enableAllInfo().scan()) { assertThat(scanResult.getClassesWithAnnotation(InheritedMetaAnnotation.class.getName()) .getStandardClasses().getNames()).containsOnly(AnnotatedClass.class.getName(), @@ -69,7 +69,7 @@ public void inheritedMetaAnnotation() { */ @Test public void inheritedAnnotation() { - try (ScanResult scanResult = new ClassGraph().whitelistPackages(Issue101Test.class.getPackage().getName()) + try (ScanResult scanResult = new ClassGraph().acceptPackages(Issue101Test.class.getPackage().getName()) .enableAllInfo().scan()) { assertThat(scanResult.getClassesWithAnnotation(InheritedAnnotation.class.getName()).getNames()) .containsOnly(AnnotatedClass.class.getName(), NonAnnotatedSubclass.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 bd3129994..629848cac 100644 --- a/src/test/java/io/github/classgraph/issues/issue107/Issue107Test.java +++ b/src/test/java/io/github/classgraph/issues/issue107/Issue107Test.java @@ -48,7 +48,7 @@ public class Issue107Test { 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().whitelistPackages(pkg).enableAnnotationInfo() + try (ScanResult scanResult = new ClassGraph().acceptPackages(pkg).enableAnnotationInfo() // package-info is a non-public class .ignoreClassVisibility() // .scan()) { diff --git a/src/test/java/io/github/classgraph/issues/issue140/Issue140Test.java b/src/test/java/io/github/classgraph/issues/issue140/Issue140Test.java index 93664e8e1..0ea444094 100644 --- a/src/test/java/io/github/classgraph/issues/issue140/Issue140Test.java +++ b/src/test/java/io/github/classgraph/issues/issue140/Issue140Test.java @@ -57,7 +57,7 @@ public class Issue140Test { */ @Test public void issue140Test() { - try (ScanResult scanResult = new ClassGraph().whitelistPackages(Issue140Test.class.getPackage().getName()) + try (ScanResult scanResult = new ClassGraph().acceptPackages(Issue140Test.class.getPackage().getName()) .enableFieldInfo().scan()) { final ClassInfo ci = scanResult.getClassInfo(Issue140Test.class.getName()); assertThat(ci).isNotNull(); diff --git a/src/test/java/io/github/classgraph/issues/issue146/Issue146Test.java b/src/test/java/io/github/classgraph/issues/issue146/Issue146Test.java index 435bf047b..a3a0687b7 100644 --- a/src/test/java/io/github/classgraph/issues/issue146/Issue146Test.java +++ b/src/test/java/io/github/classgraph/issues/issue146/Issue146Test.java @@ -49,7 +49,7 @@ public void issue146Test() { // Scans io.github.classgraph.issues.issue146.CompiledWithJDK8, which is in // src/test/resources final String pkg = Issue146Test.class.getPackage().getName(); - try (ScanResult scanResult = new ClassGraph().whitelistPackages(pkg) // + try (ScanResult scanResult = new ClassGraph().acceptPackages(pkg) // .enableMethodInfo() // .scan()) { final ClassInfo classInfo = scanResult.getClassInfo(pkg + "." + "CompiledWithJDK8"); diff --git a/src/test/java/io/github/classgraph/issues/issue148/Issue148Test.java b/src/test/java/io/github/classgraph/issues/issue148/Issue148Test.java index 2c814b25a..a6198a9e8 100644 --- a/src/test/java/io/github/classgraph/issues/issue148/Issue148Test.java +++ b/src/test/java/io/github/classgraph/issues/issue148/Issue148Test.java @@ -63,7 +63,7 @@ public void run() { final String pkg = Issue148Test.class.getPackage().getName(); final StringBuilder buf = new StringBuilder(); - try (ScanResult scanResult = new ClassGraph().whitelistPackages(pkg).enableAllInfo().scan()) { + try (ScanResult scanResult = new ClassGraph().acceptPackages(pkg).enableAllInfo().scan()) { for (final ClassInfo ci : scanResult.getAllClasses()) { buf.append(ci.getName()).append("|"); buf.append(ci.isInnerClass()).append(" ").append(ci.isAnonymousInnerClass()).append(" ") 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 54c3c1cb0..89508258f 100644 --- a/src/test/java/io/github/classgraph/issues/issue151/Issue151Test.java +++ b/src/test/java/io/github/classgraph/issues/issue151/Issue151Test.java @@ -53,7 +53,7 @@ public void issue151Test() { // Scans io.github.classgraph.issues.issue146.CompiledWithJDK8, which is in // src/test/resources final String pkg = Issue151Test.class.getPackage().getName(); - try (ScanResult scanResult = new ClassGraph().whitelistPackages(pkg) // + try (ScanResult scanResult = new ClassGraph().acceptPackages(pkg) // .enableMethodInfo() // .enableAnnotationInfo() // .scan()) { 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 1aa03d145..377b727c1 100644 --- a/src/test/java/io/github/classgraph/issues/issue152/Issue152Test.java +++ b/src/test/java/io/github/classgraph/issues/issue152/Issue152Test.java @@ -89,7 +89,7 @@ public static class TestType { @Test public void issue152Test() { final String pkg = Issue152Test.class.getPackage().getName(); - try (ScanResult scanResult = new ClassGraph().whitelistPackages(pkg) // + try (ScanResult scanResult = new ClassGraph().acceptPackages(pkg) // .enableMethodInfo() // .enableFieldInfo() // .scan()) { diff --git a/src/test/java/io/github/classgraph/issues/issue153/Issue153Test.java b/src/test/java/io/github/classgraph/issues/issue153/Issue153Test.java index 89e2be04f..2d219cf9f 100644 --- a/src/test/java/io/github/classgraph/issues/issue153/Issue153Test.java +++ b/src/test/java/io/github/classgraph/issues/issue153/Issue153Test.java @@ -208,7 +208,7 @@ public void testMethod() { @Test public void classAnnotationParameters() { try (ScanResult scanResult = new ClassGraph() // - .whitelistPackages(pkg) // + .acceptPackages(pkg) // .enableMethodInfo() // .enableFieldInfo() // .enableAnnotationInfo() // diff --git a/src/test/java/io/github/classgraph/issues/issue167/Issue167Test.java b/src/test/java/io/github/classgraph/issues/issue167/Issue167Test.java index 187ba83ab..5869c324e 100644 --- a/src/test/java/io/github/classgraph/issues/issue167/Issue167Test.java +++ b/src/test/java/io/github/classgraph/issues/issue167/Issue167Test.java @@ -66,7 +66,7 @@ public class Issue167Test { */ @Test public void scanPackagesTest1() { - try (ScanResult scanResult = new ClassGraph().whitelistPackagesNonRecursive(packages.toArray(new String[0])) + try (ScanResult scanResult = new ClassGraph().acceptPackagesNonRecursive(packages.toArray(new String[0])) .enableClassInfo().scan()) { assertEquals(classNames, scanResult.getAllClasses().getNames()); } @@ -80,7 +80,7 @@ public void scanPackagesTest2() { final List reversedPackages = new ArrayList<>(packages); Collections.reverse(reversedPackages); try (ScanResult scanResult = new ClassGraph() - .whitelistPackagesNonRecursive(reversedPackages.toArray(new String[0])).enableClassInfo().scan()) { + .acceptPackagesNonRecursive(reversedPackages.toArray(new String[0])).enableClassInfo().scan()) { assertEquals(classNames, scanResult.getAllClasses().getNames()); } } 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 0a882268f..683c04d41 100644 --- a/src/test/java/io/github/classgraph/issues/issue171/Issue171Test.java +++ b/src/test/java/io/github/classgraph/issues/issue171/Issue171Test.java @@ -22,7 +22,7 @@ public void springBootFullyExecutableJar() { final URL jarURL = Issue171Test.class.getClassLoader().getResource("spring-boot-fully-executable-jar.jar"); try (ScanResult scanResult = new ClassGraph() - .whitelistPackagesNonRecursive("hello", "org.springframework.boot") + .acceptPackagesNonRecursive("hello", "org.springframework.boot") .overrideClasspath(jarURL + "!BOOT-INF/classes") // .scan()) { final List classNames = scanResult.getAllClasses().getNames(); 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 f960293ff..b3f3650a8 100644 --- a/src/test/java/io/github/classgraph/issues/issue175/Issue175Test.java +++ b/src/test/java/io/github/classgraph/issues/issue175/Issue175Test.java @@ -56,7 +56,7 @@ public void testSynthetic() { final URL aJarURL = classLoader.getResource(aJarName); final URLClassLoader overrideClassLoader = new URLClassLoader(new URL[] { aJarURL }); - try (ScanResult result = new ClassGraph().whitelistPackages("net.corda.core.contracts") // + try (ScanResult result = new ClassGraph().acceptPackages("net.corda.core.contracts") // .overrideClassLoaders(overrideClassLoader).ignoreParentClassLoaders().ignoreMethodVisibility() .ignoreFieldVisibility().enableMethodInfo().enableFieldInfo().scan()) { final List methods = new ArrayList<>(); @@ -83,7 +83,7 @@ public void testMandated() { final URL aJarURL = classLoader.getResource(aJarName); final URLClassLoader overrideClassLoader = new URLClassLoader(new URL[] { aJarURL }); - try (ScanResult result = new ClassGraph().whitelistPackages("net.corda.core") // + try (ScanResult result = new ClassGraph().acceptPackages("net.corda.core") // .overrideClassLoaders(overrideClassLoader).ignoreParentClassLoaders().enableAllInfo().scan()) { final List methods = new ArrayList<>(); for (final String className : result.getAllClasses().getNames()) { @@ -108,7 +108,7 @@ public void testMismatchedTypes() { final URL aJarURL = classLoader.getResource(aJarName); final URLClassLoader overrideClassLoader = new URLClassLoader(new URL[] { aJarURL }); - try (ScanResult result = new ClassGraph().whitelistPackages("net.corda.core") // + try (ScanResult result = new ClassGraph().acceptPackages("net.corda.core") // .overrideClassLoaders(overrideClassLoader).ignoreParentClassLoaders().enableAllInfo().scan()) { final List methods = new ArrayList<>(); for (final String className : result.getAllClasses().getNames()) { @@ -135,7 +135,7 @@ public void testResultTypesNotReconciled1() { final URL aJarURL = classLoader.getResource(aJarName); final URLClassLoader overrideClassLoader = new URLClassLoader(new URL[] { aJarURL }); - try (ScanResult result = new ClassGraph().whitelistPackages("net.corda.core.contracts") // + try (ScanResult result = new ClassGraph().acceptPackages("net.corda.core.contracts") // .overrideClassLoaders(overrideClassLoader).ignoreParentClassLoaders().enableAllInfo().scan()) { final List methods = new ArrayList<>(); for (final String className : result.getAllClasses().getNames()) { @@ -169,7 +169,7 @@ public void testResultTypesNotReconciled2() { final URL aJarURL = classLoader.getResource(aJarName); final URLClassLoader overrideClassLoader = new URLClassLoader(new URL[] { aJarURL }); - try (ScanResult result = new ClassGraph().whitelistPackages("net.corda.testing.node") // + try (ScanResult result = new ClassGraph().acceptPackages("net.corda.testing.node") // .overrideClassLoaders(overrideClassLoader).ignoreParentClassLoaders().enableAllInfo().scan()) { final List methods = new ArrayList<>(); for (final String className : result.getAllClasses().getNames()) { @@ -231,7 +231,7 @@ public void testAttributeParameterMismatch() { final URL aJarURL = classLoader.getResource(aJarName); final URLClassLoader overrideClassLoader = new URLClassLoader(new URL[] { aJarURL }); - try (ScanResult result = new ClassGraph().whitelistPackages("net.corda.core.node.services.vault") // + try (ScanResult result = new ClassGraph().acceptPackages("net.corda.core.node.services.vault") // .overrideClassLoaders(overrideClassLoader).ignoreParentClassLoaders().enableAllInfo().scan()) { final List methods = new ArrayList<>(); for (final String className : result.getAllClasses().getNames()) { @@ -258,7 +258,7 @@ public void testResultTypeReconciliationIssue() { final URL aJarURL = classLoader.getResource(aJarName); final URLClassLoader overrideClassLoader = new URLClassLoader(new URL[] { aJarURL }); - try (ScanResult result = new ClassGraph().whitelistPackages("net.corda.client.jackson") // + try (ScanResult result = new ClassGraph().acceptPackages("net.corda.client.jackson") // .overrideClassLoaders(overrideClassLoader).ignoreParentClassLoaders().enableAllInfo().scan()) { final List methods = new ArrayList<>(); for (final String className : result.getAllClasses().getNames()) { @@ -294,7 +294,7 @@ public void testParameterArityMismatch() { final URL aJarURL = classLoader.getResource(aJarName); final URLClassLoader overrideClassLoader = new URLClassLoader(new URL[] { aJarURL }); - try (ScanResult result = new ClassGraph().whitelistPackages("net.corda.core.node.services.vault") // + try (ScanResult result = new ClassGraph().acceptPackages("net.corda.core.node.services.vault") // .overrideClassLoaders(overrideClassLoader).ignoreParentClassLoaders().enableAllInfo().scan()) { final List methods = new ArrayList<>(); for (final String className : result.getAllClasses().getNames()) { @@ -318,7 +318,7 @@ public void testBareTypeIssue() { final URL aJarURL = classLoader.getResource(aJarName); final URLClassLoader overrideClassLoader = new URLClassLoader(new URL[] { aJarURL }); - try (ScanResult result = new ClassGraph().whitelistPackages("net.corda.client.jackson") // + try (ScanResult result = new ClassGraph().acceptPackages("net.corda.client.jackson") // .overrideClassLoaders(overrideClassLoader).ignoreParentClassLoaders().enableAllInfo().scan()) { final List methods = new ArrayList<>(); for (final String className : result.getAllClasses().getNames()) { diff --git a/src/test/java/io/github/classgraph/issues/issue193/Issue193Test.java b/src/test/java/io/github/classgraph/issues/issue193/Issue193Test.java index 27e743113..fc50d7061 100644 --- a/src/test/java/io/github/classgraph/issues/issue193/Issue193Test.java +++ b/src/test/java/io/github/classgraph/issues/issue193/Issue193Test.java @@ -65,7 +65,7 @@ public void issue193Test() throws IOException { // Scan the classpath -- used to throw an exception for Stack, since companion object inherits // from different class try (ScanResult scanResult = new ClassGraph() // - .whitelistPackages("scala.collection.immutable") // + .acceptPackages("scala.collection.immutable") // .overrideClassLoaders(classLoader) // .scan()) { final List classes = scanResult // diff --git a/src/test/java/io/github/classgraph/issues/issue209/Issue209Test.java b/src/test/java/io/github/classgraph/issues/issue209/Issue209Test.java index 2862042c1..70515dbb2 100644 --- a/src/test/java/io/github/classgraph/issues/issue209/Issue209Test.java +++ b/src/test/java/io/github/classgraph/issues/issue209/Issue209Test.java @@ -47,7 +47,7 @@ public class Issue209Test { */ @Test public void testSpringBootJarWithLibJars() { - try (ScanResult result = new ClassGraph().whitelistPackages( // + try (ScanResult result = new ClassGraph().acceptPackages( // "org.springframework.boot.loader.util", "com.foo", "issue209lib") // .overrideClassLoaders(new URLClassLoader( new URL[] { Issue209Test.class.getClassLoader().getResource("issue209.jar") })) // 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 5567c7ee1..0ead78230 100644 --- a/src/test/java/io/github/classgraph/issues/issue216/Issue216Test.java +++ b/src/test/java/io/github/classgraph/issues/issue216/Issue216Test.java @@ -49,7 +49,7 @@ public class Issue216Test { */ @Test public void testSpringBootJarWithLibJars() { - try (ScanResult result = new ClassGraph().whitelistPackages(Issue216Test.class.getPackage().getName()) + try (ScanResult result = new ClassGraph().acceptPackages(Issue216Test.class.getPackage().getName()) .enableAllInfo().scan()) { assertThat(result.getAllClasses().filter(new ClassInfoFilter() { @Override diff --git a/src/test/java/io/github/classgraph/issues/issue223/Issue223Test.java b/src/test/java/io/github/classgraph/issues/issue223/Issue223Test.java index 22c2a9b85..95ae80910 100644 --- a/src/test/java/io/github/classgraph/issues/issue223/Issue223Test.java +++ b/src/test/java/io/github/classgraph/issues/issue223/Issue223Test.java @@ -56,7 +56,7 @@ public interface InnerInterface { */ @Test public void testClassloadInnerClasses() { - try (ScanResult scanResult = new ClassGraph().whitelistPackages(Issue223Test.class.getPackage().getName()) + try (ScanResult scanResult = new ClassGraph().acceptPackages(Issue223Test.class.getPackage().getName()) .enableAllInfo().scan()) { final ClassInfoList innerClasses = scanResult.getAllClasses().filter(new ClassInfoFilter() { @Override diff --git a/src/test/java/io/github/classgraph/issues/issue238/Issue238Test.java b/src/test/java/io/github/classgraph/issues/issue238/Issue238Test.java index 3e6986c9d..7d197efdd 100644 --- a/src/test/java/io/github/classgraph/issues/issue238/Issue238Test.java +++ b/src/test/java/io/github/classgraph/issues/issue238/Issue238Test.java @@ -91,7 +91,7 @@ public static class F extends A { */ @Test public void testSuperclassInheritanceOrder() { - try (ScanResult scanResult = new ClassGraph().whitelistPackages(Issue238Test.class.getPackage().getName()) + try (ScanResult scanResult = new ClassGraph().acceptPackages(Issue238Test.class.getPackage().getName()) .enableAllInfo().scan()) { final List classNames = scanResult.getAllClasses().get(E.class.getName()).getSuperclasses() .getNames(); diff --git a/src/test/java/io/github/classgraph/issues/issue245/Issue245Test.java b/src/test/java/io/github/classgraph/issues/issue245/Issue245Test.java index 69edc1644..cb25917a3 100644 --- a/src/test/java/io/github/classgraph/issues/issue245/Issue245Test.java +++ b/src/test/java/io/github/classgraph/issues/issue245/Issue245Test.java @@ -52,7 +52,7 @@ public void testCustomPackageRoot() { try (ScanResult scanResult = new ClassGraph() // .overrideClasspath(jarURL.toString() + "!/META-INF/maven") // - .whitelistPaths("org.springframework/gs-spring-boot") // + .acceptPaths("org.springframework/gs-spring-boot") // .disableNestedJarScanning() // .scan()) { assertThat(scanResult.getAllResources().getPaths()).containsOnly( diff --git a/src/test/java/io/github/classgraph/issues/issue246/Issue246Test.java b/src/test/java/io/github/classgraph/issues/issue246/Issue246Test.java index 17ee2234f..688b91104 100644 --- a/src/test/java/io/github/classgraph/issues/issue246/Issue246Test.java +++ b/src/test/java/io/github/classgraph/issues/issue246/Issue246Test.java @@ -45,7 +45,7 @@ public class Issue246Test { @Test public void testMethodParameterAnnotations() { try (ScanResult scanResult = new ClassGraph() // - .whitelistClasses(Issue246Test.class.getName()) // + .acceptClasses(Issue246Test.class.getName()) // .enableAllInfo() // .scan()) { assertEquals(0, // diff --git a/src/test/java/io/github/classgraph/issues/issue260/Issue260Test.java b/src/test/java/io/github/classgraph/issues/issue260/Issue260Test.java index f437c0591..ed3688923 100644 --- a/src/test/java/io/github/classgraph/issues/issue260/Issue260Test.java +++ b/src/test/java/io/github/classgraph/issues/issue260/Issue260Test.java @@ -44,7 +44,7 @@ public class Issue260Test { */ @Test public void issue260Test() { - try (ScanResult scanResult = new ClassGraph().whitelistPackages(Issue260Test.class.getPackage().getName()) + try (ScanResult scanResult = new ClassGraph().acceptPackages(Issue260Test.class.getPackage().getName()) .enableAllInfo().scan()) { // Should be no exception here assertThat(true).isTrue(); 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 ea6ff8596..6f52e4580 100644 --- a/src/test/java/io/github/classgraph/issues/issue261/Issue261Test.java +++ b/src/test/java/io/github/classgraph/issues/issue261/Issue261Test.java @@ -62,9 +62,8 @@ private static class Cls extends SuperCls { */ @Test public void issue261Test() { - // Whitelist only the class Cls, so that SuperCls and SuperSuperCls are external classes - try (ScanResult scanResult = new ClassGraph().whitelistClasses(Cls.class.getName()).enableAllInfo() - .scan()) { + // 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()) .containsOnly(SuperCls.class.getName(), Cls.class.getName()); } diff --git a/src/test/java/io/github/classgraph/issues/issue267/ClassLoadingWorksWithParentLastLoaders.java b/src/test/java/io/github/classgraph/issues/issue267/ClassLoadingWorksWithParentLastLoaders.java index 2e0d349ba..1a6e1b294 100644 --- a/src/test/java/io/github/classgraph/issues/issue267/ClassLoadingWorksWithParentLastLoaders.java +++ b/src/test/java/io/github/classgraph/issues/issue267/ClassLoadingWorksWithParentLastLoaders.java @@ -61,7 +61,7 @@ public void assertCorrectClassLoaders(final String parentClassLoader, final Stri .isEqualTo(expectedClassLoader); assertThat(a.getClass().getClassLoader().getClass().getSimpleName()).isEqualTo(expectedClassLoader); - final ClassGraph classGraph = new ClassGraph().whitelistPackages("com.xyz.meta").enableAllInfo(); + final ClassGraph classGraph = new ClassGraph().acceptPackages("com.xyz.meta").enableAllInfo(); // ClassGraph is in that setup not part of the RestartClass loader. That one takes by default only // URLs from the current project into consideration and can only be modified by adding additional diff --git a/src/test/java/io/github/classgraph/issues/issue277/Issue227Test.java b/src/test/java/io/github/classgraph/issues/issue277/Issue227Test.java index 355157d63..3de570a0d 100644 --- a/src/test/java/io/github/classgraph/issues/issue277/Issue227Test.java +++ b/src/test/java/io/github/classgraph/issues/issue277/Issue227Test.java @@ -9,18 +9,18 @@ */ public class Issue227Test { /** - * Test no args blacklist lib or ext jars. + * Test no args reject lib or ext jars. */ @Test - public void testNoArgsBlacklistLibOrExtJars() { - new ClassGraph().blacklistLibOrExtJars(); + public void testNoArgsRejectLibOrExtJars() { + new ClassGraph().rejectLibOrExtJars(); } /** - * Test no args whitelist lib or ext jars. + * Test no args accept lib or ext jars. */ @Test - public void testNoArgsWhitelistLibOrExtJars() { - new ClassGraph().whitelistLibOrExtJars(); + public void testNoArgsAcceptLibOrExtJars() { + new ClassGraph().acceptLibOrExtJars(); } } diff --git a/src/test/java/io/github/classgraph/issues/issue303/Issue303Test.java b/src/test/java/io/github/classgraph/issues/issue303/Issue303Test.java index 885c5485f..6d22b0c31 100644 --- a/src/test/java/io/github/classgraph/issues/issue303/Issue303Test.java +++ b/src/test/java/io/github/classgraph/issues/issue303/Issue303Test.java @@ -55,12 +55,12 @@ public void testPackageInfoClasses() { final List packageClassNamesNonRecursive0; final List packageClassNamesNonRecursive1; final List packageClassNamesNonRecursive2; - try (ScanResult scanResult = new ClassGraph().whitelistPackages(PACKAGE_NAME).enableAllInfo().scan()) { + try (ScanResult scanResult = new ClassGraph().acceptPackages(PACKAGE_NAME).enableAllInfo().scan()) { packageClassNamesRecursive = scanResult.getPackageInfo(PACKAGE_NAME).getClassInfoRecursive().getNames(); packageClassNamesNonRecursive0 = scanResult.getPackageInfo(PACKAGE_NAME).getClassInfo().getNames(); allClassNamesRecursive = scanResult.getAllClasses().getNames(); } - try (ScanResult scanResult = new ClassGraph().whitelistPackagesNonRecursive(PACKAGE_NAME).enableAllInfo() + try (ScanResult scanResult = new ClassGraph().acceptPackagesNonRecursive(PACKAGE_NAME).enableAllInfo() .scan()) { packageClassNamesNonRecursive1 = scanResult.getPackageInfo(PACKAGE_NAME).getClassInfoRecursive() .getNames(); diff --git a/src/test/java/io/github/classgraph/issues/issue310/Issue310.java b/src/test/java/io/github/classgraph/issues/issue310/Issue310.java index 07316d763..f8048b768 100644 --- a/src/test/java/io/github/classgraph/issues/issue310/Issue310.java +++ b/src/test/java/io/github/classgraph/issues/issue310/Issue310.java @@ -39,7 +39,7 @@ public void issue310() { final String classpathBase = classfileURL.substring(0, classfileURL.length() - (Issue310.class.getName().length() + 6)); try (ScanResult scanResult1 = new ClassGraph().overrideClasspath(classpathBase) - .whitelistClasses(Issue310.class.getName()).enableAllInfo().scan()) { + .acceptClasses(Issue310.class.getName()).enableAllInfo().scan()) { assertThat(scanResult1.getClassInfo(Issue310.class.getName()).getFieldInfo("B")).isNotNull(); final String json1 = scanResult1.toJSON(2); assertThat(json1).isNotEmpty(); 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 de8009fc6..d0016228c 100644 --- a/src/test/java/io/github/classgraph/issues/issue314/Issue314.java +++ b/src/test/java/io/github/classgraph/issues/issue314/Issue314.java @@ -36,7 +36,7 @@ public void issue314() { final String classpathBase = classfileURL.substring(0, classfileURL.length() - (Issue314.class.getName().length() + 6)); try (ScanResult scanResult1 = new ClassGraph().overrideClasspath(classpathBase) - .whitelistPackages(Issue314.class.getPackage().getName()).enableAllInfo().scan()) { + .acceptPackages(Issue314.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/Issue318.java index b47446330..b207e598d 100644 --- a/src/test/java/io/github/classgraph/issues/issue318/Issue318.java +++ b/src/test/java/io/github/classgraph/issues/issue318/Issue318.java @@ -75,7 +75,7 @@ class With3MyAnn { */ @Test public void issue318() { - try (final ScanResult scanResult = new ClassGraph().whitelistPackages(Issue318.class.getPackage().getName()) + try (final ScanResult scanResult = new ClassGraph().acceptPackages(Issue318.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/Issue329.java index 4c3314d7a..25b19ed10 100644 --- a/src/test/java/io/github/classgraph/issues/issue329/Issue329.java +++ b/src/test/java/io/github/classgraph/issues/issue329/Issue329.java @@ -28,7 +28,7 @@ public class Bar { @Test public void test() { try (ScanResult scanResult = new ClassGraph().enableAllInfo().enableInterClassDependencies() - .enableExternalClasses().whitelistClasses(Foo.class.getName()).scan()) { + .enableExternalClasses().acceptClasses(Foo.class.getName()).scan()) { final ClassInfo classInfo = scanResult.getClassInfo(Foo.class.getName()); assertThat(classInfo.getClassDependencies().getNames()).containsOnly(Issue329.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/Issue339.java index e2e0e10d9..d4cb248cc 100644 --- a/src/test/java/io/github/classgraph/issues/issue339/Issue339.java +++ b/src/test/java/io/github/classgraph/issues/issue339/Issue339.java @@ -54,7 +54,7 @@ public void method() { @Test public void test() { try (ScanResult scanResult = new ClassGraph().enableAllInfo().enableExternalClasses() - .whitelistClasses(Cls.class.getName()).scan()) { + .acceptClasses(Cls.class.getName()).scan()) { final ClassInfo classInfo = scanResult.getClassInfo(Cls.class.getName()); final AnnotationParameterValueList annotationParamVals = classInfo.getMethodInfo("method").get(0) .getAnnotationInfo().get(0).getParameterValues(); diff --git a/src/test/java/io/github/classgraph/issues/issue345/Issue345.java b/src/test/java/io/github/classgraph/issues/issue345/Issue345.java index ac37631a4..ee8ee9a3c 100644 --- a/src/test/java/io/github/classgraph/issues/issue345/Issue345.java +++ b/src/test/java/io/github/classgraph/issues/issue345/Issue345.java @@ -33,7 +33,7 @@ public static class Sub extends Super { */ @Test public void withIgnoreClassVisibility() { - try (ScanResult scanResult = new ClassGraph().whitelistClasses(Super.class.getName(), Sub.class.getName()) + try (ScanResult scanResult = new ClassGraph().acceptClasses(Super.class.getName(), Sub.class.getName()) .ignoreClassVisibility().scan()) { final ClassInfo subClassInfo = scanResult.getClassInfo(Sub.class.getName()); assertThat(subClassInfo).isNotNull(); @@ -50,7 +50,7 @@ public void withIgnoreClassVisibility() { */ @Test public void withoutIgnoreClassVisibility() { - try (ScanResult scanResult = new ClassGraph().whitelistClasses(Super.class.getName(), Sub.class.getName()) + try (ScanResult scanResult = new ClassGraph().acceptClasses(Super.class.getName(), Sub.class.getName()) .scan()) { final ClassInfo subClassInfo = scanResult.getClassInfo(Sub.class.getName()); assertThat(subClassInfo).isNotNull(); @@ -66,7 +66,7 @@ public void withoutIgnoreClassVisibility() { */ @Test public void testExtensionToParent() { - try (ScanResult scanResult = new ClassGraph().whitelistClasses(Sub.class.getName()).ignoreClassVisibility() + try (ScanResult scanResult = new ClassGraph().acceptClasses(Sub.class.getName()).ignoreClassVisibility() .scan()) { final ClassInfo superClassInfo = scanResult.getClassInfo(Super.class.getName()); assertThat(superClassInfo).isNotNull(); @@ -79,8 +79,8 @@ public void testExtensionToParent() { */ @Test public void testExtensionToOuterClass() { - try (ScanResult scanResult = new ClassGraph().whitelistClasses(Super.class.getName()) - .ignoreClassVisibility().scan()) { + try (ScanResult scanResult = new ClassGraph().acceptClasses(Super.class.getName()).ignoreClassVisibility() + .scan()) { final ClassInfo outerClassInfo = scanResult.getClassInfo(Issue345.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().whitelistClasses(Issue345.class.getName()) + try (ScanResult scanResult = new ClassGraph().acceptClasses(Issue345.class.getName()) .ignoreClassVisibility().scan()) { final ClassInfo innerClassInfo = scanResult.getClassInfo(Super.class.getName()); assertThat(innerClassInfo).isNotNull(); @@ -110,7 +110,7 @@ public void testNonExtensionToInnerClass() { public void issue345b() throws Exception { // Find URL of this class' classpath element URL classpathURL; - try (ScanResult scanResult = new ClassGraph().whitelistClasses(Issue345.class.getName()).scan()) { + try (ScanResult scanResult = new ClassGraph().acceptClasses(Issue345.class.getName()).scan()) { classpathURL = scanResult.getClassInfo(Issue345.class.getName()).getClasspathElementURL(); } // Use this to create an override URLClassLoader @@ -148,7 +148,7 @@ public static class C extends B { @Test public void issue345c() { try (ScanResult scanResult = new ClassGraph().enableClassInfo() - .whitelistPackages(Issue345.class.getPackage().getName()).ignoreClassVisibility().scan()) { + .acceptPackages(Issue345.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/Issue348.java index b4e5df412..3083885ef 100644 --- a/src/test/java/io/github/classgraph/issues/issue348/Issue348.java +++ b/src/test/java/io/github/classgraph/issues/issue348/Issue348.java @@ -20,14 +20,14 @@ public class Issue348 { /** Test for wildcarded jars. */ @Test public void testWildcard() { - try (ScanResult scanResult1 = new ClassGraph().whitelistPathsNonRecursive("").scan()) { + try (ScanResult scanResult1 = new ClassGraph().acceptPathsNonRecursive("").scan()) { // Find all resources within classpath elements with ".jar" extension final List jarResourceUris = scanResult1.getResourcesWithExtension("jar").stream() .map(r -> r.getURI().toString()).collect(Collectors.toList()); assertThat(jarResourceUris).isNotEmpty(); try (ScanResult scanResult2 = new ClassGraph().overrideClasspath(jarResourceUris) - .whitelistJars("issue*.jar").scan()) { + .acceptJars("issue*.jar").scan()) { // Find all classpath element URIs for non-nested jars final List cpUris = scanResult2.getClasspathURIs().stream().map(URI::toString) .filter(u -> !u.contains("!")).collect(Collectors.toList()); 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 8e1118699..db21f3aa7 100644 --- a/src/test/java/io/github/classgraph/issues/issue350/Issue350.java +++ b/src/test/java/io/github/classgraph/issues/issue350/Issue350.java @@ -54,14 +54,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().whitelistPackages(Issue350.class.getPackage().getName()) + try (ScanResult scanResult = new ClassGraph().acceptPackages(Issue350.class.getPackage().getName()) .enableClassInfo().enableFieldInfo().enableMethodInfo().enableAnnotationInfo().scan()) { assertThat(scanResult.getClassesWithFieldAnnotation(SuperclassAnnotation.class.getName()).getNames()) .containsOnly(Pub.class.getName(), PubSub.class.getName()); assertThat(scanResult.getClassesWithMethodAnnotation(SuperclassAnnotation.class.getName()).getNames()) .containsOnly(Pub.class.getName(), PubSub.class.getName()); } - try (ScanResult scanResult = new ClassGraph().whitelistPackages(Issue350.class.getPackage().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()) diff --git a/src/test/java/io/github/classgraph/issues/issue352/Issue352.java b/src/test/java/io/github/classgraph/issues/issue352/Issue352.java index 2ddd8d290..87dc963ff 100644 --- a/src/test/java/io/github/classgraph/issues/issue352/Issue352.java +++ b/src/test/java/io/github/classgraph/issues/issue352/Issue352.java @@ -23,24 +23,24 @@ public void test() throws IOException { "istack-commons-runtime", null, null, "3.0.7"); assertThat(resolvedFile).isFile(); - // Test that module-info.class is not included in resource list if the root package ("") is not whitelisted - try (ScanResult scanResult = new ClassGraph().overrideClasspath(resolvedFile) - .whitelistPackagesNonRecursive("").enableClassInfo().scan()) { + // Test that module-info.class is not included in resource list if the root package ("") is not accepted + try (ScanResult scanResult = new ClassGraph().overrideClasspath(resolvedFile).acceptPackagesNonRecursive("") + .enableClassInfo().scan()) { assertThat(scanResult.getAllResources().getPaths()).contains("module-info.class"); } try (ScanResult scanResult = new ClassGraph().overrideClasspath(resolvedFile) - .whitelistPackages("com.sun.istack").enableClassInfo().scan()) { + .acceptPackages("com.sun.istack").enableClassInfo().scan()) { assertThat(scanResult.getAllResources().getPaths()).doesNotContain("module-info.class"); } - // Test that package-info.class is only included in resource list for whitelisted packages + // Test that package-info.class is only included in resource list for accepted packages final String pkgInfoPath = Issue107Test.class.getPackage().getName().replace('.', '/') + "/package-info.class"; - try (ScanResult scanResult = new ClassGraph().whitelistPackages(Issue107Test.class.getPackage().getName()) + try (ScanResult scanResult = new ClassGraph().acceptPackages(Issue107Test.class.getPackage().getName()) .enableClassInfo().scan()) { assertThat(scanResult.getAllResources().getPaths()).contains(pkgInfoPath); } - try (ScanResult scanResult = new ClassGraph().whitelistPackages(Issue352.class.getPackage().getName()) + try (ScanResult scanResult = new ClassGraph().acceptPackages(Issue352.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/Issue355.java index 6717d5a22..1e6ca012d 100644 --- a/src/test/java/io/github/classgraph/issues/issue355/Issue355.java +++ b/src/test/java/io/github/classgraph/issues/issue355/Issue355.java @@ -44,7 +44,7 @@ public void y(final X[] x) { @Test public void test() throws IOException { try (ScanResult scanResult = new ClassGraph() - .whitelistPackagesNonRecursive(Issue355.class.getPackage().getName()).enableClassInfo() + .acceptPackagesNonRecursive(Issue355.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/issue368/Issue368Test.java b/src/test/java/io/github/classgraph/issues/issue368/Issue368Test.java index 4a4f77c43..9359ab131 100644 --- a/src/test/java/io/github/classgraph/issues/issue368/Issue368Test.java +++ b/src/test/java/io/github/classgraph/issues/issue368/Issue368Test.java @@ -55,7 +55,7 @@ public static class InnerClass { */ @Test public void issue368Test() { - try (ScanResult scanResult = new ClassGraph().whitelistPackages(Issue368Test.class.getPackage().getName()) + try (ScanResult scanResult = new ClassGraph().acceptPackages(Issue368Test.class.getPackage().getName()) .enableAllInfo().scan()) { final String json = JSONSerializer.serializeObject(new InnerClass()); assertThat(json) diff --git a/src/test/java/io/github/classgraph/issues/issue37/Issue37Test.java b/src/test/java/io/github/classgraph/issues/issue37/Issue37Test.java index b46fd2190..745734faa 100644 --- a/src/test/java/io/github/classgraph/issues/issue37/Issue37Test.java +++ b/src/test/java/io/github/classgraph/issues/issue37/Issue37Test.java @@ -60,7 +60,7 @@ public Issue37Test() { public void issue37Test() { final List methodNames = new ArrayList<>(); final String pkg = Issue37Test.class.getPackage().getName(); - try (ScanResult scanResult = new ClassGraph().whitelistPackages(pkg) // + try (ScanResult scanResult = new ClassGraph().acceptPackages(pkg) // .enableMethodInfo() // .scan()) { final ClassInfoList classes = scanResult.getAllClasses(); 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 a67654a17..c099dc214 100644 --- a/src/test/java/io/github/classgraph/issues/issue370/Issue370Test.java +++ b/src/test/java/io/github/classgraph/issues/issue370/Issue370Test.java @@ -50,7 +50,7 @@ public class Issue370Test { @Test public void issue370Test() { try (ScanResult scanResult = new ClassGraph().enableAllInfo() - .whitelistPackages(ClassWithAnnotation.class.getPackage().getName()).scan()) { + .acceptPackages(ClassWithAnnotation.class.getPackage().getName()).scan()) { final ClassInfo clazzInfo = scanResult.getClassInfo(ClassWithAnnotation.class.getName()); assertThat(clazzInfo).isNotNull(); for (final MethodInfo methodInfo : clazzInfo.getMethodInfo().filter(MethodInfo::isPublic)) { 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 c3be28ba3..402e3bf22 100644 --- a/src/test/java/io/github/classgraph/issues/issue38/Issue38Test.java +++ b/src/test/java/io/github/classgraph/issues/issue38/Issue38Test.java @@ -27,7 +27,7 @@ public static abstract class AnnotationLiteral implements */ @Test void testImplementsSuppressWarnings() { - try (ScanResult scanResult = new ClassGraph().whitelistPackages(Issue38Test.class.getPackage().getName()) + try (ScanResult scanResult = new ClassGraph().acceptPackages(Issue38Test.class.getPackage().getName()) .scan()) { assertThat(scanResult.getClassesImplementing(SuppressWarnings.class.getName()).getNames()) .containsOnly(ImplementsSuppressWarnings.class.getName()); diff --git a/src/test/java/io/github/classgraph/issues/issue407/Issue407Test.java b/src/test/java/io/github/classgraph/issues/issue407/Issue407Test.java index 190a9c28d..a8cd439d8 100644 --- a/src/test/java/io/github/classgraph/issues/issue407/Issue407Test.java +++ b/src/test/java/io/github/classgraph/issues/issue407/Issue407Test.java @@ -65,7 +65,7 @@ public void issue407Test() throws IOException { // Scan the classpath -- used to throw an exception for Stack, since companion object inherits // from different class try (ScanResult scanResult = new ClassGraph() // - .whitelistPackages("com.google.thirdparty.publicsuffix") // + .acceptPackages("com.google.thirdparty.publicsuffix") // .overrideClassLoaders(classLoader) // .scan()) { final List classNames = scanResult // 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 30c0b90ba..8e53da1eb 100644 --- a/src/test/java/io/github/classgraph/issues/issue420/Issue420Test.java +++ b/src/test/java/io/github/classgraph/issues/issue420/Issue420Test.java @@ -70,7 +70,7 @@ public void testScanningFileBackedByFileSystem() throws IOException, URISyntaxEx try (URLClassLoader childClassLoader = new URLClassLoader(new URL[] { memFsCopyOfJarURL }, getClass().getClassLoader())) { final ClassGraph classGraph = new ClassGraph().enableURLScheme(memFsCopyOfJarURL.getProtocol()) - .overrideClassLoaders(childClassLoader).ignoreParentClassLoaders().whitelistPackages("mrj") + .overrideClassLoaders(childClassLoader).ignoreParentClassLoaders().acceptPackages("mrj") .enableAllInfo(); try (ScanResult scanResult = classGraph.scan()) { assertThat(scanResult.getClassInfo("mrj.Cls")).isNotNull(); @@ -108,7 +108,7 @@ private void testDir(final String packageRootPrefix) throws IOException, URISynt getClass().getClassLoader())) { final ClassGraph classGraph = new ClassGraph().enableURLScheme(memFsRootURL.getProtocol()) .overrideClassLoaders(childClassLoader).ignoreParentClassLoaders() - .whitelistPackages(packageName).enableAllInfo(); + .acceptPackages(packageName).enableAllInfo(); try (ScanResult scanResult = classGraph.scan()) { assertThat(scanResult.getClassInfo(classFullyQualifiedName)).isNotNull(); } diff --git a/src/test/java/io/github/classgraph/issues/issue431/Issue431Test.java b/src/test/java/io/github/classgraph/issues/issue431/Issue431Test.java index 47359e314..49b405052 100644 --- a/src/test/java/io/github/classgraph/issues/issue431/Issue431Test.java +++ b/src/test/java/io/github/classgraph/issues/issue431/Issue431Test.java @@ -82,7 +82,7 @@ private void testFieldEquality(final String fieldName, final ClassInfo classInfo /** Test serializing and deserializing primitive types. */ @Test public void primitiveTypeSerialization() { - final ClassGraph classGraph = new ClassGraph().whitelistPackages(Issue431Test.class.getPackage().getName()) + final ClassGraph classGraph = new ClassGraph().acceptPackages(Issue431Test.class.getPackage().getName()) .enableAllInfo(); try (ScanResult scanResult1 = classGraph.scan()) { final ClassInfo classInfo1 = scanResult1.getClassInfo(X.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 65f14e5e1..77be1fa95 100644 --- a/src/test/java/io/github/classgraph/issues/issue74/Issue74Test.java +++ b/src/test/java/io/github/classgraph/issues/issue74/Issue74Test.java @@ -40,7 +40,7 @@ public class ImplementsFunction implements Function { */ @Test public void issue74() { - try (ScanResult scanResult = new ClassGraph().whitelistPackages(Issue74Test.class.getPackage().getName()) + try (ScanResult scanResult = new ClassGraph().acceptPackages(Issue74Test.class.getPackage().getName()) .scan()) { assertThat(scanResult.getClassesImplementing(Function.class.getName()).getNames()).containsOnly( FunctionAdapter.class.getName(), ImplementsFunction.class.getName(), diff --git a/src/test/java/io/github/classgraph/issues/issue78/Issue78Test.java b/src/test/java/io/github/classgraph/issues/issue78/Issue78Test.java index 3b3bd2e75..0ce505995 100644 --- a/src/test/java/io/github/classgraph/issues/issue78/Issue78Test.java +++ b/src/test/java/io/github/classgraph/issues/issue78/Issue78Test.java @@ -16,7 +16,7 @@ public class Issue78Test { */ @Test public void issue78() { - try (ScanResult scanResult = new ClassGraph().whitelistClasses(Issue78Test.class.getName()).scan()) { + try (ScanResult scanResult = new ClassGraph().acceptClasses(Issue78Test.class.getName()).scan()) { assertThat(scanResult.getAllClasses().getNames()).containsOnly(Issue78Test.class.getName()); } } diff --git a/src/test/java/io/github/classgraph/issues/issue83/Issue83Test.java b/src/test/java/io/github/classgraph/issues/issue83/Issue83Test.java index 73e284341..4db37dcd3 100644 --- a/src/test/java/io/github/classgraph/issues/issue83/Issue83Test.java +++ b/src/test/java/io/github/classgraph/issues/issue83/Issue83Test.java @@ -21,14 +21,14 @@ public class Issue83Test { private static final URL jarPathURL = Issue83Test.class.getClassLoader().getResource("nested-jars-level1.zip"); /** - * Jar whitelist. + * Jar accept. */ @Test - public void jarWhitelist() { + public void jarAccept() { assertThat(jarPathURL).isNotNull(); final List paths = new ArrayList<>(); try (ScanResult scanResult = new ClassGraph().overrideClasspath(jarPathURL) - .whitelistJars("nested-jars-level1.zip").scan()) { + .acceptJars("nested-jars-level1.zip").scan()) { final ResourceList resources = scanResult.getAllResources(); for (final Resource res : resources) { paths.add(res.getPath()); @@ -38,14 +38,14 @@ public void jarWhitelist() { } /** - * Jar blacklist. + * Jar reject. */ @Test - public void jarBlacklist() { + public void jarReject() { assertThat(jarPathURL).isNotNull(); final ArrayList paths = new ArrayList<>(); try (ScanResult scanResult = new ClassGraph().overrideClasspath(jarPathURL) - .blacklistJars("nested-jars-level1.zip").scan()) { + .rejectJars("nested-jars-level1.zip").scan()) { final ResourceList resources = scanResult.getAllResources(); for (final Resource res : resources) { paths.add(res.getPath()); 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 8996946d9..54ae8b607 100644 --- a/src/test/java/io/github/classgraph/issues/issue93/Issue93.java +++ b/src/test/java/io/github/classgraph/issues/issue93/Issue93.java @@ -48,7 +48,7 @@ static class RetentionRuntimeAnnotated { /** Test that both CLASS-retained and RUNTIME-retained annotations are visible by default. */ @Test public void classRetentionIsDefault() { - try (ScanResult scanResult = new ClassGraph().whitelistPackages(PKG).enableAnnotationInfo() + try (ScanResult scanResult = new ClassGraph().acceptPackages(PKG).enableAnnotationInfo() .ignoreClassVisibility().scan()) { assertThat(scanResult.getClassesWithAnnotation(RetentionClass.class.getName()).getNames()) .containsOnly(RetentionClassAnnotated.class.getName()); @@ -63,7 +63,7 @@ public void classRetentionIsDefault() { */ @Test public void classRetentionIsNotVisibleWithRetentionPolicyRUNTIME() { - try (ScanResult scanResult = new ClassGraph().whitelistPackages(PKG).enableAnnotationInfo() + 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()) diff --git a/src/test/java/io/github/classgraph/issues/issue99/Issue99Test.java b/src/test/java/io/github/classgraph/issues/issue99/Issue99Test.java index ec1b83d41..22b73516e 100644 --- a/src/test/java/io/github/classgraph/issues/issue99/Issue99Test.java +++ b/src/test/java/io/github/classgraph/issues/issue99/Issue99Test.java @@ -44,21 +44,21 @@ public class Issue99Test { .getPath() + "!level2.jar!level3.jar!classpath1/classpath2"; /** - * Test without blacklist. + * Test without reject. */ @Test - public void testWithoutBlacklist() { + public void testWithoutReject() { try (ScanResult scanResult = new ClassGraph().overrideClasspath(jarPath).enableClassInfo().scan()) { assertThat(scanResult.getAllClasses().getNames()).containsOnly("com.test.Test"); } } /** - * Test with blacklist. + * Test with reject. */ @Test - public void testWithBlacklist() { - try (ScanResult scanResult = new ClassGraph().overrideClasspath(jarPath).blacklistJars("level3.jar") + public void testWithReject() { + try (ScanResult scanResult = new ClassGraph().overrideClasspath(jarPath).rejectJars("level3.jar") .enableClassInfo().scan()) { assertThat(scanResult.getAllClasses().getNames()).isEmpty(); } diff --git a/src/test/java/io/github/classgraph/json/JSONSerializationTest.java b/src/test/java/io/github/classgraph/json/JSONSerializationTest.java index f92f27576..0558e6957 100644 --- a/src/test/java/io/github/classgraph/json/JSONSerializationTest.java +++ b/src/test/java/io/github/classgraph/json/JSONSerializationTest.java @@ -282,7 +282,7 @@ public void testSerializeThenDeserializeScanResult() { final String classpathBase = classfileURL.substring(0, classfileURL.length() - (JSONSerializationTest.class.getName().length() + 6)); try (ScanResult scanResult = new ClassGraph().overrideClasspath(classpathBase) - .whitelistPackagesNonRecursive(JSONSerializationTest.class.getPackage().getName()) + .acceptPackagesNonRecursive(JSONSerializationTest.class.getPackage().getName()) .ignoreClassVisibility().scan()) { final int indent = 2; final String scanResultJSON = scanResult.toJSON(indent); diff --git a/src/test/java/io/github/classgraph/test/ClassGraphTest.java b/src/test/java/io/github/classgraph/test/ClassGraphTest.java index 2d524eaa9..1a4ab2170 100644 --- a/src/test/java/io/github/classgraph/test/ClassGraphTest.java +++ b/src/test/java/io/github/classgraph/test/ClassGraphTest.java @@ -42,26 +42,26 @@ import io.github.classgraph.Resource; import io.github.classgraph.ResourceList.ByteArrayConsumerThrowsIOException; import io.github.classgraph.ScanResult; -import io.github.classgraph.test.blacklisted.BlacklistedAnnotation; -import io.github.classgraph.test.blacklisted.BlacklistedSubclass; -import io.github.classgraph.test.blacklisted.BlacklistedSubinterface; -import io.github.classgraph.test.blacklisted.BlacklistedSuperclass; -import io.github.classgraph.test.whitelisted.Cls; -import io.github.classgraph.test.whitelisted.ClsSub; -import io.github.classgraph.test.whitelisted.ClsSubSub; -import io.github.classgraph.test.whitelisted.Iface; -import io.github.classgraph.test.whitelisted.IfaceSub; -import io.github.classgraph.test.whitelisted.IfaceSubSub; -import io.github.classgraph.test.whitelisted.Impl1; -import io.github.classgraph.test.whitelisted.Impl1Sub; -import io.github.classgraph.test.whitelisted.Impl1SubSub; -import io.github.classgraph.test.whitelisted.Impl2; -import io.github.classgraph.test.whitelisted.Impl2Sub; -import io.github.classgraph.test.whitelisted.Impl2SubSub; -import io.github.classgraph.test.whitelisted.StaticField; -import io.github.classgraph.test.whitelisted.Whitelisted; -import io.github.classgraph.test.whitelisted.WhitelistedInterface; -import io.github.classgraph.test.whitelisted.blacklistedsub.BlacklistedSub; +import io.github.classgraph.test.accepted.Accepted; +import io.github.classgraph.test.accepted.AcceptedInterface; +import io.github.classgraph.test.accepted.Cls; +import io.github.classgraph.test.accepted.ClsSub; +import io.github.classgraph.test.accepted.ClsSubSub; +import io.github.classgraph.test.accepted.Iface; +import io.github.classgraph.test.accepted.IfaceSub; +import io.github.classgraph.test.accepted.IfaceSubSub; +import io.github.classgraph.test.accepted.Impl1; +import io.github.classgraph.test.accepted.Impl1Sub; +import io.github.classgraph.test.accepted.Impl1SubSub; +import io.github.classgraph.test.accepted.Impl2; +import io.github.classgraph.test.accepted.Impl2Sub; +import io.github.classgraph.test.accepted.Impl2SubSub; +import io.github.classgraph.test.accepted.StaticField; +import io.github.classgraph.test.accepted.rejectedsub.RejectedSub; +import io.github.classgraph.test.rejected.RejectedAnnotation; +import io.github.classgraph.test.rejected.RejectedSubclass; +import io.github.classgraph.test.rejected.RejectedSubinterface; +import io.github.classgraph.test.rejected.RejectedSuperclass; /** * ClassGraphTest. @@ -70,8 +70,8 @@ public class ClassGraphTest { /** The Constant ROOT_PACKAGE. */ private static final String ROOT_PACKAGE = ClassGraphTest.class.getPackage().getName(); - /** The Constant WHITELIST_PACKAGE. */ - private static final String WHITELIST_PACKAGE = Whitelisted.class.getPackage().getName(); + /** The Constant ACCEPT_PACKAGE. */ + private static final String ACCEPT_PACKAGE = Accepted.class.getPackage().getName(); /** * Scan. @@ -84,38 +84,38 @@ public void scan() { assertThat(allClasses).contains(ClassGraph.class.getName()); assertThat(allClasses).contains(ClassGraphTest.class.getName()); assertThat(allClasses).doesNotContain(String.class.getName()); - assertThat(allClasses).contains(BlacklistedSub.class.getName()); + assertThat(allClasses).contains(RejectedSub.class.getName()); } } /** - * Scan with whitelist. + * Scan with accept. */ @Test - public void scanWithWhitelist() { - try (ScanResult scanResult = new ClassGraph().whitelistPackages(WHITELIST_PACKAGE).scan()) { + public void scanWithAccept() { + try (ScanResult scanResult = new ClassGraph().acceptPackages(ACCEPT_PACKAGE).scan()) { final List allClasses = scanResult.getAllClasses().getNames(); assertThat(allClasses).contains(Cls.class.getName()); assertThat(allClasses).doesNotContain(ClassGraph.class.getName()); assertThat(allClasses).doesNotContain(ClassGraphTest.class.getName()); assertThat(allClasses).doesNotContain(String.class.getName()); - assertThat(allClasses).contains(BlacklistedSub.class.getName()); + assertThat(allClasses).contains(RejectedSub.class.getName()); } } /** - * Scan with whitelist and blacklist. + * Scan with accept and reject. */ @Test - public void scanWithWhitelistAndBlacklist() { - try (ScanResult scanResult = new ClassGraph().whitelistPackages(WHITELIST_PACKAGE) - .blacklistPackages(BlacklistedSub.class.getPackage().getName()).scan()) { + public void scanWithAcceptAndReject() { + try (ScanResult scanResult = new ClassGraph().acceptPackages(ACCEPT_PACKAGE) + .rejectPackages(RejectedSub.class.getPackage().getName()).scan()) { final List allClasses = scanResult.getAllClasses().getNames(); assertThat(allClasses).contains(Cls.class.getName()); assertThat(allClasses).doesNotContain(ClassGraph.class.getName()); assertThat(allClasses).doesNotContain(ClassGraphTest.class.getName()); assertThat(allClasses).doesNotContain(String.class.getName()); - assertThat(allClasses).doesNotContain(BlacklistedSub.class.getName()); + assertThat(allClasses).doesNotContain(RejectedSub.class.getName()); } } @@ -124,7 +124,7 @@ public void scanWithWhitelistAndBlacklist() { */ @Test public void scanSubAndSuperclasses() { - try (ScanResult scanResult = new ClassGraph().whitelistPackages(WHITELIST_PACKAGE).scan()) { + try (ScanResult scanResult = new ClassGraph().acceptPackages(ACCEPT_PACKAGE).scan()) { final List subclasses = scanResult.getSubclasses(Cls.class.getName()).getNames(); assertThat(subclasses).doesNotContain(Cls.class.getName()); assertThat(subclasses).contains(ClsSub.class.getName()); @@ -141,7 +141,7 @@ public void scanSubAndSuperclasses() { */ @Test public void scanSubAndSuperinterface() { - try (ScanResult scanResult = new ClassGraph().whitelistPackages(WHITELIST_PACKAGE).scan()) { + try (ScanResult scanResult = new ClassGraph().acceptPackages(ACCEPT_PACKAGE).scan()) { final List subinterfaces = scanResult.getClassesImplementing(Iface.class.getName()).getNames(); assertThat(subinterfaces).doesNotContain(Iface.class.getName()); assertThat(subinterfaces).contains(IfaceSub.class.getName()); @@ -158,7 +158,7 @@ public void scanSubAndSuperinterface() { */ @Test public void scanTransitiveImplements() { - try (ScanResult scanResult = new ClassGraph().whitelistPackages(WHITELIST_PACKAGE).scan()) { + try (ScanResult scanResult = new ClassGraph().acceptPackages(ACCEPT_PACKAGE).scan()) { assertThat(scanResult.getClassesImplementing(Iface.class.getName()).getNames()) .doesNotContain(Iface.class.getName()); assertThat(scanResult.getClassesImplementing(IfaceSubSub.class.getName()).getNames()) @@ -209,79 +209,74 @@ public void scanTransitiveImplements() { */ @Test public void testExternalSuperclassReturned() { - try (ScanResult scanResult = new ClassGraph().whitelistPackages(WHITELIST_PACKAGE).scan()) { - assertThat(scanResult.getSuperclasses(Whitelisted.class.getName()).getNames()) - .containsExactly(BlacklistedSuperclass.class.getName()); - assertThat(scanResult.getSubclasses(Whitelisted.class.getName()).getNames()).isEmpty(); - assertThat(scanResult.getClassesImplementing(WhitelistedInterface.class.getName()).getNames()) - .isEmpty(); - assertThat(scanResult.getClassesImplementing(WhitelistedInterface.class.getName()).getNames()) - .isEmpty(); + 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(); } } /** - * Test whitelisted without exception without strict whitelist. + * Test accepted without exception without strict accept. */ @Test - public void testWhitelistedWithoutExceptionWithoutStrictWhitelist() { - try (ScanResult scanResult = new ClassGraph().whitelistPackages(WHITELIST_PACKAGE).enableExternalClasses() + public void testAcceptedWithoutExceptionWithoutStrictAccept() { + try (ScanResult scanResult = new ClassGraph().acceptPackages(ACCEPT_PACKAGE).enableExternalClasses() .scan()) { - assertThat(scanResult.getSuperclasses(Whitelisted.class.getName()).getNames()) - .containsExactly(BlacklistedSuperclass.class.getName()); + assertThat(scanResult.getSuperclasses(Accepted.class.getName()).getNames()) + .containsExactly(RejectedSuperclass.class.getName()); } } /** - * Test can query with blacklisted annotation. + * Test can query with rejected annotation. */ - public void testCanQueryWithBlacklistedAnnotation() { - try (ScanResult scanResult = new ClassGraph().whitelistPackages(WHITELIST_PACKAGE).scan()) { - assertThat(scanResult.getSuperclasses(Whitelisted.class.getName()).getNames()).isEmpty(); - assertThat(scanResult.getClassesWithAnnotation(BlacklistedAnnotation.class.getName()).getNames()) - .containsExactly(Whitelisted.class.getName()); + 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()) + .containsExactly(Accepted.class.getName()); } } /** - * Test blacklisted placeholder not returned. + * Test rejected placeholder not returned. */ @Test - public void testBlacklistedPlaceholderNotReturned() { - try (ScanResult scanResult = new ClassGraph().whitelistPackages(ROOT_PACKAGE) - .blacklistPackages(BlacklistedAnnotation.class.getPackage().getName()).enableAnnotationInfo() - .scan()) { - assertThat(scanResult.getSuperclasses(Whitelisted.class.getName()).getNames()).isEmpty(); - assertThat(scanResult.getSubclasses(Whitelisted.class.getName()).getNames()).isEmpty(); - assertThat(scanResult.getClassesImplementing(WhitelistedInterface.class.getName()).getNames()) - .isEmpty(); - assertThat(scanResult.getClassesImplementing(WhitelistedInterface.class.getName()).getNames()) - .isEmpty(); - assertThat(scanResult.getAnnotationsOnClass(WhitelistedInterface.class.getName()).getNames()).isEmpty(); + 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.getAnnotationsOnClass(AcceptedInterface.class.getName()).getNames()).isEmpty(); } } /** - * Test blacklisted package overrides whitelisted class with whitelisted override returned. + * Test rejected package overrides accepted class with accepted override returned. */ @Test - public void testBlacklistedPackageOverridesWhitelistedClassWithWhitelistedOverrideReturned() { - try (ScanResult scanResult = new ClassGraph().whitelistPackages(ROOT_PACKAGE) - .blacklistPackages(BlacklistedAnnotation.class.getPackage().getName()) - .whitelistClasses(BlacklistedAnnotation.class.getName()).enableAnnotationInfo().scan()) { - assertThat(scanResult.getAnnotationsOnClass(Whitelisted.class.getName()).getNames()).isEmpty(); + public void testRejectedPackageOverridesAcceptedClassWithAcceptedOverrideReturned() { + try (ScanResult scanResult = new ClassGraph().acceptPackages(ROOT_PACKAGE) + .rejectPackages(RejectedAnnotation.class.getPackage().getName()) + .acceptClasses(RejectedAnnotation.class.getName()).enableAnnotationInfo().scan()) { + assertThat(scanResult.getAnnotationsOnClass(Accepted.class.getName()).getNames()).isEmpty(); } } /** - * Test non whitelisted annotation returned without strict whitelist. + * Test non accepted annotation returned without strict accept. */ @Test - public void testNonWhitelistedAnnotationReturnedWithoutStrictWhitelist() { - try (ScanResult scanResult = new ClassGraph().whitelistPackages(WHITELIST_PACKAGE).enableAnnotationInfo() + public void testNonAcceptedAnnotationReturnedWithoutStrictAccept() { + try (ScanResult scanResult = new ClassGraph().acceptPackages(ACCEPT_PACKAGE).enableAnnotationInfo() .enableExternalClasses().scan()) { - assertThat(scanResult.getAnnotationsOnClass(Whitelisted.class.getName()).getNames()) - .containsOnly(BlacklistedAnnotation.class.getName()); + assertThat(scanResult.getAnnotationsOnClass(Accepted.class.getName()).getNames()) + .containsOnly(RejectedAnnotation.class.getName()); } } @@ -290,69 +285,66 @@ public void testNonWhitelistedAnnotationReturnedWithoutStrictWhitelist() { */ @Test public void testExternalAnnotationReturned() { - try (ScanResult scanResult = new ClassGraph().whitelistPackages(WHITELIST_PACKAGE).enableAnnotationInfo() + try (ScanResult scanResult = new ClassGraph().acceptPackages(ACCEPT_PACKAGE).enableAnnotationInfo() .scan()) { - assertThat(scanResult.getAnnotationsOnClass(Whitelisted.class.getName()).getNames()) - .containsExactly(BlacklistedAnnotation.class.getName()); + assertThat(scanResult.getAnnotationsOnClass(Accepted.class.getName()).getNames()) + .containsExactly(RejectedAnnotation.class.getName()); } } /** - * Test blacklisted package. + * Test rejected package. */ - public void testBlacklistedPackage() { + public void testRejectedPackage() { try (ScanResult scanResult = new ClassGraph() - .whitelistPackages(ROOT_PACKAGE, "-" + BlacklistedSuperclass.class.getPackage().getName()).scan()) { - assertThat(scanResult.getSuperclasses(Whitelisted.class.getName()).getNames()).isEmpty(); - assertThat(scanResult.getSubclasses(Whitelisted.class.getName()).getNames()).isEmpty(); - assertThat(scanResult.getClassesImplementing(WhitelistedInterface.class.getName()).getNames()) - .isEmpty(); - assertThat(scanResult.getClassesImplementing(WhitelistedInterface.class.getName()).getNames()) - .isEmpty(); - assertThat(scanResult.getClassesWithAnnotation(BlacklistedAnnotation.class.getName()).getNames()) + .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.getClassesWithAnnotation(RejectedAnnotation.class.getName()).getNames()) .isEmpty(); } } /** - * Test no exception if querying blacklisted. + * Test no exception if querying rejected. */ - public void testNoExceptionIfQueryingBlacklisted() { + public void testNoExceptionIfQueryingRejected() { try (ScanResult scanResult = new ClassGraph() - .whitelistPackages(WHITELIST_PACKAGE, "-" + BlacklistedSuperclass.class.getPackage().getName()) - .scan()) { - assertThat(scanResult.getSuperclasses(BlacklistedSuperclass.class.getName()).getNames()).isEmpty(); + .acceptPackages(ACCEPT_PACKAGE, "-" + RejectedSuperclass.class.getPackage().getName()).scan()) { + assertThat(scanResult.getSuperclasses(RejectedSuperclass.class.getName()).getNames()).isEmpty(); } } /** - * Test no exception if explicitly whitelisted class in blacklisted package. + * Test no exception if explicitly accepted class in rejected package. */ - public void testNoExceptionIfExplicitlyWhitelistedClassInBlacklistedPackage() { - try (ScanResult scanResult = new ClassGraph().whitelistPackages(WHITELIST_PACKAGE, - "-" + BlacklistedSuperclass.class.getPackage().getName() + BlacklistedSuperclass.class.getName()) + public void testNoExceptionIfExplicitlyAcceptedClassInRejectedPackage() { + try (ScanResult scanResult = new ClassGraph() + .acceptPackages(ACCEPT_PACKAGE, + "-" + RejectedSuperclass.class.getPackage().getName() + RejectedSuperclass.class.getName()) .scan()) { - assertThat(scanResult.getSuperclasses(BlacklistedSuperclass.class.getName()).getNames()).isEmpty(); + assertThat(scanResult.getSuperclasses(RejectedSuperclass.class.getName()).getNames()).isEmpty(); } } /** - * Test visible if not blacklisted. + * Test visible if not rejected. */ @Test - public void testVisibleIfNotBlacklisted() { - try (ScanResult scanResult = new ClassGraph().whitelistPackages(ROOT_PACKAGE).enableAnnotationInfo() - .scan()) { - assertThat(scanResult.getSuperclasses(Whitelisted.class.getName()).getNames()) - .containsExactly(BlacklistedSuperclass.class.getName()); - assertThat(scanResult.getSubclasses(Whitelisted.class.getName()).getNames()) - .containsExactly(BlacklistedSubclass.class.getName()); - assertThat(scanResult.getClassesImplementing(WhitelistedInterface.class.getName()).getNames()) - .containsExactly(BlacklistedSubinterface.class.getName()); - assertThat(scanResult.getClassesImplementing(WhitelistedInterface.class.getName()).getNames()) - .containsExactly(BlacklistedSubinterface.class.getName()); - assertThat(scanResult.getClassesWithAnnotation(BlacklistedAnnotation.class.getName()).getNames()) - .containsExactly(Whitelisted.class.getName()); + 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()) + .containsExactly(RejectedSubclass.class.getName()); + assertThat(scanResult.getClassesImplementing(AcceptedInterface.class.getName()).getNames()) + .containsExactly(RejectedSubinterface.class.getName()); + assertThat(scanResult.getClassesImplementing(AcceptedInterface.class.getName()).getNames()) + .containsExactly(RejectedSubinterface.class.getName()); + assertThat(scanResult.getClassesWithAnnotation(RejectedAnnotation.class.getName()).getNames()) + .containsExactly(Accepted.class.getName()); } } @@ -362,7 +354,7 @@ public void testVisibleIfNotBlacklisted() { @Test public void scanFilePattern() { final AtomicBoolean readFileContents = new AtomicBoolean(false); - try (ScanResult scanResult = new ClassGraph().whitelistPathsNonRecursive("").scan()) { + try (ScanResult scanResult = new ClassGraph().acceptPathsNonRecursive("").scan()) { try { scanResult.getResourcesWithLeafName("file-content-test.txt") .forEachByteArrayThrowingIOException(new ByteArrayConsumerThrowsIOException() { @@ -383,7 +375,7 @@ public void accept(final Resource resource, final byte[] byteArray) throws IOExc */ @Test public void scanStaticFinalFieldName() { - try (ScanResult scanResult = new ClassGraph().whitelistPackages(WHITELIST_PACKAGE) + try (ScanResult scanResult = new ClassGraph().acceptPackages(ACCEPT_PACKAGE) .enableStaticFinalFieldConstantInitializerValues().scan()) { int numInitializers = 0; for (final FieldInfo fieldInfo : scanResult.getClassInfo(StaticField.class.getName()).getFieldInfo()) { @@ -408,7 +400,7 @@ public void scanStaticFinalFieldNameIgnoreVisibility() throws Exception { "integerField", "booleanField" }) { fieldNames.add(StaticField.class.getName() + "." + fieldName); } - try (ScanResult scanResult = new ClassGraph().whitelistPackages(WHITELIST_PACKAGE) + try (ScanResult scanResult = new ClassGraph().acceptPackages(ACCEPT_PACKAGE) .enableStaticFinalFieldConstantInitializerValues().ignoreFieldVisibility().scan()) { int numInitializers = 0; for (final FieldInfo fieldInfo : scanResult.getClassInfo(StaticField.class.getName()).getFieldInfo()) { @@ -445,7 +437,7 @@ public void scanStaticFinalFieldNameIgnoreVisibility() throws Exception { */ @Test public void generateGraphVizFile() { - try (ScanResult scanResult = new ClassGraph().whitelistPackages(ROOT_PACKAGE).enableAllInfo().scan()) { + try (ScanResult scanResult = new ClassGraph().acceptPackages(ROOT_PACKAGE).enableAllInfo().scan()) { final String dotFile = scanResult.getAllClasses().generateGraphVizDotFile(20, 20); assertThat(dotFile).contains("\"" + ClsSub.class.getName() + "\" -> \"" + Cls.class.getName() + "\""); } @@ -456,7 +448,7 @@ public void generateGraphVizFile() { */ @Test public void testGetClasspathElements() { - assertThat(new ClassGraph().whitelistPackages(ROOT_PACKAGE).enableAllInfo().getClasspathFiles().size()) + assertThat(new ClassGraph().acceptPackages(ROOT_PACKAGE).enableAllInfo().getClasspathFiles().size()) .isGreaterThan(0); } @@ -466,7 +458,7 @@ public void testGetClasspathElements() { @Test public void testGetManifest() { final AtomicBoolean foundManifest = new AtomicBoolean(); - try (ScanResult scanResult = new ClassGraph().whitelistPaths("META-INF").enableAllInfo().scan()) { + try (ScanResult scanResult = new ClassGraph().acceptPaths("META-INF").enableAllInfo().scan()) { for (@SuppressWarnings("unused") final Resource res : scanResult.getResourcesWithLeafName("MANIFEST.MF")) { foundManifest.set(true); diff --git a/src/test/java/io/github/classgraph/test/ClassInfoTest.java b/src/test/java/io/github/classgraph/test/ClassInfoTest.java index be84df9df..52ddc8a6a 100644 --- a/src/test/java/io/github/classgraph/test/ClassInfoTest.java +++ b/src/test/java/io/github/classgraph/test/ClassInfoTest.java @@ -12,17 +12,17 @@ import io.github.classgraph.ClassInfo; import io.github.classgraph.ClassInfoList.ClassInfoFilter; import io.github.classgraph.ScanResult; -import io.github.classgraph.test.whitelisted.ClsSub; -import io.github.classgraph.test.whitelisted.ClsSubSub; -import io.github.classgraph.test.whitelisted.Iface; -import io.github.classgraph.test.whitelisted.IfaceSub; -import io.github.classgraph.test.whitelisted.IfaceSubSub; -import io.github.classgraph.test.whitelisted.Impl1; -import io.github.classgraph.test.whitelisted.Impl1Sub; -import io.github.classgraph.test.whitelisted.Impl1SubSub; -import io.github.classgraph.test.whitelisted.Impl2; -import io.github.classgraph.test.whitelisted.Impl2Sub; -import io.github.classgraph.test.whitelisted.Impl2SubSub; +import io.github.classgraph.test.accepted.ClsSub; +import io.github.classgraph.test.accepted.ClsSubSub; +import io.github.classgraph.test.accepted.Iface; +import io.github.classgraph.test.accepted.IfaceSub; +import io.github.classgraph.test.accepted.IfaceSubSub; +import io.github.classgraph.test.accepted.Impl1; +import io.github.classgraph.test.accepted.Impl1Sub; +import io.github.classgraph.test.accepted.Impl1SubSub; +import io.github.classgraph.test.accepted.Impl2; +import io.github.classgraph.test.accepted.Impl2Sub; +import io.github.classgraph.test.accepted.Impl2SubSub; /** * ClassInfoTest. @@ -36,7 +36,7 @@ public class ClassInfoTest { */ @BeforeAll public static void setup() { - scanResult = new ClassGraph().whitelistPackages(Impl1.class.getPackage().getName()).scan(); + scanResult = new ClassGraph().acceptPackages(Impl1.class.getPackage().getName()).scan(); } /** diff --git a/src/test/java/io/github/classgraph/test/accepted/Accepted.java b/src/test/java/io/github/classgraph/test/accepted/Accepted.java new file mode 100644 index 000000000..02acd5869 --- /dev/null +++ b/src/test/java/io/github/classgraph/test/accepted/Accepted.java @@ -0,0 +1,12 @@ +package io.github.classgraph.test.accepted; + +import io.github.classgraph.test.rejected.RejectedAnnotation; +import io.github.classgraph.test.rejected.RejectedInterface; +import io.github.classgraph.test.rejected.RejectedSuperclass; + +/** + * Accepted. + */ +@RejectedAnnotation +public class Accepted extends RejectedSuperclass implements RejectedInterface { +} diff --git a/src/test/java/io/github/classgraph/test/accepted/AcceptedInterface.java b/src/test/java/io/github/classgraph/test/accepted/AcceptedInterface.java new file mode 100644 index 000000000..585c92416 --- /dev/null +++ b/src/test/java/io/github/classgraph/test/accepted/AcceptedInterface.java @@ -0,0 +1,9 @@ +package io.github.classgraph.test.accepted; + +import io.github.classgraph.test.rejected.RejectedInterface; + +/** + * The Interface AcceptedInterface. + */ +public interface AcceptedInterface extends RejectedInterface { +} diff --git a/src/test/java/io/github/classgraph/test/accepted/Cls.java b/src/test/java/io/github/classgraph/test/accepted/Cls.java new file mode 100644 index 000000000..0c07551e2 --- /dev/null +++ b/src/test/java/io/github/classgraph/test/accepted/Cls.java @@ -0,0 +1,7 @@ +package io.github.classgraph.test.accepted; + +/** + * Cls. + */ +public class Cls { +} diff --git a/src/test/java/io/github/classgraph/test/whitelisted/ClsSub.java b/src/test/java/io/github/classgraph/test/accepted/ClsSub.java similarity index 54% rename from src/test/java/io/github/classgraph/test/whitelisted/ClsSub.java rename to src/test/java/io/github/classgraph/test/accepted/ClsSub.java index 2029358a0..a0d2c144a 100644 --- a/src/test/java/io/github/classgraph/test/whitelisted/ClsSub.java +++ b/src/test/java/io/github/classgraph/test/accepted/ClsSub.java @@ -1,4 +1,4 @@ -package io.github.classgraph.test.whitelisted; +package io.github.classgraph.test.accepted; /** * ClsSub. diff --git a/src/test/java/io/github/classgraph/test/whitelisted/ClsSubSub.java b/src/test/java/io/github/classgraph/test/accepted/ClsSubSub.java similarity index 58% rename from src/test/java/io/github/classgraph/test/whitelisted/ClsSubSub.java rename to src/test/java/io/github/classgraph/test/accepted/ClsSubSub.java index 58311355b..048486c3e 100644 --- a/src/test/java/io/github/classgraph/test/whitelisted/ClsSubSub.java +++ b/src/test/java/io/github/classgraph/test/accepted/ClsSubSub.java @@ -1,4 +1,4 @@ -package io.github.classgraph.test.whitelisted; +package io.github.classgraph.test.accepted; /** * ClsSubSub. diff --git a/src/test/java/io/github/classgraph/test/whitelisted/HasFieldWithTypeCls.java b/src/test/java/io/github/classgraph/test/accepted/HasFieldWithTypeCls.java similarity index 96% rename from src/test/java/io/github/classgraph/test/whitelisted/HasFieldWithTypeCls.java rename to src/test/java/io/github/classgraph/test/accepted/HasFieldWithTypeCls.java index 5c2ccc565..bd22e0962 100644 --- a/src/test/java/io/github/classgraph/test/whitelisted/HasFieldWithTypeCls.java +++ b/src/test/java/io/github/classgraph/test/accepted/HasFieldWithTypeCls.java @@ -1,4 +1,4 @@ -package io.github.classgraph.test.whitelisted; +package io.github.classgraph.test.accepted; import java.util.ArrayList; import java.util.HashMap; diff --git a/src/test/java/io/github/classgraph/test/whitelisted/Iface.java b/src/test/java/io/github/classgraph/test/accepted/Iface.java similarity index 56% rename from src/test/java/io/github/classgraph/test/whitelisted/Iface.java rename to src/test/java/io/github/classgraph/test/accepted/Iface.java index cb3c868ad..1de272751 100644 --- a/src/test/java/io/github/classgraph/test/whitelisted/Iface.java +++ b/src/test/java/io/github/classgraph/test/accepted/Iface.java @@ -1,4 +1,4 @@ -package io.github.classgraph.test.whitelisted; +package io.github.classgraph.test.accepted; /** * The Interface Iface. diff --git a/src/test/java/io/github/classgraph/test/whitelisted/IfaceSub.java b/src/test/java/io/github/classgraph/test/accepted/IfaceSub.java similarity index 62% rename from src/test/java/io/github/classgraph/test/whitelisted/IfaceSub.java rename to src/test/java/io/github/classgraph/test/accepted/IfaceSub.java index 215de7749..d151e353a 100644 --- a/src/test/java/io/github/classgraph/test/whitelisted/IfaceSub.java +++ b/src/test/java/io/github/classgraph/test/accepted/IfaceSub.java @@ -1,4 +1,4 @@ -package io.github.classgraph.test.whitelisted; +package io.github.classgraph.test.accepted; /** * The Interface IfaceSub. diff --git a/src/test/java/io/github/classgraph/test/whitelisted/IfaceSubSub.java b/src/test/java/io/github/classgraph/test/accepted/IfaceSubSub.java similarity index 65% rename from src/test/java/io/github/classgraph/test/whitelisted/IfaceSubSub.java rename to src/test/java/io/github/classgraph/test/accepted/IfaceSubSub.java index a9be20e36..7a73a1767 100644 --- a/src/test/java/io/github/classgraph/test/whitelisted/IfaceSubSub.java +++ b/src/test/java/io/github/classgraph/test/accepted/IfaceSubSub.java @@ -1,4 +1,4 @@ -package io.github.classgraph.test.whitelisted; +package io.github.classgraph.test.accepted; /** * The Interface IfaceSubSub. diff --git a/src/test/java/io/github/classgraph/test/whitelisted/Impl1.java b/src/test/java/io/github/classgraph/test/accepted/Impl1.java similarity index 58% rename from src/test/java/io/github/classgraph/test/whitelisted/Impl1.java rename to src/test/java/io/github/classgraph/test/accepted/Impl1.java index f81a6be11..75f6eb53e 100644 --- a/src/test/java/io/github/classgraph/test/whitelisted/Impl1.java +++ b/src/test/java/io/github/classgraph/test/accepted/Impl1.java @@ -1,4 +1,4 @@ -package io.github.classgraph.test.whitelisted; +package io.github.classgraph.test.accepted; /** * Impl1. diff --git a/src/test/java/io/github/classgraph/test/whitelisted/Impl1Sub.java b/src/test/java/io/github/classgraph/test/accepted/Impl1Sub.java similarity index 56% rename from src/test/java/io/github/classgraph/test/whitelisted/Impl1Sub.java rename to src/test/java/io/github/classgraph/test/accepted/Impl1Sub.java index 506623de0..f9aaff8d4 100644 --- a/src/test/java/io/github/classgraph/test/whitelisted/Impl1Sub.java +++ b/src/test/java/io/github/classgraph/test/accepted/Impl1Sub.java @@ -1,4 +1,4 @@ -package io.github.classgraph.test.whitelisted; +package io.github.classgraph.test.accepted; /** * Impl1Sub. diff --git a/src/test/java/io/github/classgraph/test/whitelisted/Impl1SubSub.java b/src/test/java/io/github/classgraph/test/accepted/Impl1SubSub.java similarity index 60% rename from src/test/java/io/github/classgraph/test/whitelisted/Impl1SubSub.java rename to src/test/java/io/github/classgraph/test/accepted/Impl1SubSub.java index 051df6708..633aba1b1 100644 --- a/src/test/java/io/github/classgraph/test/whitelisted/Impl1SubSub.java +++ b/src/test/java/io/github/classgraph/test/accepted/Impl1SubSub.java @@ -1,4 +1,4 @@ -package io.github.classgraph.test.whitelisted; +package io.github.classgraph.test.accepted; /** * Impl1SubSub. diff --git a/src/test/java/io/github/classgraph/test/whitelisted/Impl2.java b/src/test/java/io/github/classgraph/test/accepted/Impl2.java similarity index 55% rename from src/test/java/io/github/classgraph/test/whitelisted/Impl2.java rename to src/test/java/io/github/classgraph/test/accepted/Impl2.java index 591f1f4ee..17b6deacc 100644 --- a/src/test/java/io/github/classgraph/test/whitelisted/Impl2.java +++ b/src/test/java/io/github/classgraph/test/accepted/Impl2.java @@ -1,4 +1,4 @@ -package io.github.classgraph.test.whitelisted; +package io.github.classgraph.test.accepted; /** * Impl2. diff --git a/src/test/java/io/github/classgraph/test/whitelisted/Impl2Sub.java b/src/test/java/io/github/classgraph/test/accepted/Impl2Sub.java similarity index 56% rename from src/test/java/io/github/classgraph/test/whitelisted/Impl2Sub.java rename to src/test/java/io/github/classgraph/test/accepted/Impl2Sub.java index 27b8ad6ec..7c3e0a462 100644 --- a/src/test/java/io/github/classgraph/test/whitelisted/Impl2Sub.java +++ b/src/test/java/io/github/classgraph/test/accepted/Impl2Sub.java @@ -1,4 +1,4 @@ -package io.github.classgraph.test.whitelisted; +package io.github.classgraph.test.accepted; /** * Impl2Sub. diff --git a/src/test/java/io/github/classgraph/test/whitelisted/Impl2SubSub.java b/src/test/java/io/github/classgraph/test/accepted/Impl2SubSub.java similarity index 66% rename from src/test/java/io/github/classgraph/test/whitelisted/Impl2SubSub.java rename to src/test/java/io/github/classgraph/test/accepted/Impl2SubSub.java index 01bdb8547..0727b2ea0 100644 --- a/src/test/java/io/github/classgraph/test/whitelisted/Impl2SubSub.java +++ b/src/test/java/io/github/classgraph/test/accepted/Impl2SubSub.java @@ -1,4 +1,4 @@ -package io.github.classgraph.test.whitelisted; +package io.github.classgraph.test.accepted; /** * Impl2SubSub. diff --git a/src/test/java/io/github/classgraph/test/whitelisted/StaticField.java b/src/test/java/io/github/classgraph/test/accepted/StaticField.java similarity index 93% rename from src/test/java/io/github/classgraph/test/whitelisted/StaticField.java rename to src/test/java/io/github/classgraph/test/accepted/StaticField.java index da900caa0..5dc1d169b 100644 --- a/src/test/java/io/github/classgraph/test/whitelisted/StaticField.java +++ b/src/test/java/io/github/classgraph/test/accepted/StaticField.java @@ -1,4 +1,4 @@ -package io.github.classgraph.test.whitelisted; +package io.github.classgraph.test.accepted; /** * StaticField. diff --git a/src/test/java/io/github/classgraph/test/accepted/rejectedsub/RejectedSub.java b/src/test/java/io/github/classgraph/test/accepted/rejectedsub/RejectedSub.java new file mode 100644 index 000000000..884739ce5 --- /dev/null +++ b/src/test/java/io/github/classgraph/test/accepted/rejectedsub/RejectedSub.java @@ -0,0 +1,7 @@ +package io.github.classgraph.test.accepted.rejectedsub; + +/** + * RejectedSub. + */ +public class RejectedSub { +} diff --git a/src/test/java/io/github/classgraph/test/blacklisted/BlacklistedInterface.java b/src/test/java/io/github/classgraph/test/blacklisted/BlacklistedInterface.java deleted file mode 100644 index 9c4e851e7..000000000 --- a/src/test/java/io/github/classgraph/test/blacklisted/BlacklistedInterface.java +++ /dev/null @@ -1,7 +0,0 @@ -package io.github.classgraph.test.blacklisted; - -/** - * The Interface BlacklistedInterface. - */ -public interface BlacklistedInterface { -} diff --git a/src/test/java/io/github/classgraph/test/blacklisted/BlacklistedSubclass.java b/src/test/java/io/github/classgraph/test/blacklisted/BlacklistedSubclass.java deleted file mode 100644 index b7ec4b3ae..000000000 --- a/src/test/java/io/github/classgraph/test/blacklisted/BlacklistedSubclass.java +++ /dev/null @@ -1,9 +0,0 @@ -package io.github.classgraph.test.blacklisted; - -import io.github.classgraph.test.whitelisted.Whitelisted; - -/** - * BlacklistedSubclass. - */ -public class BlacklistedSubclass extends Whitelisted { -} diff --git a/src/test/java/io/github/classgraph/test/blacklisted/BlacklistedSubinterface.java b/src/test/java/io/github/classgraph/test/blacklisted/BlacklistedSubinterface.java deleted file mode 100644 index 3bc634d86..000000000 --- a/src/test/java/io/github/classgraph/test/blacklisted/BlacklistedSubinterface.java +++ /dev/null @@ -1,9 +0,0 @@ -package io.github.classgraph.test.blacklisted; - -import io.github.classgraph.test.whitelisted.WhitelistedInterface; - -/** - * The Interface BlacklistedSubinterface. - */ -public interface BlacklistedSubinterface extends WhitelistedInterface { -} diff --git a/src/test/java/io/github/classgraph/test/blacklisted/BlacklistedSuperclass.java b/src/test/java/io/github/classgraph/test/blacklisted/BlacklistedSuperclass.java deleted file mode 100644 index afa7556cf..000000000 --- a/src/test/java/io/github/classgraph/test/blacklisted/BlacklistedSuperclass.java +++ /dev/null @@ -1,7 +0,0 @@ -package io.github.classgraph.test.blacklisted; - -/** - * BlacklistedSuperclass. - */ -public class BlacklistedSuperclass { -} 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 8379a03b5..133cfea2c 100644 --- a/src/test/java/io/github/classgraph/test/classrefannotation/AnnotationClassRefTest.java +++ b/src/test/java/io/github/classgraph/test/classrefannotation/AnnotationClassRefTest.java @@ -83,7 +83,7 @@ public void methodWithAnnotation() { @Test public void testClassRefAnnotation() { try (ScanResult scanResult = new ClassGraph() - .whitelistPackages(AnnotationClassRefTest.class.getPackage().getName()).enableMethodInfo() + .acceptPackages(AnnotationClassRefTest.class.getPackage().getName()).enableMethodInfo() .enableAnnotationInfo().scan()) { final ClassInfoList testClasses = scanResult .getClassesWithMethodAnnotation(ClassRefAnnotation.class.getName()); 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 b8c3a7745..360ee5e01 100644 --- a/src/test/java/io/github/classgraph/test/fieldannotation/FieldAndMethodAnnotationTest.java +++ b/src/test/java/io/github/classgraph/test/fieldannotation/FieldAndMethodAnnotationTest.java @@ -58,7 +58,7 @@ public class FieldAndMethodAnnotationTest { @Test public void testGetNamesOfClassesWithFieldAnnotation() { try (ScanResult scanResult = new ClassGraph() - .whitelistPackages(FieldAndMethodAnnotationTest.class.getPackage().getName()).enableFieldInfo() + .acceptPackages(FieldAndMethodAnnotationTest.class.getPackage().getName()).enableFieldInfo() .enableAnnotationInfo().scan()) { final List testClasses = scanResult .getClassesWithFieldAnnotation(ExternalAnnotation.class.getName()).getNames(); @@ -72,7 +72,7 @@ public void testGetNamesOfClassesWithFieldAnnotation() { @Test public void testGetNamesOfClassesWithFieldAnnotationIgnoringVisibility() { try (ScanResult scanResult = new ClassGraph() - .whitelistPackages(FieldAndMethodAnnotationTest.class.getPackage().getName()).enableFieldInfo() + .acceptPackages(FieldAndMethodAnnotationTest.class.getPackage().getName()).enableFieldInfo() .ignoreFieldVisibility().enableAnnotationInfo().scan()) { final List testClasses = scanResult .getClassesWithFieldAnnotation(ExternalAnnotation.class.getName()).getNames(); @@ -87,7 +87,7 @@ public void testGetNamesOfClassesWithFieldAnnotationIgnoringVisibility() { @ExternalAnnotation public void testGetNamesOfClassesWithMethodAnnotation() { try (ScanResult scanResult = new ClassGraph() - .whitelistPackages(FieldAndMethodAnnotationTest.class.getPackage().getName()).enableMethodInfo() + .acceptPackages(FieldAndMethodAnnotationTest.class.getPackage().getName()).enableMethodInfo() .enableAnnotationInfo().scan()) { final List testClasses = scanResult .getClassesWithMethodAnnotation(ExternalAnnotation.class.getName()).getNames(); diff --git a/src/test/java/io/github/classgraph/test/fieldinfo/FieldInfoTest.java b/src/test/java/io/github/classgraph/test/fieldinfo/FieldInfoTest.java index 3910fcaeb..4d4d7e4f2 100644 --- a/src/test/java/io/github/classgraph/test/fieldinfo/FieldInfoTest.java +++ b/src/test/java/io/github/classgraph/test/fieldinfo/FieldInfoTest.java @@ -71,7 +71,7 @@ public class FieldInfoTest { */ @Test public void fieldInfoNotEnabled() { - try (ScanResult scanResult = new ClassGraph().whitelistPackages(FieldInfoTest.class.getPackage().getName()) + try (ScanResult scanResult = new ClassGraph().acceptPackages(FieldInfoTest.class.getPackage().getName()) .scan()) { Assertions.assertThrows(IllegalArgumentException.class, () -> scanResult.getClassInfo(FieldInfoTest.class.getName()).getFieldInfo()); @@ -83,7 +83,7 @@ public void fieldInfoNotEnabled() { */ @Test public void testGetFieldInfo() { - try (ScanResult scanResult = new ClassGraph().whitelistPackages(FieldInfoTest.class.getPackage().getName()) + try (ScanResult scanResult = new ClassGraph().acceptPackages(FieldInfoTest.class.getPackage().getName()) .enableFieldInfo().enableStaticFinalFieldConstantInitializerValues().enableAnnotationInfo() .scan()) { final List fieldInfoStrs = scanResult.getClassInfo(FieldInfoTest.class.getName()).getFieldInfo() @@ -101,7 +101,7 @@ public void testGetFieldInfo() { */ @Test public void testGetFieldInfoIgnoringVisibility() { - try (ScanResult scanResult = new ClassGraph().whitelistPackages(FieldInfoTest.class.getPackage().getName()) + try (ScanResult scanResult = new ClassGraph().acceptPackages(FieldInfoTest.class.getPackage().getName()) .enableFieldInfo().enableStaticFinalFieldConstantInitializerValues().enableAnnotationInfo() .ignoreFieldVisibility().scan()) { final List fieldInfoStrs = scanResult.getClassInfo(FieldInfoTest.class.getName()).getFieldInfo() 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 2e1752505..9ce34401b 100644 --- a/src/test/java/io/github/classgraph/test/internal/InternalExternalTest.java +++ b/src/test/java/io/github/classgraph/test/internal/InternalExternalTest.java @@ -15,11 +15,11 @@ */ public class InternalExternalTest { /** - * Test whitelisting external classes. + * Test accepting external classes. */ @Test - public void testWhitelistingExternalClasses() { - try (ScanResult scanResult = new ClassGraph().whitelistPackages( + public void testAcceptingExternalClasses() { + try (ScanResult scanResult = new ClassGraph().acceptPackages( InternalExternalTest.class.getPackage().getName(), ExternalAnnotation.class.getName()).scan()) { assertThat(scanResult.getAllStandardClasses().getNames()).containsOnly( InternalExternalTest.class.getName(), InternalExtendsExternal.class.getName(), @@ -33,7 +33,7 @@ public void testWhitelistingExternalClasses() { @Test public void testEnableExternalClasses() { try (ScanResult scanResult = new ClassGraph() - .whitelistPackages(InternalExternalTest.class.getPackage().getName(), + .acceptPackages(InternalExternalTest.class.getPackage().getName(), ExternalAnnotation.class.getName()) .enableExternalClasses().scan()) { assertThat(scanResult.getAllStandardClasses().getNames()).containsOnly( @@ -44,12 +44,12 @@ public void testEnableExternalClasses() { } /** - * Test whitelisting external classes without enabling external classes. + * Test accepting external classes without enabling external classes. */ @Test - public void testWhitelistingExternalClassesWithoutEnablingExternalClasses() { + public void testAcceptingExternalClassesWithoutEnablingExternalClasses() { try (ScanResult scanResult = new ClassGraph() - .whitelistPackages(InternalExternalTest.class.getPackage().getName(), + .acceptPackages(InternalExternalTest.class.getPackage().getName(), ExternalAnnotation.class.getName()) .enableAllInfo().scan()) { assertThat(scanResult.getAllStandardClasses().getNames()).containsOnly( @@ -72,7 +72,7 @@ public void testWhitelistingExternalClassesWithoutEnablingExternalClasses() { @Test public void testIncludeReferencedClasses() { try (ScanResult scanResult = new ClassGraph() - .whitelistPackages(InternalExternalTest.class.getPackage().getName()).enableAllInfo().scan()) { + .acceptPackages(InternalExternalTest.class.getPackage().getName()).enableAllInfo().scan()) { assertThat(scanResult.getAllStandardClasses().getNames()) .doesNotContain(ExternalSuperclass.class.getName()); assertThat(scanResult.getSubclasses(ExternalSuperclass.class.getName()).getNames()) 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 c34a05bb5..c7f5bde00 100644 --- a/src/test/java/io/github/classgraph/test/methodannotation/MethodAnnotationTest.java +++ b/src/test/java/io/github/classgraph/test/methodannotation/MethodAnnotationTest.java @@ -51,7 +51,7 @@ public class MethodAnnotationTest { @Test public void testGetNamesOfClassesWithMethodAnnotation() { try (ScanResult scanResult = new ClassGraph() - .whitelistPackages(MethodAnnotationTest.class.getPackage().getName()).enableClassInfo() + .acceptPackages(MethodAnnotationTest.class.getPackage().getName()).enableClassInfo() .enableMethodInfo().enableAnnotationInfo().scan()) { final List testClasses = scanResult .getClassesWithMethodAnnotation(ExternalAnnotation.class.getName()).getNames(); @@ -65,7 +65,7 @@ public void testGetNamesOfClassesWithMethodAnnotation() { @Test public void testGetNamesOfClassesWithMethodAnnotationIgnoringVisibility() { try (ScanResult scanResult = new ClassGraph() - .whitelistPackages(MethodAnnotationTest.class.getPackage().getName()).enableClassInfo() + .acceptPackages(MethodAnnotationTest.class.getPackage().getName()).enableClassInfo() .enableMethodInfo().enableAnnotationInfo().ignoreMethodVisibility().scan()) { final ClassInfoList classesWithMethodAnnotation = scanResult .getClassesWithMethodAnnotation(ExternalAnnotation.class.getName()); 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 cf245d303..2dae751f4 100644 --- a/src/test/java/io/github/classgraph/test/methodannotation2/TestMethodMetaAnnotation.java +++ b/src/test/java/io/github/classgraph/test/methodannotation2/TestMethodMetaAnnotation.java @@ -105,7 +105,7 @@ public void annotatedMethod() { @ExternalAnnotation public void testMetaAnnotation() { try (ScanResult scanResult = new ClassGraph() - .whitelistPackages(TestMethodMetaAnnotation.class.getPackage().getName()).enableAnnotationInfo() + .acceptPackages(TestMethodMetaAnnotation.class.getPackage().getName()).enableAnnotationInfo() .scan()) { final List testClasses = scanResult.getClassesWithAnnotation(MetaAnnotation.class.getName()) .getNames(); @@ -121,7 +121,7 @@ public void testMetaAnnotation() { @ExternalAnnotation public void testMetaAnnotationStandardClassesOnly() { try (ScanResult scanResult = new ClassGraph() - .whitelistPackages(TestMethodMetaAnnotation.class.getPackage().getName()).enableAnnotationInfo() + .acceptPackages(TestMethodMetaAnnotation.class.getPackage().getName()).enableAnnotationInfo() .scan()) { final List testClasses = scanResult.getClassesWithAnnotation(MetaAnnotation.class.getName()) .getStandardClasses().getNames(); @@ -136,7 +136,7 @@ public void testMetaAnnotationStandardClassesOnly() { @ExternalAnnotation public void testMethodMetaAnnotation() { try (ScanResult scanResult = new ClassGraph() - .whitelistPackages(TestMethodMetaAnnotation.class.getPackage().getName()).enableMethodInfo() + .acceptPackages(TestMethodMetaAnnotation.class.getPackage().getName()).enableMethodInfo() .enableAnnotationInfo().scan()) { final List testClasses = scanResult .getClassesWithMethodAnnotation(MetaAnnotation.class.getName()).getNames(); 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 905ef8c53..0b9ebc690 100644 --- a/src/test/java/io/github/classgraph/test/methodinfo/MethodInfoTest.java +++ b/src/test/java/io/github/classgraph/test/methodinfo/MethodInfoTest.java @@ -103,7 +103,7 @@ private static String[] privateMethod() { @Test public void methodInfoNotEnabled() { // .enableSaveMethodInfo() not called - try (ScanResult scanResult = new ClassGraph().whitelistPackages(MethodInfoTest.class.getPackage().getName()) + try (ScanResult scanResult = new ClassGraph().acceptPackages(MethodInfoTest.class.getPackage().getName()) .scan()) { Assertions.assertThrows(IllegalArgumentException.class, () -> scanResult.getClassInfo(MethodInfoTest.class.getName()).getMethodInfo()); @@ -115,7 +115,7 @@ public void methodInfoNotEnabled() { */ @Test public void testGetMethodInfo() { - try (ScanResult scanResult = new ClassGraph().whitelistPackages(MethodInfoTest.class.getPackage().getName()) + try (ScanResult scanResult = new ClassGraph().acceptPackages(MethodInfoTest.class.getPackage().getName()) .enableClassInfo().enableMethodInfo().enableAnnotationInfo().scan()) { assertThat(scanResult.getClassInfo(MethodInfoTest.class.getName()).getMethodInfo() .filter(new MethodInfoFilter() { @@ -144,7 +144,7 @@ public boolean accept(final MethodInfo methodInfo) { */ @Test public void testGetConstructorInfo() { - try (ScanResult scanResult = new ClassGraph().whitelistPackages(MethodInfoTest.class.getPackage().getName()) + try (ScanResult scanResult = new ClassGraph().acceptPackages(MethodInfoTest.class.getPackage().getName()) .enableMethodInfo().scan()) { assertThat(scanResult.getClassInfo(MethodInfoTest.class.getName()).getConstructorInfo().getAsStrings()) .containsOnly("public ()"); @@ -156,7 +156,7 @@ public void testGetConstructorInfo() { */ @Test public void testGetMethodInfoIgnoringVisibility() { - try (ScanResult scanResult = new ClassGraph().whitelistPackages(MethodInfoTest.class.getPackage().getName()) + try (ScanResult scanResult = new ClassGraph().acceptPackages(MethodInfoTest.class.getPackage().getName()) .enableClassInfo().enableMethodInfo().enableAnnotationInfo().ignoreMethodVisibility().scan()) { assertThat(scanResult.getClassInfo(MethodInfoTest.class.getName()).getMethodInfo() .filter(new MethodInfoFilter() { @@ -186,7 +186,7 @@ public boolean accept(final MethodInfo methodInfo) { */ @Test public void testMethodInfoLoadMethodForArrayArg() { - try (ScanResult scanResult = new ClassGraph().whitelistPackages(MethodInfoTest.class.getPackage().getName()) + try (ScanResult scanResult = new ClassGraph().acceptPackages(MethodInfoTest.class.getPackage().getName()) .enableClassInfo().enableMethodInfo().enableAnnotationInfo().scan()) { final MethodInfo mi = scanResult.getClassInfo(MethodInfoTest.class.getName()).getMethodInfo() .getSingleMethod("publicMethodWithArgs"); 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 47efbe33f..53e3399c1 100644 --- a/src/test/java/io/github/classgraph/test/parameterannotation/RetentionPolicyForFunctionParameterAnnotationsTest.java +++ b/src/test/java/io/github/classgraph/test/parameterannotation/RetentionPolicyForFunctionParameterAnnotationsTest.java @@ -89,7 +89,7 @@ public class RetentionPolicyForFunctionParameterAnnotationsTest { @BeforeAll public static void beforeClass() { scanResult = new ClassGraph() - .whitelistPackages(RetentionPolicyForFunctionParameterAnnotationsTest.class.getPackage().getName()) + .acceptPackages(RetentionPolicyForFunctionParameterAnnotationsTest.class.getPackage().getName()) .enableAllInfo().scan(); classInfo = scanResult.getClassInfo(RetentionPolicyForFunctionParameterAnnotationsTest.class.getName()); } diff --git a/src/test/java/io/github/classgraph/test/blacklisted/BlacklistedAnnotation.java b/src/test/java/io/github/classgraph/test/rejected/RejectedAnnotation.java similarity index 64% rename from src/test/java/io/github/classgraph/test/blacklisted/BlacklistedAnnotation.java rename to src/test/java/io/github/classgraph/test/rejected/RejectedAnnotation.java index a1ca11231..8df320ada 100644 --- a/src/test/java/io/github/classgraph/test/blacklisted/BlacklistedAnnotation.java +++ b/src/test/java/io/github/classgraph/test/rejected/RejectedAnnotation.java @@ -1,4 +1,4 @@ -package io.github.classgraph.test.blacklisted; +package io.github.classgraph.test.rejected; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -6,9 +6,9 @@ import java.lang.annotation.Target; /** - * The Interface BlacklistedAnnotation. + * The Interface RejectedAnnotation. */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) -public @interface BlacklistedAnnotation { +public @interface RejectedAnnotation { } diff --git a/src/test/java/io/github/classgraph/test/rejected/RejectedInterface.java b/src/test/java/io/github/classgraph/test/rejected/RejectedInterface.java new file mode 100644 index 000000000..8125f209a --- /dev/null +++ b/src/test/java/io/github/classgraph/test/rejected/RejectedInterface.java @@ -0,0 +1,7 @@ +package io.github.classgraph.test.rejected; + +/** + * The Interface RejectedInterface. + */ +public interface RejectedInterface { +} diff --git a/src/test/java/io/github/classgraph/test/rejected/RejectedSubclass.java b/src/test/java/io/github/classgraph/test/rejected/RejectedSubclass.java new file mode 100644 index 000000000..5cb399109 --- /dev/null +++ b/src/test/java/io/github/classgraph/test/rejected/RejectedSubclass.java @@ -0,0 +1,9 @@ +package io.github.classgraph.test.rejected; + +import io.github.classgraph.test.accepted.Accepted; + +/** + * RejectedSubclass. + */ +public class RejectedSubclass extends Accepted { +} diff --git a/src/test/java/io/github/classgraph/test/rejected/RejectedSubinterface.java b/src/test/java/io/github/classgraph/test/rejected/RejectedSubinterface.java new file mode 100644 index 000000000..d51c5a23e --- /dev/null +++ b/src/test/java/io/github/classgraph/test/rejected/RejectedSubinterface.java @@ -0,0 +1,9 @@ +package io.github.classgraph.test.rejected; + +import io.github.classgraph.test.accepted.AcceptedInterface; + +/** + * The Interface RejectedSubinterface. + */ +public interface RejectedSubinterface extends AcceptedInterface { +} diff --git a/src/test/java/io/github/classgraph/test/rejected/RejectedSuperclass.java b/src/test/java/io/github/classgraph/test/rejected/RejectedSuperclass.java new file mode 100644 index 000000000..e9e1878f1 --- /dev/null +++ b/src/test/java/io/github/classgraph/test/rejected/RejectedSuperclass.java @@ -0,0 +1,7 @@ +package io.github.classgraph.test.rejected; + +/** + * RejectedSuperclass. + */ +public class RejectedSuperclass { +} diff --git a/src/test/java/io/github/classgraph/test/whitelisted/Cls.java b/src/test/java/io/github/classgraph/test/whitelisted/Cls.java deleted file mode 100644 index 4095ccbbb..000000000 --- a/src/test/java/io/github/classgraph/test/whitelisted/Cls.java +++ /dev/null @@ -1,7 +0,0 @@ -package io.github.classgraph.test.whitelisted; - -/** - * Cls. - */ -public class Cls { -} diff --git a/src/test/java/io/github/classgraph/test/whitelisted/Whitelisted.java b/src/test/java/io/github/classgraph/test/whitelisted/Whitelisted.java deleted file mode 100644 index ab1ca04a4..000000000 --- a/src/test/java/io/github/classgraph/test/whitelisted/Whitelisted.java +++ /dev/null @@ -1,12 +0,0 @@ -package io.github.classgraph.test.whitelisted; - -import io.github.classgraph.test.blacklisted.BlacklistedAnnotation; -import io.github.classgraph.test.blacklisted.BlacklistedInterface; -import io.github.classgraph.test.blacklisted.BlacklistedSuperclass; - -/** - * Whitelisted. - */ -@BlacklistedAnnotation -public class Whitelisted extends BlacklistedSuperclass implements BlacklistedInterface { -} diff --git a/src/test/java/io/github/classgraph/test/whitelisted/WhitelistedInterface.java b/src/test/java/io/github/classgraph/test/whitelisted/WhitelistedInterface.java deleted file mode 100644 index b786bfb76..000000000 --- a/src/test/java/io/github/classgraph/test/whitelisted/WhitelistedInterface.java +++ /dev/null @@ -1,9 +0,0 @@ -package io.github.classgraph.test.whitelisted; - -import io.github.classgraph.test.blacklisted.BlacklistedInterface; - -/** - * The Interface WhitelistedInterface. - */ -public interface WhitelistedInterface extends BlacklistedInterface { -} diff --git a/src/test/java/io/github/classgraph/test/whitelisted/blacklistedsub/BlacklistedSub.java b/src/test/java/io/github/classgraph/test/whitelisted/blacklistedsub/BlacklistedSub.java deleted file mode 100644 index d544bbde4..000000000 --- a/src/test/java/io/github/classgraph/test/whitelisted/blacklistedsub/BlacklistedSub.java +++ /dev/null @@ -1,7 +0,0 @@ -package io.github.classgraph.test.whitelisted.blacklistedsub; - -/** - * BlacklistedSub. - */ -public class BlacklistedSub { -} From 2ff22347fbe5ba4f07a6f3e5ff8c1bddf3408953 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 12 Jun 2020 05:38:52 -0600 Subject: [PATCH 0847/1778] [maven-release-plugin] prepare release classgraph-4.8.86 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 5ae1406af..66a67db41 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.86-SNAPSHOT + 4.8.86 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.86 From 78e26d5952282ebd5774d65d25567a0550753fa9 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 12 Jun 2020 05:39:00 -0600 Subject: [PATCH 0848/1778] [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 66a67db41..d6ea0f982 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.86 + 4.8.87-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.86 + HEAD From a2e059fef9c756bc753ae55808ec7dd0f1c7226b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 12 Jun 2020 05:42:09 -0600 Subject: [PATCH 0849/1778] Bump version back down --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index d6ea0f982..f789b5c15 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.87-SNAPSHOT + 4.8.86-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -233,7 +233,7 @@ - + org.apache.maven.plugins maven-enforcer-plugin From 4e06e2fbf1bb8e6cc05104b8e3fea372c4f4775e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 12 Jun 2020 05:42:55 -0600 Subject: [PATCH 0850/1778] [maven-release-plugin] prepare release classgraph-4.8.86 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index f789b5c15..53c24602b 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.86-SNAPSHOT + 4.8.86 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.86 From a0d510593e3da9f41852a1210112a0f96aa3647e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 12 Jun 2020 05:43:02 -0600 Subject: [PATCH 0851/1778] [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 53c24602b..107967023 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.86 + 4.8.87-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.86 + HEAD From 667c9ff5631d94ea458d057c13af6b47f0519510 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 13 Jun 2020 18:39:07 -0600 Subject: [PATCH 0852/1778] Only throw exception on `jrt:` URLs if it's an unsupported scheme (#436) --- src/main/java/io/github/classgraph/Resource.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/github/classgraph/Resource.java b/src/main/java/io/github/classgraph/Resource.java index 54e226c6e..2400915f9 100644 --- a/src/main/java/io/github/classgraph/Resource.java +++ b/src/main/java/io/github/classgraph/Resource.java @@ -99,14 +99,17 @@ public Resource(final ClasspathElement classpathElement, final long length) { * if the URI could not be converted to a URL, or the URI had "jrt:" scheme. */ private static URL uriToURL(final URI uri) { - 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: " + uri); - } try { return uri.toURL(); } catch (final MalformedURLException e) { - throw new IllegalArgumentException("Could not create URL from URI: " + uri + " -- " + 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 " + + "(\"jrt:\" is not supported by the URL class without a custom URL protocol handler): " + + uri); + } else { + throw new IllegalArgumentException("Could not create URL from URI: " + uri + " -- " + e); + } } } From 51a59e41f831b85723157fb02d323d829d26d0ec Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 15 Jun 2020 07:13:13 -0600 Subject: [PATCH 0853/1778] Remove failing incremental build phase --- pom.xml | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/pom.xml b/pom.xml index 107967023..3cef5ed38 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,7 @@ - + 4.0.0 io.github.classgraph @@ -31,8 +34,8 @@ scm:git:git@github.com:classgraph/classgraph.git scm:git:git@github.com:classgraph/classgraph.git https://github.com/classgraph/classgraph - HEAD - + HEAD + https://github.com/classgraph/classgraph/issues @@ -262,14 +265,6 @@ true - - - display-info - compile - - display-info - - check-signatures @@ -279,7 +274,8 @@ - + org.codehaus.mojo.signature @@ -359,8 +355,13 @@ - - + + From 2106ca33308248a8fcfa0ed0fead619a9394b076 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 15 Jun 2020 07:24:34 -0600 Subject: [PATCH 0854/1778] Enforce Maven version --- pom.xml | 3 --- 1 file changed, 3 deletions(-) diff --git a/pom.xml b/pom.xml index 3cef5ed38..be36ced9e 100644 --- a/pom.xml +++ b/pom.xml @@ -260,9 +260,6 @@ [3.6.3,) - - - true From 34fb80f3298e7afce3ba546c11a549f31c9f918c Mon Sep 17 00:00:00 2001 From: "Sean C. Sullivan" Date: Fri, 19 Jun 2020 16:44:30 -0700 Subject: [PATCH 0855/1778] add dependabot.yml reference: https://github.blog/2020-06-01-keep-all-your-packages-up-to-date-with-dependabot/ e Please enter the commit message for your changes. Lines starting --- .github/dependabot.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..15cffe75f --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,12 @@ +version: 2 +updates: + - package-ecosystem: "maven" + directory: "/" + schedule: + interval: "daily" + time: "02:00" + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + time: "03:00" From 070db89739df8996edeed252a424830ca6ccadf6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 20 Jun 2020 06:12:45 +0000 Subject: [PATCH 0856/1778] Bump actions/setup-java from v1.2.0 to v1.3.0 Bumps [actions/setup-java](https://github.com/actions/setup-java) from v1.2.0 to v1.3.0. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/v1.2.0...b74d5a6a962763ad3655696a314a4b34df4896c2) 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 47dfd71b2..198930a7c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Set up JDK - uses: actions/setup-java@v1.2.0 + uses: actions/setup-java@v1.3.0 with: java-version: ${{ matrix.java }} - name: print Java version From ada8f23d87f620bf323711ceadeef8fd2eb4dd9c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 20 Jun 2020 06:12:46 +0000 Subject: [PATCH 0857/1778] Bump maven-surefire-plugin from 3.0.0-M4 to 3.0.0-M5 Bumps [maven-surefire-plugin](https://github.com/apache/maven-surefire) from 3.0.0-M4 to 3.0.0-M5. - [Release notes](https://github.com/apache/maven-surefire/releases) - [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.0.0-M4...surefire-3.0.0-M5) Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index be36ced9e..cc1e82593 100644 --- a/pom.xml +++ b/pom.xml @@ -167,7 +167,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.0.0-M4 + 3.0.0-M5 org.codehaus.mojo From 18bb6e11d583243e3d400a3fdca685ffc4b4e348 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 20 Jun 2020 06:12:46 +0000 Subject: [PATCH 0858/1778] Bump org.eclipse.jdt.annotation from 2.2.400 to 2.2.600 Bumps org.eclipse.jdt.annotation from 2.2.400 to 2.2.600. Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index be36ced9e..698b2d46e 100644 --- a/pom.xml +++ b/pom.xml @@ -139,7 +139,7 @@ org.eclipse.jdt org.eclipse.jdt.annotation - 2.2.400 + 2.2.600 provided From 388a2a0de14042a6f5b8d49d0c22473ab3c9fed4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 20 Jun 2020 06:12:46 +0000 Subject: [PATCH 0859/1778] Bump maven-antrun-plugin from 1.8 to 3.0.0 Bumps [maven-antrun-plugin](https://github.com/apache/maven-antrun-plugin) from 1.8 to 3.0.0. - [Release notes](https://github.com/apache/maven-antrun-plugin/releases) - [Commits](https://github.com/apache/maven-antrun-plugin/compare/maven-antrun-plugin-1.8...maven-antrun-plugin-3.0.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 be36ced9e..402678e05 100644 --- a/pom.xml +++ b/pom.xml @@ -182,7 +182,7 @@ org.apache.maven.plugins maven-antrun-plugin - 1.8 + 3.0.0 org.apache.maven.plugins From fac59d29adc620b7a4b25384e7c4c38e3536ead3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 20 Jun 2020 06:12:47 +0000 Subject: [PATCH 0860/1778] Bump junit-jupiter from 5.6.0 to 5.6.2 Bumps [junit-jupiter](https://github.com/junit-team/junit5) from 5.6.0 to 5.6.2. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.6.0...r5.6.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 be36ced9e..6a7ec0cc7 100644 --- a/pom.xml +++ b/pom.xml @@ -72,7 +72,7 @@ org.junit.jupiter junit-jupiter - 5.6.0 + 5.6.2 test From ea751e2abd90e82f9ca2ced181e2e4d2d77f5f08 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 20 Jun 2020 06:12:48 +0000 Subject: [PATCH 0861/1778] Bump build-helper-maven-plugin from 3.0.0 to 3.1.0 Bumps [build-helper-maven-plugin](https://github.com/mojohaus/build-helper-maven-plugin) from 3.0.0 to 3.1.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.0.0...build-helper-maven-plugin-3.1.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 be36ced9e..6a6f3c1b5 100644 --- a/pom.xml +++ b/pom.xml @@ -172,7 +172,7 @@ org.codehaus.mojo build-helper-maven-plugin - 3.0.0 + 3.1.0 org.apache.maven.plugins From f65eea0393adecf249b0271f977f6e50c9821b03 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 20 Jun 2020 00:19:57 -0600 Subject: [PATCH 0862/1778] whitelist -> accept --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0207ad79b..6088ab8e9 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ try (ScanResult scanResult = new ClassGraph() .verbose() // Log to stderr .enableAllInfo() // Scan classes, methods, fields, annotations - .whitelistPackages(pkg) // Scan com.xyz and subpackages (omit to scan all packages) + .acceptPackages(pkg) // Scan com.xyz and subpackages (omit to scan all packages) .scan()) { // Start the scan for (ClassInfo routeClassInfo : scanResult.getClassesWithAnnotation(routeAnnotation)) { AnnotationInfo routeAnnotationInfo = routeClassInfo.getAnnotationInfo(routeAnnotation); @@ -59,7 +59,7 @@ try (ScanResult scanResult = The following code finds all JSON files in `META-INF/config` in all ClassLoaders or modules, and calls the method `readJson(String path, String content)` with the path and content of each file. ```java -try (ScanResult scanResult = new ClassGraph().whitelistPathsNonRecursive("META-INF/config").scan()) { +try (ScanResult scanResult = new ClassGraph().acceptPathsNonRecursive("META-INF/config").scan()) { scanResult.getResourcesWithExtension("json").forEachByteArray((Resource res, byte[] content) -> { readJson(res.getPath(), new String(content, StandardCharsets.UTF_8)); }); From 21523b8889ae170b6617ace2554759930d1af7cf Mon Sep 17 00:00:00 2001 From: "Sean C. Sullivan" Date: Sat, 20 Jun 2020 07:22:37 -0700 Subject: [PATCH 0863/1778] add [jdependency] to README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 6088ab8e9..dc2eaeedf 100644 --- a/README.md +++ b/README.md @@ -167,6 +167,7 @@ Some other classpath scanning mechanisms include: * [coffea](https://github.com/sbilinski/coffea), a command line tool and Python library for analyzing static dependences in Java bytecode * [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) ## License From 1f2a224a6a2f073f6b5eeb62feb4400454c08903 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 21 Jun 2020 15:28:39 -0600 Subject: [PATCH 0864/1778] Add `loadClassAndGetConstructor()` (#447) --- .../java/io/github/classgraph/MethodInfo.java | 58 ++++++++++++++++--- .../github/classgraph/ScanResultObject.java | 5 +- 2 files changed, 53 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index 5da1a659d..72a8614e3 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -29,6 +29,7 @@ package io.github.classgraph; import java.lang.annotation.Repeatable; +import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; @@ -573,27 +574,70 @@ public boolean hasParameterAnnotation(final String annotationName) { // ------------------------------------------------------------------------------------------------------------- /** - * Load the class this method is associated with, and get the {@link Method} reference for this method. + * Load and return the classes of each of the method parameters. * - * @return The {@link Method} reference for this field. - * @throws IllegalArgumentException - * if the method does not exist. + * @return An array of the {@link Class} references for each method parameter. */ - public Method loadClassAndGetMethod() throws IllegalArgumentException { + private Class[] loadParameterClasses() { final MethodParameterInfo[] allParameterInfo = getParameterInfo(); final List> parameterClasses = new ArrayList<>(allParameterInfo.length); for (final MethodParameterInfo mpi : allParameterInfo) { final TypeSignature parameterType = mpi.getTypeSignatureOrTypeDescriptor(); parameterClasses.add(parameterType.loadClass()); } - final Class[] parameterClassesArr = parameterClasses.toArray(new Class[0]); + return parameterClasses.toArray(new Class[0]); + } + + /** + * Load the class this method is associated with, and get the {@link Method} reference for this method. Only + * call this if {@link #isConstructor()} returns false, otherwise an {@link IllegalArgumentException} will be + * thrown. Instead call {@link #loadClassAndGetConstructor()} for constructors. + * + * @return The {@link Method} reference for this method. + * @throws IllegalArgumentException + * if the method does not exist, or if the method is a constructor. + */ + public Method loadClassAndGetMethod() throws IllegalArgumentException { + if (isConstructor()) { + throw new IllegalArgumentException( + "Need to call loadClassAndGetConstructor() for constructors, not loadClassAndGetMethod()"); + } + final Class[] parameterClassesArr = loadParameterClasses(); try { return loadClass().getMethod(getName(), parameterClassesArr); } catch (final NoSuchMethodException e1) { try { return loadClass().getDeclaredMethod(getName(), parameterClassesArr); } catch (final NoSuchMethodException es2) { - throw new IllegalArgumentException("No such method: " + getClassName() + "." + getName()); + throw new IllegalArgumentException("Method not found: " + getClassName() + "." + getName()); + } + } + } + + /** + * Load the class this constructor is associated with, and get the {@link Constructor} reference for this + * constructor. Only call this if {@link #isConstructor()} returns true, otherwise an + * {@link IllegalArgumentException} will be thrown. Instead call {@link #loadClassAndGetMethod()} for non-method + * constructors. + * + * @return The {@link Constructor} reference for this constructor. + * @throws IllegalArgumentException + * if the constructor does not exist, or if the method is not a constructor. + */ + public Constructor loadClassAndGetConstructor() throws IllegalArgumentException { + if (!isConstructor()) { + throw new IllegalArgumentException( + "Need to call loadClassAndGetMethod() for non-constructor methods, not " + + "loadClassAndGetConstructor()"); + } + final Class[] parameterClassesArr = loadParameterClasses(); + try { + return loadClass().getConstructor(parameterClassesArr); + } catch (final NoSuchMethodException e1) { + try { + return loadClass().getDeclaredConstructor(parameterClassesArr); + } catch (final NoSuchMethodException es2) { + throw new IllegalArgumentException("Constructor not found for class " + getClassName()); } } } diff --git a/src/main/java/io/github/classgraph/ScanResultObject.java b/src/main/java/io/github/classgraph/ScanResultObject.java index a65edf28b..fd7de4725 100644 --- a/src/main/java/io/github/classgraph/ScanResultObject.java +++ b/src/main/java/io/github/classgraph/ScanResultObject.java @@ -231,10 +231,9 @@ Class loadClass(final boolean ignoreExceptions) { * Load the class named returned by {@link #getClassInfo()}, or if that returns null, the class named by * {@link #getClassName()}. Returns a {@code Class} reference for the class. * - * @return The {@code Class} reference for the referenced class, or null if the class could not be loaded and - * ignoreExceptions is true. + * @return The {@code Class} reference for the referenced class. * @throws IllegalArgumentException - * if the class could not be loaded and ignoreExceptions was false. + * if the class could not be loaded. */ Class loadClass() { return loadClass(/* ignoreExceptions = */ false); From 85479085f903bc30f6e53fd4684a6faff6136689 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Jun 2020 02:02:27 +0000 Subject: [PATCH 0865/1778] Bump maven-javadoc-plugin from 3.1.1 to 3.2.0 Bumps [maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.1.1 to 3.2.0. - [Release notes](https://github.com/apache/maven-javadoc-plugin/releases) - [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.1.1...maven-javadoc-plugin-3.2.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 23bde7604..3a4d45e77 100644 --- a/pom.xml +++ b/pom.xml @@ -192,7 +192,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.1.1 + 3.2.0 org.apache.maven.plugins From fd784f5d7e338a5c16b1fd4ba985596883c62d2f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Jun 2020 02:02:30 +0000 Subject: [PATCH 0866/1778] Bump cdi-api from 2.0.SP1 to 2.0 Bumps [cdi-api](https://github.com/cdi-spec/cdi) from 2.0.SP1 to 2.0. - [Release notes](https://github.com/cdi-spec/cdi/releases) - [Commits](https://github.com/cdi-spec/cdi/compare/2.0.SP1...2.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 23bde7604..18b409240 100644 --- a/pom.xml +++ b/pom.xml @@ -96,7 +96,7 @@ javax.enterprise cdi-api - 2.0.SP1 + 2.0 test From 5509a399a612a67ee8a7e3f05c2b2eae4fb2dc26 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Jun 2020 02:02:31 +0000 Subject: [PATCH 0867/1778] Bump maven-site-plugin from 3.8.2 to 3.9.0 Bumps [maven-site-plugin](https://github.com/apache/maven-site-plugin) from 3.8.2 to 3.9.0. - [Release notes](https://github.com/apache/maven-site-plugin/releases) - [Commits](https://github.com/apache/maven-site-plugin/compare/maven-site-plugin-3.8.2...maven-site-plugin-3.9.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 23bde7604..1cce1964f 100644 --- a/pom.xml +++ b/pom.xml @@ -229,7 +229,7 @@ org.apache.maven.plugins maven-site-plugin - 3.8.2 + 3.9.0 From ad6e1a93ad6e8c43aa529a7b78ee51a75dcc2297 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Jun 2020 02:03:38 +0000 Subject: [PATCH 0868/1778] Bump build-helper-maven-plugin from 3.1.0 to 3.2.0 Bumps [build-helper-maven-plugin](https://github.com/mojohaus/build-helper-maven-plugin) from 3.1.0 to 3.2.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.1.0...build-helper-maven-plugin-3.2.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 e8a61800d..f81d1feaf 100644 --- a/pom.xml +++ b/pom.xml @@ -172,7 +172,7 @@ org.codehaus.mojo build-helper-maven-plugin - 3.1.0 + 3.2.0 org.apache.maven.plugins From 1c6fbc806f31bd72164c5906b87a505352fdbc43 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 23 Jun 2020 10:12:57 -0600 Subject: [PATCH 0869/1778] [maven-release-plugin] prepare release classgraph-4.8.87 --- pom.xml | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/pom.xml b/pom.xml index f81d1feaf..345d3008b 100644 --- a/pom.xml +++ b/pom.xml @@ -1,12 +1,9 @@ - + 4.0.0 io.github.classgraph classgraph - 4.8.87-SNAPSHOT + 4.8.87 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 - HEAD + classgraph-4.8.87 @@ -271,8 +268,7 @@ - + org.codehaus.mojo.signature @@ -352,13 +348,8 @@ - - + + From 390f61c3f377de0ba9d93dd432040a1d08a53c42 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 23 Jun 2020 10:13:06 -0600 Subject: [PATCH 0870/1778] [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 345d3008b..ca3e7b157 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.87 + 4.8.88-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.87 + HEAD From 1bc0db1c9b2aa54cd46f848a8b344d8482cd4b06 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 25 Jun 2020 02:02:56 +0000 Subject: [PATCH 0871/1778] Bump maven-site-plugin from 3.9.0 to 3.9.1 Bumps [maven-site-plugin](https://github.com/apache/maven-site-plugin) from 3.9.0 to 3.9.1. - [Release notes](https://github.com/apache/maven-site-plugin/releases) - [Commits](https://github.com/apache/maven-site-plugin/compare/maven-site-plugin-3.9.0...maven-site-plugin-3.9.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 ca3e7b157..1926e6e5e 100644 --- a/pom.xml +++ b/pom.xml @@ -226,7 +226,7 @@ org.apache.maven.plugins maven-site-plugin - 3.9.0 + 3.9.1 From b45713995ae80c4107d0a223704f9f2c5de5106e Mon Sep 17 00:00:00 2001 From: Andrea Di Cesare Date: Thu, 25 Jun 2020 12:37:31 +0200 Subject: [PATCH 0872/1778] Add cause to IllegalArgumentException thrown by ScanResult.loadClass() --- 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 c0d032ecf..e09bd468d 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -1220,7 +1220,7 @@ public Class loadClass(final String className, final boolean returnNullIfClas if (returnNullIfClassNotFound) { return null; } else { - throw new IllegalArgumentException("Could not load class " + className + " : " + e); + throw new IllegalArgumentException("Could not load class " + className + " : " + e, e); } } } From e07592ee515c2a56a135c122d2a611ea36f2c51c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 5 Jul 2020 13:10:40 -0600 Subject: [PATCH 0873/1778] Throw exception if passing ClassLoader to overrideClasspath (#457) --- .../java/nonapi/io/github/classgraph/scanspec/ScanSpec.java | 4 ++++ 1 file changed, 4 insertions(+) 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 4a5382936..c5cc67189 100644 --- a/src/main/java/nonapi/io/github/classgraph/scanspec/ScanSpec.java +++ b/src/main/java/nonapi/io/github/classgraph/scanspec/ScanSpec.java @@ -295,6 +295,10 @@ public void addClasspathOverride(final Object overrideClasspathElement) { if (this.overrideClasspath == null) { this.overrideClasspath = new ArrayList<>(); } + if (overrideClasspathElement instanceof ClassLoader) { + throw new IllegalArgumentException( + "Need to pass ClassLoader instances to overrideClassLoaders, not overrideClasspath"); + } this.overrideClasspath .add(overrideClasspathElement instanceof String || overrideClasspathElement instanceof URL || overrideClasspathElement instanceof URI ? overrideClasspathElement From c4beca66616ba519bc5b97c4cca15f6d88db65df Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 5 Jul 2020 13:16:40 -0600 Subject: [PATCH 0874/1778] Improve error reporting (#474) --- .../classgraph/json/JSONSerializer.java | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) 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 666cbc790..5f5460ae7 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/JSONSerializer.java +++ b/src/main/java/nonapi/io/github/classgraph/json/JSONSerializer.java @@ -367,36 +367,36 @@ private static Object toJSONGraph(final Object obj, final Set fieldOrder = resolvedFields.fieldOrder; - final int n = fieldOrder.size(); - - // Convert field values to JSON values - final String[] fieldNames = new String[n]; - final Object[] convertedVals = new Object[n]; - for (int i = 0; i < n; i++) { - final FieldTypeInfo fieldInfo = fieldOrder.get(i); - final Field field = fieldInfo.field; - fieldNames[i] = field.getName(); + // Cache class fields to include in serialization (typeResolutions can be null, + // since it's not necessary to resolve type parameters during serialization) + final ClassFields resolvedFields = classFieldCache.get(cls); + final List fieldOrder = resolvedFields.fieldOrder; + final int n = fieldOrder.size(); + + // Convert field values to JSON values + final String[] fieldNames = new String[n]; + final Object[] convertedVals = new Object[n]; + for (int i = 0; i < n; i++) { + final FieldTypeInfo fieldInfo = fieldOrder.get(i); + final Field field = fieldInfo.field; + fieldNames[i] = field.getName(); + try { convertedVals[i] = JSONUtils.getFieldValue(obj, field); + } catch (IllegalArgumentException | IllegalAccessException e) { + throw ClassGraphException.newClassGraphException("Could not get value of field \"" + + fieldNames[i] + "\" in object of class " + obj.getClass().getName(), e); } - convertVals(convertedVals, visitedOnPath, standardObjectVisited, classFieldCache, objToJSONVal, - onlySerializePublicFields); - - // Create new JSON object representing the standard object - final List> convertedKeyValPairs = new ArrayList<>(n); - for (int i = 0; i < n; i++) { - convertedKeyValPairs.add(new SimpleEntry(fieldNames[i], convertedVals[i])); - } - jsonVal = new JSONObject(convertedKeyValPairs); + } + convertVals(convertedVals, visitedOnPath, standardObjectVisited, classFieldCache, objToJSONVal, + onlySerializePublicFields); - } catch (IllegalArgumentException | IllegalAccessException e) { - throw ClassGraphException.newClassGraphException("Could not get value of field in object: " + obj, - e); + // Create new JSON object representing the standard object + final List> convertedKeyValPairs = new ArrayList<>(n); + for (int i = 0; i < n; i++) { + convertedKeyValPairs.add(new SimpleEntry(fieldNames[i], convertedVals[i])); } + jsonVal = new JSONObject(convertedKeyValPairs); + } // In the case of a DAG, just serialize the same object multiple times, i.e. remove obj From 1e808afcb5beed0b2d2367f143bb324269211d2b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 5 Jul 2020 13:17:16 -0600 Subject: [PATCH 0875/1778] Simplify code --- src/main/java/nonapi/io/github/classgraph/json/JSONUtils.java | 2 -- 1 file changed, 2 deletions(-) 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 ace5e212f..d6afa882e 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/JSONUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/json/JSONUtils.java @@ -264,8 +264,6 @@ static Object getFieldValue(final Object containingObj, final Field field) return field.getByte(containingObj); } else if (fieldType == Character.TYPE) { return field.getChar(containingObj); - } else if (fieldType == Class.class) { - return field.get(containingObj); } else { return field.get(containingObj); } From aa58e8f75255fe8b225537ec26b20558d489f5c6 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 5 Jul 2020 13:18:20 -0600 Subject: [PATCH 0876/1778] Rename variable --- .../java/nonapi/io/github/classgraph/json/JSONSerializer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 5f5460ae7..6a0fd92eb 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/JSONSerializer.java +++ b/src/main/java/nonapi/io/github/classgraph/json/JSONSerializer.java @@ -377,8 +377,8 @@ private static Object toJSONGraph(final Object obj, final Set Date: Mon, 6 Jul 2020 02:03:09 +0000 Subject: [PATCH 0877/1778] Bump animal-sniffer-enforcer-rule from 1.18 to 1.19 Bumps [animal-sniffer-enforcer-rule](https://github.com/mojohaus/animal-sniffer) from 1.18 to 1.19. - [Release notes](https://github.com/mojohaus/animal-sniffer/releases) - [Commits](https://github.com/mojohaus/animal-sniffer/compare/animal-sniffer-parent-1.18...animal-sniffer-parent-1.19) Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1926e6e5e..cd4816a60 100644 --- a/pom.xml +++ b/pom.xml @@ -240,7 +240,7 @@ org.codehaus.mojo animal-sniffer-enforcer-rule - 1.18 + 1.19 From 0cf87101d82c7142e2a84ed26097b463266dab6f Mon Sep 17 00:00:00 2001 From: "Sean C. Sullivan" Date: Fri, 17 Jul 2020 19:34:55 -0700 Subject: [PATCH 0878/1778] ci: Java 8, 11, 12, 13, 14 --- .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 198930a7c..314cd898e 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: [ '1.8.0', '11.0.x', '12.0.x', '13.0.x' ] + java: [ '8', '11', '12', '13', '14' ] steps: - uses: actions/checkout@v2 - name: Set up JDK From 4006ce8c35c6bf75e899f37a13502c78d64f4289 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 21 Jul 2020 04:07:12 -0600 Subject: [PATCH 0879/1778] Code cleanups --- .../nonapi/io/github/classgraph/utils/FileUtils.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 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 828c90f88..8a1be28a0 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -512,30 +512,30 @@ private static boolean closeDirectByteBufferPrivileged(final ByteBuffer byteBuff return false; } // Invoke ((DirectBuffer) byteBuffer).cleaner().clean() - final Method cleaner = byteBuffer.getClass().getMethod("cleaner"); - if (cleaner == null) { + final Method cleanerMethod = byteBuffer.getClass().getMethod("cleaner"); + if (cleanerMethod == null) { if (log != null) { log.log("Could not unmap ByteBuffer, cleaner == null"); } return false; } try { - cleaner.setAccessible(true); + cleanerMethod.setAccessible(true); } catch (final Exception e) { if (log != null) { log.log("Could not unmap ByteBuffer, cleaner.setAccessible(true) failed"); } return false; } - final Object cleanerResult = cleaner.invoke(byteBuffer); - if (cleanerResult == null) { + final Object cleaner = cleanerMethod.invoke(byteBuffer); + if (cleaner == null) { if (log != null) { log.log("Could not unmap ByteBuffer, cleanerResult == null"); } return false; } try { - cleanMethod.invoke(cleaner.invoke(byteBuffer)); + cleanMethod.invoke(cleaner); return true; } catch (final Exception e) { if (log != null) { From 754e2a4d9d8033e3f7af2412433b42b24bb069d6 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 21 Jul 2020 04:09:20 -0600 Subject: [PATCH 0880/1778] Fix log messages --- .../java/nonapi/io/github/classgraph/utils/FileUtils.java | 8 ++++---- 1 file changed, 4 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 8a1be28a0..86e220c9e 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -515,7 +515,7 @@ private static boolean closeDirectByteBufferPrivileged(final ByteBuffer byteBuff final Method cleanerMethod = byteBuffer.getClass().getMethod("cleaner"); if (cleanerMethod == null) { if (log != null) { - log.log("Could not unmap ByteBuffer, cleaner == null"); + log.log("Could not unmap ByteBuffer, cleanerMethod == null"); } return false; } @@ -523,14 +523,14 @@ private static boolean closeDirectByteBufferPrivileged(final ByteBuffer byteBuff cleanerMethod.setAccessible(true); } catch (final Exception e) { if (log != null) { - log.log("Could not unmap ByteBuffer, cleaner.setAccessible(true) failed"); + log.log("Could not unmap ByteBuffer, cleanerMethod.setAccessible(true) failed"); } return false; } final Object cleaner = cleanerMethod.invoke(byteBuffer); if (cleaner == null) { if (log != null) { - log.log("Could not unmap ByteBuffer, cleanerResult == null"); + log.log("Could not unmap ByteBuffer, cleaner == null"); } return false; } @@ -539,7 +539,7 @@ private static boolean closeDirectByteBufferPrivileged(final ByteBuffer byteBuff return true; } catch (final Exception e) { if (log != null) { - log.log("Could not unmap ByteBuffer, cleanMethod.invoke(cleanerResult) failed: " + e); + log.log("Could not unmap ByteBuffer, cleanMethod.invoke(cleaner) failed: " + e); } return false; } From d378ebfefdd6700e4c755c1c340c6233b1894cca Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 22 Jul 2020 19:00:12 -0600 Subject: [PATCH 0881/1778] Prep for incubator feature for releasing ByteBuffer using MemorySegment --- .../io/github/classgraph/utils/FileUtils.java | 139 ++++++++++++------ 1 file changed, 95 insertions(+), 44 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 86e220c9e..1f22e7225 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -50,9 +50,20 @@ * File utilities. */ public final class FileUtils { + /** The DirectByteBuffer.cleaner() method. */ + private static Method directByteBufferCleanerMethod; - /** The clean() method. */ - private static Method cleanMethod; + /** The Cleaner.clean() method. */ + private static Method cleanerCleanMethod; + + // /** The jdk.incubator.foreign.MemorySegment class (JDK14+). */ + // private static Class memorySegmentClass; + // + // /** The jdk.incubator.foreign.MemorySegment.ofByteBuffer method (JDK14+). */ + // private static Method memorySegmentOfByteBufferMethod; + // + // /** The jdk.incubator.foreign.MemorySegment.ofByteBuffer method (JDK14+). */ + // private static Method memorySegmentCloseMethod; /** The attachment() method. */ private static Method attachmentMethod; @@ -425,46 +436,66 @@ public static String getParentDirPath(final String path) { private static void lookupCleanMethodPrivileged() { if (VersionFinder.JAVA_MAJOR_VERSION < 9) { try { - // See: https://stackoverflow.com/a/19447758/3950982 - cleanMethod = Class.forName("sun.misc.Cleaner").getMethod("clean"); - cleanMethod.setAccessible(true); - attachmentMethod = Class.forName("sun.nio.ch.DirectBuffer").getMethod("attachment"); + // See: + // https://stackoverflow.com/a/19447758/3950982 + cleanerCleanMethod = Class.forName("sun.misc.Cleaner").getDeclaredMethod("clean"); + cleanerCleanMethod.setAccessible(true); + final Class directByteBufferClass = Class.forName("sun.nio.ch.DirectBuffer"); + directByteBufferCleanerMethod = directByteBufferClass.getDeclaredMethod("cleaner"); + attachmentMethod = directByteBufferClass.getMethod("attachment"); attachmentMethod.setAccessible(true); } catch (final SecurityException e) { throw ClassGraphException.newClassGraphException( - "You need to grant classgraph RuntimePermission(\"accessClassInPackage.sun.misc\"), " - + "RuntimePermission(\"accessClassInPackage.sun.nio.ch\"), " + "You need to grant classgraph RuntimePermission(\"accessClassInPackage.sun.misc\") " + "and ReflectPermission(\"suppressAccessChecks\")", e); } catch (final ReflectiveOperationException | LinkageError e) { // Ignore } } else { - // 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. - try { - Class unsafeClass; + boolean jdk14Success = 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. try { - unsafeClass = Class.forName("sun.misc.Unsafe"); - } catch (final ReflectiveOperationException | LinkageError e) { - // jdk.internal.misc.Unsafe doesn't yet have an invokeCleaner() method, - // but that method should be added if sun.misc.Unsafe is removed. - unsafeClass = Class.forName("jdk.internal.misc.Unsafe"); + Class unsafeClass; + try { + unsafeClass = Class.forName("sun.misc.Unsafe"); + } catch (final ReflectiveOperationException | LinkageError e) { + throw ClassGraphException.newClassGraphException("Could not get class sun.misc.Unsafe", e); + } + final Field theUnsafeField = unsafeClass.getDeclaredField("theUnsafe"); + theUnsafeField.setAccessible(true); + theUnsafe = theUnsafeField.get(null); + cleanerCleanMethod = unsafeClass.getMethod("invokeCleaner", ByteBuffer.class); + cleanerCleanMethod.setAccessible(true); + } catch (final SecurityException e) { + throw ClassGraphException.newClassGraphException( + "You need to grant classgraph RuntimePermission(\"accessClassInPackage.sun.misc\") " + + "and ReflectPermission(\"suppressAccessChecks\")", + e); + } catch (final ReflectiveOperationException | LinkageError ex) { + // Ignore } - final Field theUnsafeField = unsafeClass.getDeclaredField("theUnsafe"); - theUnsafeField.setAccessible(true); - theUnsafe = theUnsafeField.get(null); - cleanMethod = unsafeClass.getMethod("invokeCleaner", ByteBuffer.class); - cleanMethod.setAccessible(true); - } catch (final SecurityException e) { - throw ClassGraphException.newClassGraphException( - "You need to grant classgraph RuntimePermission(\"accessClassInPackage.sun.misc\"), " - + "RuntimePermission(\"accessClassInPackage.jdk.internal.misc\") " - + "and ReflectPermission(\"suppressAccessChecks\")", - e); - } catch (final ReflectiveOperationException | LinkageError ex) { - // Ignore } } } @@ -489,13 +520,11 @@ public Object run() { * @return true if successful */ private static boolean closeDirectByteBufferPrivileged(final ByteBuffer byteBuffer, final LogNode log) { + if (!byteBuffer.isDirect()) { + // Nothing to do + return true; + } try { - if (cleanMethod == null) { - if (log != null) { - log.log("Could not unmap ByteBuffer, cleanMethod == null"); - } - return false; - } if (VersionFinder.JAVA_MAJOR_VERSION < 9) { if (attachmentMethod == null) { if (log != null) { @@ -512,30 +541,35 @@ private static boolean closeDirectByteBufferPrivileged(final ByteBuffer byteBuff return false; } // Invoke ((DirectBuffer) byteBuffer).cleaner().clean() - final Method cleanerMethod = byteBuffer.getClass().getMethod("cleaner"); - if (cleanerMethod == null) { + if (directByteBufferCleanerMethod == null) { if (log != null) { log.log("Could not unmap ByteBuffer, cleanerMethod == null"); } return false; } try { - cleanerMethod.setAccessible(true); + directByteBufferCleanerMethod.setAccessible(true); } catch (final Exception e) { if (log != null) { log.log("Could not unmap ByteBuffer, cleanerMethod.setAccessible(true) failed"); } return false; } - final Object cleaner = cleanerMethod.invoke(byteBuffer); - if (cleaner == null) { + final Object cleanerInstance = directByteBufferCleanerMethod.invoke(byteBuffer); + if (cleanerInstance == null) { if (log != null) { log.log("Could not unmap ByteBuffer, cleaner == null"); } return false; } + if (cleanerCleanMethod == null) { + if (log != null) { + log.log("Could not unmap ByteBuffer, cleanMethod == null"); + } + return false; + } try { - cleanMethod.invoke(cleaner); + cleanerCleanMethod.invoke(cleanerInstance); return true; } catch (final Exception e) { if (log != null) { @@ -543,6 +577,17 @@ private static boolean closeDirectByteBufferPrivileged(final ByteBuffer byteBuff } return false; } + // } else if (memorySegmentOfByteBufferMethod != null) { + // // JDK 14+ + // final Object memorySegment = memorySegmentOfByteBufferMethod.invoke(null, byteBuffer); + // if (memorySegment == null) { + // if (log != null) { + // log.log("Got null MemorySegment, could not unmap ByteBuffer"); + // } + // return false; + // } + // memorySegmentCloseMethod.invoke(memorySegment); + // return true; } else { if (theUnsafe == null) { if (log != null) { @@ -550,8 +595,14 @@ private static boolean closeDirectByteBufferPrivileged(final ByteBuffer byteBuff } return false; } + if (cleanerCleanMethod == null) { + if (log != null) { + log.log("Could not unmap ByteBuffer, cleanMethod == null"); + } + return false; + } try { - cleanMethod.invoke(theUnsafe, byteBuffer); + cleanerCleanMethod.invoke(theUnsafe, byteBuffer); return true; } catch (final IllegalArgumentException e) { // Buffer is a duplicate or slice From d57719db55bc878ca4f5aa269240399a708b6da4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Jul 2020 03:02:25 +0000 Subject: [PATCH 0882/1778] Bump actions/setup-java from v1.3.0 to v1.4.0 Bumps [actions/setup-java](https://github.com/actions/setup-java) from v1.3.0 to v1.4.0. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/v1.3.0...1253a7eed45cc6191dc4bd9cacd3542878479569) 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 314cd898e..6279877bc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Set up JDK - uses: actions/setup-java@v1.3.0 + uses: actions/setup-java@v1.4.0 with: java-version: ${{ matrix.java }} - name: print Java version From 965c746bdfccf12c123e906a1068edee499dd8bc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 Aug 2020 02:02:35 +0000 Subject: [PATCH 0883/1778] Bump jmh-core from 1.23 to 1.24 Bumps jmh-core from 1.23 to 1.24. Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cd4816a60..54de43002 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ org.openjdk.jmh jmh-core - 1.23 + 1.24 test From a0a78e62e35df4c0cc34811f277c8132b982d27c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 Aug 2020 02:02:36 +0000 Subject: [PATCH 0884/1778] Bump jmh-generator-annprocess from 1.23 to 1.24 Bumps jmh-generator-annprocess from 1.23 to 1.24. Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cd4816a60..7a9428d0e 100644 --- a/pom.xml +++ b/pom.xml @@ -81,7 +81,7 @@ org.openjdk.jmh jmh-generator-annprocess - 1.23 + 1.24 test From 39a591a4485f6440f456f0295b308b164f32f892 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 10 Aug 2020 18:15:07 -0600 Subject: [PATCH 0885/1778] Make code more legible --- .../java/io/github/classgraph/ClassInfo.java | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index e320cfe91..f525ec78a 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -770,19 +770,17 @@ private static Set filterClassInfo(final Collection classe final Set classInfoSetFiltered = new LinkedHashSet<>(classes.size()); for (final ClassInfo classInfo : classes) { // Check class type against requested type(s) - if ((includeAllTypes // + boolean includeType = includeAllTypes // || includeStandardClasses && classInfo.isStandardClass() // || includeImplementedInterfaces && classInfo.isImplementedInterface() // || includeAnnotations && classInfo.isAnnotation() // || includeEnums && classInfo.isEnum() // - || includeRecords && classInfo.isRecord()) // - // Always check reject - && !scanSpec.classOrPackageIsRejected(classInfo.name) // - // Always return accepted classes, or external classes if enableExternalClasses is true - && (!classInfo.isExternalClass || scanSpec.enableExternalClasses - // Return external (non-accepted) classes if viewing class hierarchy "upwards" - || !strictAccept)) { - // Class passed strict accept criteria + || includeRecords && classInfo.isRecord(); + // Return external (non-accepted) classes if viewing class hierarchy "upwards" + boolean acceptClass = !classInfo.isExternalClass || scanSpec.enableExternalClasses || !strictAccept; + // If class is of correct type, and class is accepted, and class/package are not explicitly rejected + if (includeType && acceptClass && !scanSpec.classOrPackageIsRejected(classInfo.name)) { + // Class passed accept criteria classInfoSetFiltered.add(classInfo); } } @@ -821,11 +819,11 @@ private ReachableAndDirectlyRelatedClasses(final Set reachableClasses * directly related. * * @param relType - * the rel type + * the relationship type * @param strictAccept - * the strict accept criterion + * If true, exclude class if it is is external, rejected, or a system class. * @param classTypes - * the class types + * the class types to accept * @return the reachable and directly related classes */ private ReachableAndDirectlyRelatedClasses filterClassInfo(final RelType relType, final boolean strictAccept, From 2bc67b45664e7185a160cd5570428381835e079a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 10 Aug 2020 18:37:21 -0600 Subject: [PATCH 0886/1778] Update comment --- 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 f525ec78a..9b2ea5d0e 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -721,7 +721,7 @@ private enum ClassType { * @param scanSpec * the scan spec * @param strictAccept - * If true, exclude class if it is is external, rejected, or a system class. + * If true, exclude class if it is external, if external classes are not enabled * @param classTypes * the class types * @return the filtered classes. @@ -821,7 +821,7 @@ private ReachableAndDirectlyRelatedClasses(final Set reachableClasses * @param relType * the relationship type * @param strictAccept - * If true, exclude class if it is is external, rejected, or a system class. + * If true, exclude class if it is external, if external classes are not enabled * @param classTypes * the class types to accept * @return the reachable and directly related classes From 21455743848eb093b827b22f8b1442d29130cfe9 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 10 Aug 2020 18:42:47 -0600 Subject: [PATCH 0887/1778] Add dependents badge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index dc2eaeedf..23ee0cc74 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ ClassGraph is an uber-fast, ultra-lightweight, parallelized classpath scanner an
[![License: MIT](https://img.shields.io/badge/license-MIT-lightgrey.svg)](https://github.com/classgraph/classgraph/blob/master/LICENSE) [![Dependencies: none](https://img.shields.io/badge/dependencies-none-lightgrey.svg)](#) +[![Dependents](https://badgen.net/github/dependents-repo/classgraph/classgraph)](https://github.com/classgraph/classgraph/network/dependents?package_id=UGFja2FnZS0xODcxNTE4NTM%3D)
[![GitHub stars chart](https://img.shields.io/badge/github%20stars-chart-yellow.svg)](https://seladb.github.io/StarTrack-js/#/preload?r=classgraph,classgraph) [![Gitter chat](https://img.shields.io/badge/gitter-join%20chat-yellow.svg)](https://gitter.im/classgraph/Lobby) From 71bbf746e0baaab022b7dab6dab7bd19af5756c9 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 10 Aug 2020 18:43:58 -0600 Subject: [PATCH 0888/1778] Update badges --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 23ee0cc74..bf853b825 100644 --- a/README.md +++ b/README.md @@ -20,12 +20,13 @@ ClassGraph is an uber-fast, ultra-lightweight, parallelized classpath scanner an [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.github.classgraph/classgraph/badge.svg)](https://mvnrepository.com/artifact/io.github.classgraph/classgraph) [![Javadocs](http://www.javadoc.io/badge/io.github.classgraph/classgraph.svg)](https://javadoc.io/doc/io.github.classgraph/classgraph)
-[![License: MIT](https://img.shields.io/badge/license-MIT-lightgrey.svg)](https://github.com/classgraph/classgraph/blob/master/LICENSE) [![Dependencies: none](https://img.shields.io/badge/dependencies-none-lightgrey.svg)](#) [![Dependents](https://badgen.net/github/dependents-repo/classgraph/classgraph)](https://github.com/classgraph/classgraph/network/dependents?package_id=UGFja2FnZS0xODcxNTE4NTM%3D)
-[![GitHub stars chart](https://img.shields.io/badge/github%20stars-chart-yellow.svg)](https://seladb.github.io/StarTrack-js/#/preload?r=classgraph,classgraph) -[![Gitter chat](https://img.shields.io/badge/gitter-join%20chat-yellow.svg)](https://gitter.im/classgraph/Lobby) +[![License: MIT](https://img.shields.io/badge/license-MIT-lightgrey.svg)](https://github.com/classgraph/classgraph/blob/master/LICENSE) +
+[![GitHub stars chart](https://img.shields.io/badge/github%20stars-chart-blue.svg)](https://seladb.github.io/StarTrack-js/#/preload?r=classgraph,classgraph) +[![Gitter chat](https://img.shields.io/badge/gitter-join%20chat-blue.svg)](https://gitter.im/classgraph/Lobby) | ClassGraph is now fully stable. This project adheres to the **[Zero Bugs Commitment](https://github.com/classgraph/classgraph/blob/master/Zero-Bugs-Commitment.md)**. | |-----------------------------| From b154308a14142a18d280090cfb974356f91db728 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 10 Aug 2020 18:45:24 -0600 Subject: [PATCH 0889/1778] Update badges --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index bf853b825..daf26a140 100644 --- a/README.md +++ b/README.md @@ -22,11 +22,11 @@ ClassGraph is an uber-fast, ultra-lightweight, parallelized classpath scanner an
[![Dependencies: none](https://img.shields.io/badge/dependencies-none-lightgrey.svg)](#) [![Dependents](https://badgen.net/github/dependents-repo/classgraph/classgraph)](https://github.com/classgraph/classgraph/network/dependents?package_id=UGFja2FnZS0xODcxNTE4NTM%3D) -
-[![License: MIT](https://img.shields.io/badge/license-MIT-lightgrey.svg)](https://github.com/classgraph/classgraph/blob/master/LICENSE) -
[![GitHub stars chart](https://img.shields.io/badge/github%20stars-chart-blue.svg)](https://seladb.github.io/StarTrack-js/#/preload?r=classgraph,classgraph) +
[![Gitter chat](https://img.shields.io/badge/gitter-join%20chat-blue.svg)](https://gitter.im/classgraph/Lobby) +
+[![License: MIT](https://img.shields.io/badge/license-MIT-lightgrey.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)**. | |-----------------------------| From cd46e67618e67777a44153cc3b5938bb402b8aaa Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 10 Aug 2020 18:46:02 -0600 Subject: [PATCH 0890/1778] Update badges --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index daf26a140..f46042735 100644 --- a/README.md +++ b/README.md @@ -20,13 +20,13 @@ ClassGraph is an uber-fast, ultra-lightweight, parallelized classpath scanner an [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.github.classgraph/classgraph/badge.svg)](https://mvnrepository.com/artifact/io.github.classgraph/classgraph) [![Javadocs](http://www.javadoc.io/badge/io.github.classgraph/classgraph.svg)](https://javadoc.io/doc/io.github.classgraph/classgraph)
-[![Dependencies: none](https://img.shields.io/badge/dependencies-none-lightgrey.svg)](#) +[![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) [![GitHub stars chart](https://img.shields.io/badge/github%20stars-chart-blue.svg)](https://seladb.github.io/StarTrack-js/#/preload?r=classgraph,classgraph)
[![Gitter chat](https://img.shields.io/badge/gitter-join%20chat-blue.svg)](https://gitter.im/classgraph/Lobby)
-[![License: MIT](https://img.shields.io/badge/license-MIT-lightgrey.svg)](https://github.com/classgraph/classgraph/blob/master/LICENSE) +[![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)**. | |-----------------------------| From baf1ca56d97d89c68781a76254afea73e9d19307 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 10 Aug 2020 18:46:33 -0600 Subject: [PATCH 0891/1778] Update badges --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f46042735..8dd24a644 100644 --- a/README.md +++ b/README.md @@ -17,13 +17,13 @@ ClassGraph is an uber-fast, ultra-lightweight, parallelized classpath scanner an [![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)
-[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.github.classgraph/classgraph/badge.svg)](https://mvnrepository.com/artifact/io.github.classgraph/classgraph) -[![Javadocs](http://www.javadoc.io/badge/io.github.classgraph/classgraph.svg)](https://javadoc.io/doc/io.github.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) [![GitHub stars chart](https://img.shields.io/badge/github%20stars-chart-blue.svg)](https://seladb.github.io/StarTrack-js/#/preload?r=classgraph,classgraph)
+[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.github.classgraph/classgraph/badge.svg)](https://mvnrepository.com/artifact/io.github.classgraph/classgraph) +[![Javadocs](http://www.javadoc.io/badge/io.github.classgraph/classgraph.svg)](https://javadoc.io/doc/io.github.classgraph/classgraph) +
[![Gitter chat](https://img.shields.io/badge/gitter-join%20chat-blue.svg)](https://gitter.im/classgraph/Lobby)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/classgraph/classgraph/blob/master/LICENSE) From eb423e20d60b85a04905537932a88e6f844db940 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 10 Aug 2020 19:22:23 -0600 Subject: [PATCH 0892/1778] Comment out unused code --- .../io/github/classgraph/utils/FileUtils.java | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 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 1f22e7225..9e8d936d8 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FileUtils.java @@ -453,7 +453,7 @@ private static void lookupCleanMethodPrivileged() { // Ignore } } else { - boolean jdk14Success = false; + //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 @@ -473,30 +473,30 @@ private static void lookupCleanMethodPrivileged() { // } 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. + //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. + try { + Class unsafeClass; try { - Class unsafeClass; - try { - unsafeClass = Class.forName("sun.misc.Unsafe"); - } catch (final ReflectiveOperationException | LinkageError e) { - throw ClassGraphException.newClassGraphException("Could not get class sun.misc.Unsafe", e); - } - final Field theUnsafeField = unsafeClass.getDeclaredField("theUnsafe"); - theUnsafeField.setAccessible(true); - theUnsafe = theUnsafeField.get(null); - cleanerCleanMethod = unsafeClass.getMethod("invokeCleaner", ByteBuffer.class); - cleanerCleanMethod.setAccessible(true); - } catch (final SecurityException e) { - throw ClassGraphException.newClassGraphException( - "You need to grant classgraph RuntimePermission(\"accessClassInPackage.sun.misc\") " - + "and ReflectPermission(\"suppressAccessChecks\")", - e); - } catch (final ReflectiveOperationException | LinkageError ex) { - // Ignore + unsafeClass = Class.forName("sun.misc.Unsafe"); + } catch (final ReflectiveOperationException | LinkageError e) { + throw ClassGraphException.newClassGraphException("Could not get class sun.misc.Unsafe", e); } + final Field theUnsafeField = unsafeClass.getDeclaredField("theUnsafe"); + theUnsafeField.setAccessible(true); + theUnsafe = theUnsafeField.get(null); + cleanerCleanMethod = unsafeClass.getMethod("invokeCleaner", ByteBuffer.class); + cleanerCleanMethod.setAccessible(true); + } catch (final SecurityException e) { + throw ClassGraphException.newClassGraphException( + "You need to grant classgraph RuntimePermission(\"accessClassInPackage.sun.misc\") " + + "and ReflectPermission(\"suppressAccessChecks\")", + e); + } catch (final ReflectiveOperationException | LinkageError ex) { + // Ignore } + //} } } From 56b04bb99eb08544e1cff1c10d8b373b0d68b9ce Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 10 Aug 2020 19:35:36 -0600 Subject: [PATCH 0893/1778] Fix broken unit test --- .../issues/issue128/Issue128Test.java | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/test/java/io/github/classgraph/issues/issue128/Issue128Test.java b/src/test/java/io/github/classgraph/issues/issue128/Issue128Test.java index 0c24f17be..20c36d9d0 100644 --- a/src/test/java/io/github/classgraph/issues/issue128/Issue128Test.java +++ b/src/test/java/io/github/classgraph/issues/issue128/Issue128Test.java @@ -31,7 +31,7 @@ import static org.assertj.core.api.Assertions.assertThat; import java.io.IOException; -import java.io.InputStream; +import java.net.HttpURLConnection; import java.net.URL; import java.net.URLClassLoader; import java.util.List; @@ -46,11 +46,11 @@ */ public class Issue128Test { /** The Constant SITE. */ - private static final String SITE = "https://github.com/classgraph"; + private static final String SITE = "https://githubraw.com/classgraph"; /** The Constant JAR_URL. */ private static final String JAR_URL = SITE + // - "/classgraph/blob/master/src/test/resources/nested-jars-level1.zip?raw=true"; + "/classgraph/latest/src/test/resources/nested-jars-level1.zip"; /** The Constant NESTED_JAR_URL. */ private static final String NESTED_JAR_URL = // @@ -72,9 +72,18 @@ public void issue128Test() throws Exception { final List filesInsideLevel3 = scanResult.getAllResources().getPaths(); if (filesInsideLevel3.isEmpty()) { // If there were no files inside jar, it is possible that remote jar could not be downloaded - try (InputStream is = jarURL.openStream()) { - throw new Exception("Able to download remote jar, but could not find files within jar"); - } catch (final IOException e) { + try { + HttpURLConnection connection = (HttpURLConnection) jarURL.openConnection(); + connection.setRequestMethod("GET"); + connection.connect(); + int code = connection.getResponseCode(); + if (code != 200) { + throw new Exception( + "Got bad response code " + code + " when trying to fetch URL " + jarURL); + } else { + throw new Exception("Able to download remote jar, but could not find files within jar"); + } + } catch (final IOException | SecurityException e) { System.err.println("Could not download remote jar, skipping test " + Issue128Test.class.getName() + ": " + e); } From 0508629ac5c4d7bb8e6437fd17e1f56ae67eacfd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Aug 2020 02:00:46 +0000 Subject: [PATCH 0894/1778] Bump maven-resources-plugin from 3.1.0 to 3.2.0 Bumps [maven-resources-plugin](https://github.com/apache/maven-resources-plugin) from 3.1.0 to 3.2.0. - [Release notes](https://github.com/apache/maven-resources-plugin/releases) - [Commits](https://github.com/apache/maven-resources-plugin/compare/maven-resources-plugin-3.1.0...maven-resources-plugin-3.2.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 43f3dff64..aee4d0ccc 100644 --- a/pom.xml +++ b/pom.xml @@ -154,7 +154,7 @@ org.apache.maven.plugins maven-resources-plugin - 3.1.0 + 3.2.0 org.apache.maven.plugins From db8f2f5736ceafeafcbf1d07aa388c783130b9ce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Aug 2020 03:00:43 +0000 Subject: [PATCH 0895/1778] Bump actions/setup-java from v1.4.0 to v1.4.1 Bumps [actions/setup-java](https://github.com/actions/setup-java) from v1.4.0 to v1.4.1. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/v1.4.0...3019d15cad3f7d2657e77aa6efe0571b3a89d0b4) 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 6279877bc..411d0ad94 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Set up JDK - uses: actions/setup-java@v1.4.0 + uses: actions/setup-java@v1.4.1 with: java-version: ${{ matrix.java }} - name: print Java version From 644cb45e246dcd15481c6a6c77d7edfd3f6e47c8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 14 Aug 2020 02:02:10 +0000 Subject: [PATCH 0896/1778] Bump jmh-generator-annprocess from 1.24 to 1.25 Bumps jmh-generator-annprocess from 1.24 to 1.25. Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index aee4d0ccc..c77d07d0e 100644 --- a/pom.xml +++ b/pom.xml @@ -81,7 +81,7 @@ org.openjdk.jmh jmh-generator-annprocess - 1.24 + 1.25 test From b191b672f4e429efd7533de609e6aca7f1d206e8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 14 Aug 2020 02:02:11 +0000 Subject: [PATCH 0897/1778] Bump jmh-core from 1.24 to 1.25 Bumps jmh-core from 1.24 to 1.25. Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index aee4d0ccc..af3d2c3dd 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ org.openjdk.jmh jmh-core - 1.24 + 1.25 test From 68c730d2ed98a8050e3358fb25e1237939b19025 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 20 Aug 2020 05:42:39 -0600 Subject: [PATCH 0898/1778] Allow '+' characters unescaped in URLs (#468) --- .../java/nonapi/io/github/classgraph/utils/URLPathEncoder.java | 2 ++ 1 file changed, 2 insertions(+) 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 faf92746b..29fd1d80d 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/URLPathEncoder.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/URLPathEncoder.java @@ -54,6 +54,8 @@ public final class URLPathEncoder { safe['!'] = safe['*'] = safe['\''] = safe['('] = safe[')'] = safe[','] = true; // Only include "/" from "fsegment" and "hsegment" rules (exclude ':', '@', '&' and '=' for safety) safe['/'] = true; + // Also allow '+' characters (#468) + safe['+'] = true; } /** Hexadecimal digits. */ From a8716a2720c9864e3ec1947d41584d5b15e88454 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 20 Aug 2020 08:05:32 -0600 Subject: [PATCH 0899/1778] Add HTTP(S) timeout --- .../fastzipfilereader/NestedJarHandler.java | 15 ++++++++++++--- .../classgraph/issues/issue128/Issue128Test.java | 8 ++++++-- 2 files changed, 18 insertions(+), 5 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 f1837e95d..6abefd569 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/NestedJarHandler.java @@ -380,6 +380,9 @@ public RecyclableInflater newInstance() throws RuntimeException { /** The maximum initial buffer size. */ private static final int MAX_INITIAL_BUFFER_SIZE = 16 * 1024 * 1024; + /** HTTP(S) timeout, ms. */ + private static final int HTTP_TIMEOUT = 5000; + // ------------------------------------------------------------------------------------------------------------- /** @@ -542,9 +545,15 @@ private PhysicalZipFile downloadJarFromURL(final String jarURL, final LogNode lo if (conn instanceof HttpURLConnection) { // Get content length from HTTP headers, if available httpConn = (HttpURLConnection) url.openConnection(); - contentLengthHint = httpConn.getContentLengthLong(); - if (contentLengthHint < -1L) { - contentLengthHint = -1L; + 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 (conn.getURL().getProtocol().equalsIgnoreCase("file")) { // We ended up with a "file:" URL, which can happen as a result of a custom URL scheme that diff --git a/src/test/java/io/github/classgraph/issues/issue128/Issue128Test.java b/src/test/java/io/github/classgraph/issues/issue128/Issue128Test.java index 20c36d9d0..5a829a20e 100644 --- a/src/test/java/io/github/classgraph/issues/issue128/Issue128Test.java +++ b/src/test/java/io/github/classgraph/issues/issue128/Issue128Test.java @@ -73,16 +73,20 @@ public void issue128Test() throws Exception { if (filesInsideLevel3.isEmpty()) { // If there were no files inside jar, it is possible that remote jar could not be downloaded try { - HttpURLConnection connection = (HttpURLConnection) jarURL.openConnection(); + final HttpURLConnection connection = (HttpURLConnection) jarURL.openConnection(); connection.setRequestMethod("GET"); + connection.setConnectTimeout(2000); connection.connect(); - int code = connection.getResponseCode(); + final int code = connection.getResponseCode(); if (code != 200) { throw new Exception( "Got bad response code " + code + " when trying to fetch URL " + jarURL); } else { throw new Exception("Able to download remote jar, but could not find files within jar"); } + } catch (final java.net.SocketTimeoutException e) { + System.err.println("Timeout while trying to download remote jar, skipping test " + + Issue128Test.class.getName() + ": " + e); } catch (final IOException | SecurityException e) { System.err.println("Could not download remote jar, skipping test " + Issue128Test.class.getName() + ": " + e); From d1901ec89fddf27d18edec7988d30264c2c25cbf Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 20 Aug 2020 08:07:24 -0600 Subject: [PATCH 0900/1778] Support '+' characters in URLs (#468) --- .../java/io/github/classgraph/ClassInfo.java | 5 +- .../classgraph/ClasspathElementZip.java | 24 +++++- .../java/io/github/classgraph/Scanner.java | 79 +++++++++++------- .../classgraph/utils/URLPathEncoder.java | 65 +++++++++++++- .../issues/issue468/Issue468Test.java | 58 +++++++++++++ src/test/resources/issue468/x+y/z+w.jar | Bin 0 -> 168 bytes 6 files changed, 194 insertions(+), 37 deletions(-) create mode 100644 src/test/java/io/github/classgraph/issues/issue468/Issue468Test.java create mode 100644 src/test/resources/issue468/x+y/z+w.jar diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index 9b2ea5d0e..ced9749a5 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -770,14 +770,15 @@ private static Set filterClassInfo(final Collection classe final Set classInfoSetFiltered = new LinkedHashSet<>(classes.size()); for (final ClassInfo classInfo : classes) { // Check class type against requested type(s) - boolean includeType = includeAllTypes // + final boolean includeType = includeAllTypes // || includeStandardClasses && classInfo.isStandardClass() // || includeImplementedInterfaces && classInfo.isImplementedInterface() // || includeAnnotations && classInfo.isAnnotation() // || includeEnums && classInfo.isEnum() // || includeRecords && classInfo.isRecord(); // Return external (non-accepted) classes if viewing class hierarchy "upwards" - boolean acceptClass = !classInfo.isExternalClass || scanSpec.enableExternalClasses || !strictAccept; + final boolean acceptClass = !classInfo.isExternalClass || scanSpec.enableExternalClasses + || !strictAccept; // If class is of correct type, and class is accepted, and class/package are not explicitly rejected if (includeType && acceptClass && !scanSpec.classOrPackageIsRejected(classInfo.name)) { // Class passed accept criteria diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index 807901dfc..67768e3da 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -29,6 +29,7 @@ package io.github.classgraph; import java.io.File; +import java.io.IOError; import java.io.IOException; import java.io.InputStream; import java.net.URI; @@ -64,7 +65,10 @@ /** A zip/jarfile classpath element. */ class ClasspathElementZip extends ClasspathElement { - /** The {@link String} representation of the raw path, {@link URL} or {@link URI} for this zipfile. */ + /** + * The {@link String} representation of the path string, {@link URL}, {@link URI}, or {@link Path} for this + * zipfile. + */ private final String rawPath; /** The logical zipfile for this classpath element. */ LogicalZipFile logicalZipFile; @@ -89,7 +93,7 @@ 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} or {@link URI} for the jarfile + * a {@link URL}, {@link URI} ol {@link Path} for the jarfile. * @param classLoader * the classloader * @param nestedJarHandler @@ -100,9 +104,21 @@ class ClasspathElementZip extends ClasspathElement { ClasspathElementZip(final Object rawPathObj, final ClassLoader classLoader, final NestedJarHandler nestedJarHandler, final ScanSpec scanSpec) { super(classLoader, scanSpec); - // Convert the raw path object (String, URL, or URI) to a string. + // Convert the raw path object (String, URL, URI, or Path) to a string. // Any required URL/URI parsing are done in NestedJarHandler. - this.rawPath = rawPathObj.toString(); + String rawPath = null; + if (rawPathObj instanceof Path) { + // 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) { + // Fall through + } + } + if (rawPath == null) { + rawPath = rawPathObj.toString(); + } + this.rawPath = rawPath; this.zipFilePath = rawPath; // May change when open() is called this.nestedJarHandler = nestedJarHandler; } diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 2d44d19a9..f7a9b980f 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -32,11 +32,11 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.net.MalformedURLException; +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.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.AbstractMap.SimpleEntry; @@ -411,7 +411,7 @@ public ClasspathElement newInstance(final ClasspathElementAndClassLoader classpa } // Check type of classpath entry object - String classpathEntryPathStr; + Path classpathEntryPath = null; if (classpathEntryObj instanceof URL) { URL classpathEntryURL = (URL) classpathEntryObj; String scheme = classpathEntryURL.getProtocol(); @@ -425,56 +425,75 @@ public ClasspathElement newInstance(final ClasspathElementAndClassLoader classpa throw new IOException("Could not strip 'jar:' prefix from " + classpathEntryObj, e); } } - if ("file".equals(scheme)) { - // Extract file path, and use below as a path string to determine if this - // classpath element is a file (jar) or directory - classpathEntryPathStr = URLDecoder.decode(classpathEntryURL.getPath(), "UTF-8"); - // Fall through - } else if ("http".equals(scheme) || "https".equals(scheme)) { + 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); } else { try { - // See if the URL resolves to a Path of type directory - final Path path = Paths.get(classpathEntryURL.toURI()); - if (Files.isDirectory(path)) { - // If URL maps to a directory, use the NIO Path API instead of the File API - return new ClasspathElementPathDir(path, dirOrPathPackageRoot, - classpathEntry.classLoader, nestedJarHandler, scanSpec); - } - } catch (final URISyntaxException | IllegalArgumentException | SecurityException e) { + // 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()); } catch (final FileSystemNotFoundException e) { // This is a custom URL scheme without a backing FileSystem + return new ClasspathElementZip(classpathEntryURL, classpathEntry.classLoader, + nestedJarHandler, scanSpec); } - - // For custom URL schemes that do not resolve to directories, assume the URL - // points to a jarfile - return new ClasspathElementZip(classpathEntryURL, classpathEntry.classLoader, + } + } 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, 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) { + throw new IOException( + "Cannot handle URI " + classpathEntryURI + " : " + e.getMessage()); + } catch (final FileSystemNotFoundException e) { + // This is a custom URI scheme without a backing FileSystem + return new ClasspathElementZip(classpathEntryURI, classpathEntry.classLoader, + nestedJarHandler, scanSpec); + } } } else if (classpathEntryObj instanceof Path) { - final Path classpathEntryPath = (Path) classpathEntryObj; + classpathEntryPath = (Path) classpathEntryObj; + } else { + // Fall through for any other object type (toString will be used to get path) + } + + if (classpathEntryPath != null) { 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.toUri(), classpathEntry.classLoader, + return new ClasspathElementZip(classpathEntryPath, classpathEntry.classLoader, nestedJarHandler, scanSpec); } else if (FileUtils.canReadAndIsDir(packageRootPath)) { // classpathEntryObj is a Path which points to a dir -- need to scan it recursively - return new ClasspathElementPathDir((Path) classpathEntryObj, dirOrPathPackageRoot, + return new ClasspathElementPathDir(classpathEntryPath, dirOrPathPackageRoot, classpathEntry.classLoader, nestedJarHandler, scanSpec); - } else { - throw new IOException("Path is not a directory or file: " + classpathEntryPath); } - - } else { - // Convert classpathEntryObj to a string - classpathEntryPathStr = classpathEntryObj.toString(); } - // Fall through for file paths + + // Fall through for other object types (including String) + // Convert classpathEntryObj to a string + 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, 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 29fd1d80d..5ac5eb4d8 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/URLPathEncoder.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/URLPathEncoder.java @@ -28,6 +28,8 @@ */ package nonapi.io.github.classgraph.utils; +import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.nio.charset.StandardCharsets; import nonapi.io.github.classgraph.utils.VersionFinder.OperatingSystem; @@ -55,7 +57,7 @@ public final class URLPathEncoder { // Only include "/" from "fsegment" and "hsegment" rules (exclude ':', '@', '&' and '=' for safety) safe['/'] = true; // Also allow '+' characters (#468) - safe['+'] = true; + //safe['+'] = true; } /** Hexadecimal digits. */ @@ -72,6 +74,67 @@ private URLPathEncoder() { // Cannot be constructed } + /** Unescape chars in a URL. URLDecoder.decode is broken: https://bugs.openjdk.java.net/browse/JDK-8179507 */ + private static void unescapeChars(final String str, final boolean isQuery, final ByteArrayOutputStream buf) { + if (str.isEmpty()) { + return; + } + for (int chrIdx = 0, len = str.length(); chrIdx < len; chrIdx++) { + final char c = str.charAt(chrIdx); + if (c == '%') { + // Decode %-escaped char sequence, e.g. %5D + if (chrIdx > len - 3) { + // Ignore truncated %-seq at end of string + } else { + final char c1 = str.charAt(++chrIdx); + final int digit1 = c1 >= '0' && c1 <= '9' ? (c1 - '0') + : c1 >= 'a' && c1 <= 'f' ? (c1 - 'a' + 10) + : c1 >= 'A' && c1 <= 'F' ? (c1 - 'A' + 10) : -1; + final char c2 = str.charAt(++chrIdx); + final int digit2 = c2 >= '0' && c2 <= '9' ? (c2 - '0') + : c2 >= 'a' && c2 <= 'f' ? (c2 - 'a' + 10) + : c2 >= 'A' && c2 <= 'F' ? (c2 - 'A' + 10) : -1; + if (digit1 < 0 || digit2 < 0) { + try { + buf.write(str.substring(chrIdx - 2, chrIdx + 1).getBytes(StandardCharsets.UTF_8)); + } catch (final IOException e) { + // Ignore + } + } else { + buf.write((byte) ((digit1 << 4) | digit2)); + } + } + } else if (isQuery && c == '+') { + buf.write((byte) ' '); + } else if (c <= 0x7f) { + buf.write((byte) c); + } else { + try { + buf.write(Character.toString(c).getBytes(StandardCharsets.UTF_8)); + } catch (final IOException e) { + // Ignore + } + } + } + } + + /** + * Unescape a URL segment, and turn it from UTF-8 bytes into a Java string. + * + * @param str + * the str + * @return the string + */ + public static String decodePath(final String str) { + final int queryIdx = str.indexOf('?'); + final String partBeforeQuery = queryIdx < 0 ? str : str.substring(0, queryIdx); + final String partFromQuery = queryIdx < 0 ? "" : str.substring(queryIdx); + final ByteArrayOutputStream buf = new ByteArrayOutputStream(); + unescapeChars(partBeforeQuery, /* isQuery = */ false, buf); + unescapeChars(partFromQuery, /* isQuery = */ true, buf); + return new String(buf.toByteArray(), StandardCharsets.UTF_8); + } + /** * Encode a URL path using percent-encoding. '/' is not encoded. * diff --git a/src/test/java/io/github/classgraph/issues/issue468/Issue468Test.java b/src/test/java/io/github/classgraph/issues/issue468/Issue468Test.java new file mode 100644 index 000000000..4eb62411c --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue468/Issue468Test.java @@ -0,0 +1,58 @@ +/* + * 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.issues.issue468; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.FileNotFoundException; +import java.net.URL; + +import org.junit.jupiter.api.Test; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ScanResult; + +/** + * Issue468Test. + */ +public class Issue468Test { + /** Test '+' signs in URLs. */ + @Test + public void issue368Test() throws Exception { + final URL url = Issue468Test.class.getClassLoader().getResource("issue468/x+y/z+w.jar"); + if (url == null) { + throw new FileNotFoundException(); + } + System.out.println(url); + try (ScanResult scanResult = new ClassGraph().acceptPackagesNonRecursive("").overrideClasspath(url) + .scan()) { + assertThat(scanResult.getAllResources().getPaths()).containsExactly("innerfile"); + } + } +} diff --git a/src/test/resources/issue468/x+y/z+w.jar b/src/test/resources/issue468/x+y/z+w.jar new file mode 100644 index 0000000000000000000000000000000000000000..4e10f9c4df3fa2603e894768ad14a7fdcf4c98c6 GIT binary patch literal 168 zcmWIWW@h1H0D-l7B7tBAl;C8LVaUwOOD#&v%t;Lm;bdSoO|gpy;nE6j21b?_%nS@* pBEXxGNsbwpArf#iw={y7NJg?kjKnaGl?|kj5eWT(v@?jq004b27})>- literal 0 HcmV?d00001 From 9184a9dda4ff618c80da0eaf4f3402ad5e8041f1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Aug 2020 02:03:33 +0000 Subject: [PATCH 0901/1778] Bump assertj-core from 3.16.1 to 3.17.0 Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.16.1 to 3.17.0. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.16.1...assertj-core-3.17.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 0abfb263f..72979b78e 100644 --- a/pom.xml +++ b/pom.xml @@ -87,7 +87,7 @@ org.assertj assertj-core - 3.16.1 + 3.17.0 test From da5b0954e2aa8d6c62d90d57651d57092443464d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 24 Aug 2020 12:19:40 -0600 Subject: [PATCH 0902/1778] Update test --- .../issues/issue468/Issue468Test.java | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/src/test/java/io/github/classgraph/issues/issue468/Issue468Test.java b/src/test/java/io/github/classgraph/issues/issue468/Issue468Test.java index 4eb62411c..73ae09b47 100644 --- a/src/test/java/io/github/classgraph/issues/issue468/Issue468Test.java +++ b/src/test/java/io/github/classgraph/issues/issue468/Issue468Test.java @@ -36,23 +36,42 @@ import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; +import io.github.classgraph.ResourceList; import io.github.classgraph.ScanResult; /** * Issue468Test. */ public class Issue468Test { + /** Scan */ + private static void scan(final ClassGraph classGraph) { + try (ScanResult scanResult = classGraph.scan()) { + final ResourceList resources = scanResult.getAllResources(); + assertThat(resources.size()).isEqualTo(1); + assertThat(resources.getPaths()).containsExactly("innerfile"); + } + } + /** Test '+' signs in URLs. */ @Test - public void issue368Test() throws Exception { + public void testPlusSigns() throws Exception { final URL url = Issue468Test.class.getClassLoader().getResource("issue468/x+y/z+w.jar"); if (url == null) { throw new FileNotFoundException(); } - System.out.println(url); - try (ScanResult scanResult = new ClassGraph().acceptPackagesNonRecursive("").overrideClasspath(url) - .scan()) { - assertThat(scanResult.getAllResources().getPaths()).containsExactly("innerfile"); + scan(new ClassGraph().acceptPackagesNonRecursive("").overrideClasspath(url)); + } + + /** Test "file:" URIs as strings, with and without the scheme. */ + @Test + public void testFileURIs() throws Exception { + final URL url = Issue468Test.class.getClassLoader().getResource("issue468/x+y/z+w.jar"); + if (url == null) { + throw new FileNotFoundException(); } + final String urlStr = url.toString(); + scan(new ClassGraph().acceptPackagesNonRecursive("").overrideClasspath(urlStr)); + assertThat(urlStr).startsWith("file:"); + scan(new ClassGraph().acceptPackagesNonRecursive("").overrideClasspath(urlStr.substring(5))); } } From 1ae740e481b24924cca16c3993356782e3d667ee Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 24 Aug 2020 12:22:25 -0600 Subject: [PATCH 0903/1778] [maven-release-plugin] prepare release classgraph-4.8.88 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 72979b78e..5470cf9f2 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.88-SNAPSHOT + 4.8.88 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.88 From ab99a95b1e95e752b123a71acfd74007057685f1 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 24 Aug 2020 12:22:32 -0600 Subject: [PATCH 0904/1778] [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 5470cf9f2..bbc0dc102 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.88 + 4.8.89-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.88 + HEAD From a1bd671cf3f8321d3ff8b13e7162f38f7a41f671 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 24 Aug 2020 12:33:05 -0600 Subject: [PATCH 0905/1778] Handle Path classpath elements --- .../github/classgraph/classpath/ClasspathOrder.java | 13 +++++++++---- 1 file changed, 9 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 8c666edef..fcadfc325 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java @@ -294,11 +294,15 @@ public boolean addClasspathEntry(final Object pathElement, final ClassLoader cla if (pathElement == null) { return false; } - final String pathElementStr = FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, pathElement.toString()); + // 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); if (pathElementStr.isEmpty()) { return false; } - if (pathElement instanceof URL || pathElement instanceof URI || pathElement instanceof File) { + if (pathElement instanceof URL || pathElement instanceof URI || pathElement instanceof File + || pathElement instanceof Path) { if (!filter(pathElementStr)) { if (log != null) { log.log("Classpath element did not match filter criterion, skipping: " + pathElementStr); @@ -306,11 +310,12 @@ public boolean addClasspathEntry(final Object pathElement, final ClassLoader cla return false; } // For URL objects, use the object itself (so that URL scheme handling can be undertaken later); - // for URI objects, convert to URL; for File objects, use the toString result (the path) + // for URI and Path objects, convert to URL; for File objects, use the toString result (the path) final Object classpathElementObj; try { classpathElementObj = pathElement instanceof File ? pathElementStr - : pathElement instanceof URI ? ((URI) pathElement).toURL() : pathElement; + : pathElement instanceof Path ? ((Path) pathElement).toUri().toURL() + : pathElement instanceof URI ? ((URI) pathElement).toURL() : pathElement; } catch (final MalformedURLException e) { if (log != null) { log.log("Cannot convert from URI to URL: " + pathElementStr); From 4ca3159c5dd049ed278dac26b1389b19436e6687 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 24 Aug 2020 12:49:32 -0600 Subject: [PATCH 0906/1778] Support filtering classpath elements by URL (#468) --- .../java/io/github/classgraph/ClassGraph.java | 30 +++++++++++ .../classgraph/classpath/ClasspathOrder.java | 50 ++++++++++++------- .../github/classgraph/scanspec/ScanSpec.java | 22 ++++++-- 3 files changed, 80 insertions(+), 22 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index 1a46b6b9a..a02d9ad7d 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -485,6 +485,22 @@ public interface ClasspathElementFilter { boolean includeClasspathElement(String classpathElementPathStr); } + /** + * Add a classpath element URL filter. The includeClasspathElement method should return true if the {@link URL} + * passed to it corresponds to a classpath element that you want to scan. + */ + @FunctionalInterface + public interface ClasspathElementURLFilter { + /** + * Whether or not to include a given classpath element in the scan. + * + * @param classpathElementURL + * The {@link URL} of a classpath element. + * @return true if you want to scan the {@link URL}. + */ + boolean includeClasspathElement(URL classpathElementURL); + } + /** * Add a classpath element filter. The provided ClasspathElementFilter should return true if the path string * passed to it is a path you want to scan. @@ -499,6 +515,20 @@ public ClassGraph filterClasspathElements(final ClasspathElementFilter classpath return this; } + /** + * Add a classpath element filter. The provided ClasspathElementFilter should return true if the {@link URL} + * passed to it is a URL you want to scan. + * + * @param classpathElementURLFilter + * The filter function to use. This function should return true if the classpath element {@link URL} + * should be scanned, and false if not. + * @return this (for method chaining). + */ + public ClassGraph filterClasspathElements(final ClasspathElementURLFilter classpathElementURLFilter) { + scanSpec.filterClasspathElements(classpathElementURLFilter); + return this; + } + // ------------------------------------------------------------------------------------------------------------- /** 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 fcadfc325..23d95eff4 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java @@ -44,6 +44,7 @@ import java.util.Set; import io.github.classgraph.ClassGraph.ClasspathElementFilter; +import io.github.classgraph.ClassGraph.ClasspathElementURLFilter; import nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandlerRegistry; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.FastPathResolver; @@ -172,15 +173,21 @@ public Set getClasspathEntryUniqueResolvedPaths() { /** * Test to see if a classpath element has been filtered out by the user. - * + * + * @param classpathElementURL + * the classpath element URL * @param classpathElementPath * the classpath element path * @return true, if not filtered out */ - private boolean filter(final String classpathElementPath) { + private boolean filter(final URL classpathElementURL, final String classpathElementPath) { if (scanSpec.classpathElementFilters != null) { - for (final ClasspathElementFilter filter : scanSpec.classpathElementFilters) { - if (!filter.includeClasspathElement(classpathElementPath)) { + for (final Object filterObj : scanSpec.classpathElementFilters) { + if ((classpathElementURL != null && filterObj instanceof ClasspathElementURLFilter + && !((ClasspathElementURLFilter) filterObj).includeClasspathElement(classpathElementURL)) + || (classpathElementPath != null && filterObj instanceof ClasspathElementFilter + && !((ClasspathElementFilter) filterObj) + .includeClasspathElement(classpathElementPath))) { return false; } } @@ -301,9 +308,22 @@ public boolean addClasspathEntry(final Object pathElement, final ClassLoader cla if (pathElementStr.isEmpty()) { return false; } + URL pathElementURL; + 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() + : new URL(pathElement.toString()); + } catch (final MalformedURLException e1) { + if (log != null) { + log.log("Cannot convert from URI to URL: " + pathElementStr); + } + pathElementURL = null; + } if (pathElement instanceof URL || pathElement instanceof URI || pathElement instanceof File || pathElement instanceof Path) { - if (!filter(pathElementStr)) { + if (!filter(pathElementURL, pathElementStr)) { if (log != null) { log.log("Classpath element did not match filter criterion, skipping: " + pathElementStr); } @@ -312,16 +332,8 @@ public boolean addClasspathEntry(final Object pathElement, final ClassLoader cla // For URL objects, use the object itself (so that URL scheme handling can be undertaken later); // for URI and Path objects, convert to URL; for File objects, use the toString result (the path) final Object classpathElementObj; - try { - classpathElementObj = pathElement instanceof File ? pathElementStr - : pathElement instanceof Path ? ((Path) pathElement).toUri().toURL() - : pathElement instanceof URI ? ((URI) pathElement).toURL() : pathElement; - } catch (final MalformedURLException e) { - if (log != null) { - log.log("Cannot convert from URI to URL: " + pathElementStr); - } - return false; - } + classpathElementObj = pathElement instanceof File ? pathElementStr + : pathElement instanceof Path || pathElement instanceof URI ? pathElementURL : pathElement; if (addClasspathEntry(classpathElementObj, pathElementStr, classLoader, scanSpec)) { if (log != null) { log.log("Found classpath element: " + pathElementStr); @@ -346,8 +358,8 @@ public boolean addClasspathEntry(final Object pathElement, final ClassLoader cla : pathElementStr.substring(0, pathElementStr.length() - 2); final String baseDirPathResolved = FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, baseDirPath); - if (!filter(baseDirPath) - || (!baseDirPathResolved.equals(baseDirPath) && !filter(baseDirPathResolved))) { + if (!filter(pathElementURL, baseDirPath) || (!baseDirPathResolved.equals(baseDirPath) + && !filter(pathElementURL, baseDirPathResolved))) { if (log != null) { log.log("Classpath element did not match filter criterion, skipping: " + pathElementStr); @@ -419,8 +431,8 @@ public boolean addClasspathEntry(final Object pathElement, final ClassLoader cla // Non-wildcarded (standard) classpath element final String pathElementResolved = FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, pathElementStr); - if (!filter(pathElementStr) - || (!pathElementResolved.equals(pathElementStr) && !filter(pathElementResolved))) { + 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)); 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 c5cc67189..5694f7dd8 100644 --- a/src/main/java/nonapi/io/github/classgraph/scanspec/ScanSpec.java +++ b/src/main/java/nonapi/io/github/classgraph/scanspec/ScanSpec.java @@ -42,6 +42,7 @@ import java.util.Set; import io.github.classgraph.ClassGraph.ClasspathElementFilter; +import io.github.classgraph.ClassGraph.ClasspathElementURLFilter; import io.github.classgraph.ClassGraphException; import io.github.classgraph.ClassInfo; import io.github.classgraph.ModulePathInfo; @@ -209,7 +210,7 @@ public class ScanSpec { public List overrideClasspath; /** If non-null, a list of filter operations to apply to classpath elements. */ - public transient List classpathElementFilters; + public transient List classpathElementFilters; /** Whether to initialize classes when loading them. */ public boolean initializeLoadedClasses; @@ -306,8 +307,8 @@ public void addClasspathOverride(final Object overrideClasspathElement) { } /** - * Add a classpath element filter. The provided ClasspathElementFilter should return true if the path string - * passed to it is a path you want to scan. + * Add a classpath element filter. The provided {@link ClasspathElementFilter} should return true if the path + * string passed to it is a path you want to scan. * * @param classpathElementFilter * The classpath element filter to apply to all discovered classpath elements, to decide which should @@ -320,6 +321,21 @@ public void filterClasspathElements(final ClasspathElementFilter classpathElemen this.classpathElementFilters.add(classpathElementFilter); } + /** + * Add a classpath element URL filter. The provided {@link ClasspathElementURLFilter} should return true if the + * URL passed to it is one you want to scan. + * + * @param classpathElementURLFilter + * The classpath element URL filter to apply to all discovered classpath elements, to decide which + * should be scanned. + */ + public void filterClasspathElements(final ClasspathElementURLFilter classpathElementURLFilter) { + if (this.classpathElementFilters == null) { + this.classpathElementFilters = new ArrayList<>(2); + } + this.classpathElementFilters.add(classpathElementURLFilter); + } + /** * Add a ClassLoader to the list of ClassLoaders to scan. (This only works if overrideClasspath() is not * called.) From dc20462423d2179f187fc605bb7961f38a631eb8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 26 Aug 2020 03:01:45 +0000 Subject: [PATCH 0907/1778] Bump actions/setup-java from v1.4.1 to v1.4.2 Bumps [actions/setup-java](https://github.com/actions/setup-java) from v1.4.1 to v1.4.2. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/v1.4.1...8bb50d97d6b4d316daf284fdf8eafbfc988421fc) 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 411d0ad94..f906ed031 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Set up JDK - uses: actions/setup-java@v1.4.1 + uses: actions/setup-java@v1.4.2 with: java-version: ${{ matrix.java }} - name: print Java version From 7df619741df05e686ea1909dddfd90a70798403d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 26 Aug 2020 11:12:02 -0600 Subject: [PATCH 0908/1778] filterClasspathElements -> filterClasspathElementsByURL (#468) --- .../java/io/github/classgraph/ClassGraph.java | 2 +- .../classgraph/classpath/ClasspathOrder.java | 2 +- .../github/classgraph/scanspec/ScanSpec.java | 28 ++++++------------- 3 files changed, 11 insertions(+), 21 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index a02d9ad7d..40b145d0e 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -524,7 +524,7 @@ public ClassGraph filterClasspathElements(final ClasspathElementFilter classpath * should be scanned, and false if not. * @return this (for method chaining). */ - public ClassGraph filterClasspathElements(final ClasspathElementURLFilter classpathElementURLFilter) { + public ClassGraph filterClasspathElementsByURL(final ClasspathElementURLFilter classpathElementURLFilter) { scanSpec.filterClasspathElements(classpathElementURLFilter); return this; } 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 23d95eff4..489bf2022 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java @@ -317,7 +317,7 @@ public boolean addClasspathEntry(final Object pathElement, final ClassLoader cla : new URL(pathElement.toString()); } catch (final MalformedURLException e1) { if (log != null) { - log.log("Cannot convert from URI to URL: " + pathElementStr); + log.log("Cannot convert to URL: " + pathElement); } pathElementURL = null; } 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 5694f7dd8..5a33c8c22 100644 --- a/src/main/java/nonapi/io/github/classgraph/scanspec/ScanSpec.java +++ b/src/main/java/nonapi/io/github/classgraph/scanspec/ScanSpec.java @@ -307,33 +307,23 @@ public void addClasspathOverride(final Object overrideClasspathElement) { } /** - * Add a classpath element filter. The provided {@link ClasspathElementFilter} should return true if the path - * string passed to it is a path you want to scan. + * Add a classpath element filter. The provided {@link ClasspathElementFilter} or + * {@link ClasspathElementURLFilter} should return true if the path string or {@link URL} passed to it is a path + * that should be scanned. * - * @param classpathElementFilter + * @param filterLambda * The classpath element filter to apply to all discovered classpath elements, to decide which should * be scanned. */ - public void filterClasspathElements(final ClasspathElementFilter classpathElementFilter) { - if (this.classpathElementFilters == null) { - this.classpathElementFilters = new ArrayList<>(2); + public void filterClasspathElements(final Object filterLambda) { + if (!(filterLambda instanceof ClasspathElementFilter + || filterLambda instanceof ClasspathElementURLFilter)) { + throw new IllegalArgumentException(); } - this.classpathElementFilters.add(classpathElementFilter); - } - - /** - * Add a classpath element URL filter. The provided {@link ClasspathElementURLFilter} should return true if the - * URL passed to it is one you want to scan. - * - * @param classpathElementURLFilter - * The classpath element URL filter to apply to all discovered classpath elements, to decide which - * should be scanned. - */ - public void filterClasspathElements(final ClasspathElementURLFilter classpathElementURLFilter) { if (this.classpathElementFilters == null) { this.classpathElementFilters = new ArrayList<>(2); } - this.classpathElementFilters.add(classpathElementURLFilter); + this.classpathElementFilters.add(filterLambda); } /** From 3d2688c135949f45d59f66f2c72972c23cda370b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 26 Aug 2020 11:17:48 -0600 Subject: [PATCH 0909/1778] Bump version number back down --- pom.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/pom.xml b/pom.xml index bbc0dc102..03d6ffe80 100644 --- a/pom.xml +++ b/pom.xml @@ -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 - HEAD From 4f383bc4a44a3bf9859691e5130dec030ed1b00a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 26 Aug 2020 11:56:28 -0600 Subject: [PATCH 0910/1778] GitHub URL format has changed --- .../github/classgraph/issues/issue128/Issue128Test.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/java/io/github/classgraph/issues/issue128/Issue128Test.java b/src/test/java/io/github/classgraph/issues/issue128/Issue128Test.java index 5a829a20e..afd4fa4a7 100644 --- a/src/test/java/io/github/classgraph/issues/issue128/Issue128Test.java +++ b/src/test/java/io/github/classgraph/issues/issue128/Issue128Test.java @@ -45,14 +45,14 @@ * Issue128Test. */ public class Issue128Test { - /** The Constant SITE. */ - private static final String SITE = "https://githubraw.com/classgraph"; + /** The site. */ + private static final String SITE = "https://raw.githubusercontent.com/classgraph"; - /** The Constant JAR_URL. */ + /** The jar URL. */ private static final String JAR_URL = SITE + // "/classgraph/latest/src/test/resources/nested-jars-level1.zip"; - /** The Constant NESTED_JAR_URL. */ + /** The nested jar URL. */ private static final String NESTED_JAR_URL = // JAR_URL + "!level2.jar!level3.jar!classpath1/classpath2"; From cc210016da3ca7cd6601a46233d03e43c568829a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 26 Aug 2020 11:57:03 -0600 Subject: [PATCH 0911/1778] [maven-release-plugin] prepare release classgraph-4.8.89 --- pom.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 03d6ffe80..848b97083 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.89-SNAPSHOT + 4.8.89 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.89 + https://github.com/classgraph/classgraph/issues From 91e2572937ece30fd826e33f18a22d0606215ab8 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 26 Aug 2020 11:57:11 -0600 Subject: [PATCH 0912/1778] [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 848b97083..e1e317a42 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.89 + 4.8.90-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.89 + HEAD From 5a378fec3bf2f06027e00af3bfdfb2fbd8f1329e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 27 Aug 2020 02:06:38 +0000 Subject: [PATCH 0913/1778] Bump jmh-core from 1.25 to 1.25.1 Bumps jmh-core from 1.25 to 1.25.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 e1e317a42..106841adf 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ org.openjdk.jmh jmh-core - 1.25 + 1.25.1 test From b054829dccb10657e9d3f0a59c3193c159c0c68a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 27 Aug 2020 02:06:42 +0000 Subject: [PATCH 0914/1778] Bump jmh-generator-annprocess from 1.25 to 1.25.1 Bumps jmh-generator-annprocess from 1.25 to 1.25.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 e1e317a42..7aa6ab245 100644 --- a/pom.xml +++ b/pom.xml @@ -81,7 +81,7 @@ org.openjdk.jmh jmh-generator-annprocess - 1.25 + 1.25.1 test From b97123e8faaafdde5c5847b7516d938c3c70b853 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 Aug 2020 02:02:38 +0000 Subject: [PATCH 0915/1778] Bump assertj-core from 3.17.0 to 3.17.1 Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.17.0 to 3.17.1. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.17.0...assertj-core-3.17.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 f048aeddb..c86d15f20 100644 --- a/pom.xml +++ b/pom.xml @@ -87,7 +87,7 @@ org.assertj assertj-core - 3.17.0 + 3.17.1 test From 1cee6e1cf9b3114e66e46019098f5321b74b04a2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 4 Sep 2020 02:00:56 +0000 Subject: [PATCH 0916/1778] Bump jmh-core from 1.25.1 to 1.25.2 Bumps jmh-core from 1.25.1 to 1.25.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 f048aeddb..a3fc8fd56 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ org.openjdk.jmh jmh-core - 1.25.1 + 1.25.2 test From 5bbdd45c7090c6a82c3cd00cfdb203c35888104e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 4 Sep 2020 02:00:56 +0000 Subject: [PATCH 0917/1778] Bump jmh-generator-annprocess from 1.25.1 to 1.25.2 Bumps jmh-generator-annprocess from 1.25.1 to 1.25.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 f048aeddb..644882e33 100644 --- a/pom.xml +++ b/pom.xml @@ -81,7 +81,7 @@ org.openjdk.jmh jmh-generator-annprocess - 1.25.1 + 1.25.2 test From b22a007ee366171e9c7f2d253f46220997613730 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Sep 2020 02:00:59 +0000 Subject: [PATCH 0918/1778] Bump assertj-core from 3.17.1 to 3.17.2 Bumps [assertj-core](https://github.com/joel-costigliola/assertj-core) from 3.17.1 to 3.17.2. - [Release notes](https://github.com/joel-costigliola/assertj-core/releases) - [Commits](https://github.com/joel-costigliola/assertj-core/compare/assertj-core-3.17.1...assertj-core-3.17.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 e6cbf2025..4cb83ebbc 100644 --- a/pom.xml +++ b/pom.xml @@ -87,7 +87,7 @@ org.assertj assertj-core - 3.17.1 + 3.17.2 test From a1b2d31d9d5e4dd1850bb8273ca610edad429ef7 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 9 Sep 2020 17:28:58 -0600 Subject: [PATCH 0919/1778] Solve path separator issue on Windows (#477) --- src/main/java/io/github/classgraph/ClasspathElementPathDir.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementPathDir.java b/src/main/java/io/github/classgraph/ClasspathElementPathDir.java index 85830c51e..a4379a21c 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementPathDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementPathDir.java @@ -422,7 +422,7 @@ private void scanPathRecursively(final Path path, final LogNode log) { // Process files in dir before recursing if (Files.isRegularFile(subPath)) { final Path subPathRelative = classpathEltPath.relativize(subPath); - final String subPathRelativeStr = subPathRelative.toString(); + final String subPathRelativeStr = FastPathResolver.resolve(subPathRelative.toString()); // 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 && subPathRelativeStr.endsWith(".class") From a40b6aba3bdf041232291a480f9fe08221084b50 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 9 Sep 2020 17:30:30 -0600 Subject: [PATCH 0920/1778] [maven-release-plugin] prepare release classgraph-4.8.90 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e1e317a42..9775c2d9f 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.90-SNAPSHOT + 4.8.90 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.90 From 80b65a02f082aa320d8024daf2b8e64d2d960983 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 9 Sep 2020 17:31:03 -0600 Subject: [PATCH 0921/1778] [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 c88260ef2..8901b77d0 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.90 + 4.8.91-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From cc48fc4db8b8627c350b69c6b4976870a9ae8aa8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2020 02:04:08 +0000 Subject: [PATCH 0922/1778] Bump junit-jupiter from 5.6.2 to 5.7.0 Bumps [junit-jupiter](https://github.com/junit-team/junit5) from 5.6.2 to 5.7.0. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.6.2...r5.7.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 8901b77d0..857f743bb 100644 --- a/pom.xml +++ b/pom.xml @@ -69,7 +69,7 @@ org.junit.jupiter junit-jupiter - 5.6.2 + 5.7.0 test From b9b22c9d9dc883bd70a6f64a17e78d3e465547c7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Oct 2020 03:03:05 +0000 Subject: [PATCH 0923/1778] Bump actions/setup-java from v1.4.2 to v1.4.3 Bumps [actions/setup-java](https://github.com/actions/setup-java) from v1.4.2 to v1.4.3. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/v1.4.2...d202f5dbf7256730fb690ec59f6381650114feb2) 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 f906ed031..bdfd73250 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Set up JDK - uses: actions/setup-java@v1.4.2 + uses: actions/setup-java@v1.4.3 with: java-version: ${{ matrix.java }} - name: print Java version From f8550d72afbe1eca9b5bda179b55b69f81db6368 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Oct 2020 02:06:22 +0000 Subject: [PATCH 0924/1778] Bump jmh-generator-annprocess from 1.25.2 to 1.26 Bumps jmh-generator-annprocess from 1.25.2 to 1.26. Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 857f743bb..49e39897f 100644 --- a/pom.xml +++ b/pom.xml @@ -81,7 +81,7 @@ org.openjdk.jmh jmh-generator-annprocess - 1.25.2 + 1.26 test From ca1e18ee8705b3b222e00874e0386b3df2a8a2e7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Oct 2020 02:06:26 +0000 Subject: [PATCH 0925/1778] Bump jmh-core from 1.25.2 to 1.26 Bumps jmh-core from 1.25.2 to 1.26. Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 857f743bb..d37609566 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ org.openjdk.jmh jmh-core - 1.25.2 + 1.26 test From f35026ba95242fd1e473de73ac26ca2fadf6ead6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 12 Nov 2020 02:01:09 +0000 Subject: [PATCH 0926/1778] Bump assertj-core from 3.17.2 to 3.18.1 Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.17.2 to 3.18.1. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.17.2...assertj-core-3.18.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 f2dd9a455..78d4eb6c6 100644 --- a/pom.xml +++ b/pom.xml @@ -87,7 +87,7 @@ org.assertj assertj-core - 3.17.2 + 3.18.1 test From 6c88548ead051c833d88e84659b24c54f939a51e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 27 Nov 2020 23:18:05 -0700 Subject: [PATCH 0927/1778] Add automatically-stripped package root prefixes to classpath elt URI --- .../java/io/github/classgraph/ClassInfo.java | 12 ++-- .../github/classgraph/ClasspathElement.java | 8 +++ .../classgraph/ClasspathElementFileDir.java | 7 +++ .../classgraph/ClasspathElementModule.java | 10 ++++ .../classgraph/ClasspathElementPathDir.java | 5 ++ .../classgraph/ClasspathElementZip.java | 57 ++++++++++++++++++- .../java/io/github/classgraph/ScanResult.java | 14 ++--- 7 files changed, 95 insertions(+), 18 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index ced9749a5..4d668f818 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -2607,10 +2607,9 @@ public String getTypeSignatureStr() { * if the classpath element does not have a valid URI (e.g. for modules whose location URI is null). */ public URI getClasspathElementURI() { - if (classpathElement == null) { - throw new IllegalArgumentException("Classpath element is not known for this classpath element"); - } - return classpathElement.getURI(); + // Calling classfileResource.getClasspathElementURI() rather than classpathElement.getURI() will append + // any automatically-stripped package root prefix + return classfileResource.getClasspathElementURI(); } /** @@ -2625,11 +2624,8 @@ public URI getClasspathElementURI() { * a {@code jrt:/} scheme). */ public URL getClasspathElementURL() { - if (classpathElement == null) { - throw new IllegalArgumentException("Classpath element is not known for this classpath element"); - } try { - return classpathElement.getURI().toURL(); + return getClasspathElementURI().toURL(); } catch (final MalformedURLException e) { throw new IllegalArgumentException("Could not get classpath element URL", e); } diff --git a/src/main/java/io/github/classgraph/ClasspathElement.java b/src/main/java/io/github/classgraph/ClasspathElement.java index f49e2991c..69cdd00d8 100644 --- a/src/main/java/io/github/classgraph/ClasspathElement.java +++ b/src/main/java/io/github/classgraph/ClasspathElement.java @@ -381,6 +381,14 @@ abstract void open(final WorkQueue workQueue, final LogN */ abstract URI getURI(); + /** + * Get the URI for this classpath element, and the URIs for any automatic nested package prefixes (e.g. + * "spring-boot.jar/BOOT-INF/classes") within this jarfile. + * + * @return the URI for the classpath element. + */ + abstract List getAllURIs(); + /** * Get the file for this classpath element, or null if this is a module with a "jrt:" URI. * diff --git a/src/main/java/io/github/classgraph/ClasspathElementFileDir.java b/src/main/java/io/github/classgraph/ClasspathElementFileDir.java index 10ae15d59..21dfa4bfc 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementFileDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementFileDir.java @@ -37,7 +37,9 @@ 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; @@ -526,6 +528,11 @@ URI getURI() { return packageRootDir.toURI(); } + @Override + List getAllURIs() { + return Collections.singletonList(getURI()); + } + /** * Return the classpath element directory as a String. * diff --git a/src/main/java/io/github/classgraph/ClasspathElementModule.java b/src/main/java/io/github/classgraph/ClasspathElementModule.java index 897a4b535..15af59210 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementModule.java +++ b/src/main/java/io/github/classgraph/ClasspathElementModule.java @@ -34,6 +34,7 @@ import java.net.URI; import java.nio.ByteBuffer; import java.nio.file.attribute.PosixFilePermission; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -54,6 +55,10 @@ import nonapi.io.github.classgraph.utils.VersionFinder; /** A module classpath element. */ +/** + * @author luke + * + */ class ClasspathElementModule extends ClasspathElement { /** The module ref. */ @@ -431,6 +436,11 @@ URI getURI() { return uri; } + @Override + List getAllURIs() { + return Collections.singletonList(getURI()); + } + /* (non-Javadoc) * @see io.github.classgraph.ClasspathElement#getFile() */ diff --git a/src/main/java/io/github/classgraph/ClasspathElementPathDir.java b/src/main/java/io/github/classgraph/ClasspathElementPathDir.java index a4379a21c..7899bb437 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementPathDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementPathDir.java @@ -564,6 +564,11 @@ URI getURI() { return packageRootPath.toUri(); } + @Override + List getAllURIs() { + return Collections.singletonList(getURI()); + } + /** * Return the classpath element directory as a String. * diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index 67768e3da..27965a29b 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -38,7 +38,10 @@ import java.nio.ByteBuffer; import java.nio.file.Path; import java.nio.file.attribute.PosixFilePermission; +import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; +import java.util.List; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -78,6 +81,8 @@ class ClasspathElementZip extends ClasspathElement { private String zipFilePath; /** A map from relative path to {@link Resource} for non-rejected zip entries. */ private final ConcurrentHashMap relativePathToResource = new ConcurrentHashMap<>(); + /** A list of all automatic package root prefixes found as prefixes of paths within this zipfile. */ + private final Set strippedAutomaticPackageRootPrefixes = new HashSet<>(); /** The nested jar handler. */ private final NestedJarHandler nestedJarHandler; /** @@ -304,7 +309,8 @@ void open(final WorkQueue workQueue, final LogNode log) * the path relative to package root * @return the resource */ - private Resource newResource(final FastZipEntry zipEntry, final String pathRelativeToPackageRoot) { + private Resource newResource(final FastZipEntry zipEntry, final String strippedPackageRootPrefix, + final String pathRelativeToPackageRoot) { return new Resource(this, zipEntry.uncompressedSize) { /** True if the resource is open. */ protected AtomicBoolean isOpen = new AtomicBoolean(); @@ -323,6 +329,20 @@ public String getPathRelativeToClasspathElement() { return zipEntry.entryName; } + @Override + public URI getClasspathElementURI() { + final URI classpathElementURI = super.getClasspathElementURI(); + if (strippedPackageRootPrefix.isEmpty()) { + return classpathElementURI; + } else { + try { + return new URI(classpathElementURI.toString() + "!/" + strippedPackageRootPrefix); + } catch (final URISyntaxException e) { + throw new IllegalArgumentException("Could not construct valid URI for resource"); + } + } + } + @Override public long getLastModified() { return zipEntry.getLastModifiedTimeMillis(); @@ -555,6 +575,7 @@ void scanPaths(final LogNode log) { // Strip the package root prefix from the relative path // N.B. these semantics should mirror those in getResource() + String strippedPackageRootPrefix = ""; if (!packageRootPrefix.isEmpty()) { relativePath = relativePath.substring(packageRootPrefix.length()); } else { @@ -563,6 +584,14 @@ void scanPaths(final LogNode log) { if (relativePath.startsWith(ClassLoaderHandlerRegistry.AUTOMATIC_PACKAGE_ROOT_PREFIXES[i])) { relativePath = relativePath .substring(ClassLoaderHandlerRegistry.AUTOMATIC_PACKAGE_ROOT_PREFIXES[i].length()); + strippedAutomaticPackageRootPrefixes + .add(ClassLoaderHandlerRegistry.AUTOMATIC_PACKAGE_ROOT_PREFIXES[i]); + if (strippedPackageRootPrefix.isEmpty()) { + strippedPackageRootPrefix = ClassLoaderHandlerRegistry.AUTOMATIC_PACKAGE_ROOT_PREFIXES[i]; + } else { + strippedPackageRootPrefix += "/" + + ClassLoaderHandlerRegistry.AUTOMATIC_PACKAGE_ROOT_PREFIXES[i]; + } } } } @@ -593,7 +622,7 @@ void scanPaths(final LogNode log) { } // Add the ZipEntry path as a Resource - final Resource resource = newResource(zipEntry, relativePath); + final Resource resource = newResource(zipEntry, strippedPackageRootPrefix, relativePath); if (relativePathToResource.putIfAbsent(relativePath, resource) == null) { // If resource is accepted if (parentMatchStatus == ScanSpecPathMatch.HAS_ACCEPTED_PATH_PREFIX @@ -663,6 +692,30 @@ URI getURI() { } } + /** + * Return URI for classpath element, plus URIs for any stripped nested automatic package root prefixes, e.g. + * "/BOOT-INF/classes". + */ + @Override + List getAllURIs() { + if (strippedAutomaticPackageRootPrefixes.isEmpty()) { + return Collections.singletonList(getURI()); + } else { + final URI uri = getURI(); + final List uris = new ArrayList<>(); + uris.add(uri); + final String uriStr = uri.toString(); + for (final String prefix : strippedAutomaticPackageRootPrefixes) { + try { + uris.add(new URI(uriStr + "!/" + prefix)); + } catch (final URISyntaxException e) { + // Ignore + } + } + return uris; + } + } + /** * Get the {@link File} for the outermost zipfile of this classpath element. * diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index e09bd468d..d58271a1d 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -379,9 +379,10 @@ public List getClasspathURIs() { final List classpathElementOrderURIs = new ArrayList<>(); for (final ClasspathElement classpathElement : classpathOrder) { try { - final URI uri = classpathElement.getURI(); - if (uri != null) { - classpathElementOrderURIs.add(uri); + for (final URI uri : classpathElement.getAllURIs()) { + if (uri != null) { + classpathElementOrderURIs.add(uri); + } } } catch (final IllegalArgumentException e) { // Skip null location URIs @@ -402,12 +403,9 @@ public List getClasspathURLs() { throw new IllegalArgumentException("Cannot use a ScanResult after it has been closed"); } final List classpathElementOrderURLs = new ArrayList<>(); - for (final ClasspathElement classpathElement : classpathOrder) { + for (final URI uri : getClasspathURIs()) { try { - final URI uri = classpathElement.getURI(); - if (uri != null) { - classpathElementOrderURLs.add(uri.toURL()); - } + classpathElementOrderURLs.add(uri.toURL()); } catch (final IllegalArgumentException | MalformedURLException e) { // Skip "jrt:" URIs and malformed URLs } From 342e16cdeaea7a3a52823f89b3a1a48679b8e6a7 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 27 Nov 2020 23:21:02 -0700 Subject: [PATCH 0928/1778] [maven-release-plugin] prepare release classgraph-4.8.91 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 78d4eb6c6..43adc172a 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.91-SNAPSHOT + 4.8.91 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.90 + classgraph-4.8.91 From efefa6b386f647a674bc2731d9a35e51c6fe72f8 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 27 Nov 2020 23:21:09 -0700 Subject: [PATCH 0929/1778] [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 43adc172a..e5f085d6b 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.91 + 4.8.92-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.91 + classgraph-4.8.90 From e7d9b142f4dcc22e16eb5ec9afceef48e33071c6 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 27 Nov 2020 23:36:26 -0700 Subject: [PATCH 0930/1778] Remove final slash from package root at end of URIs --- .../classgraph/ClasspathElementZip.java | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index 27965a29b..8dc84d7ce 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -581,16 +581,21 @@ void scanPaths(final LogNode log) { } else { // Strip any package root prefix from the relative path for (int i = 0; i < ClassLoaderHandlerRegistry.AUTOMATIC_PACKAGE_ROOT_PREFIXES.length; i++) { - if (relativePath.startsWith(ClassLoaderHandlerRegistry.AUTOMATIC_PACKAGE_ROOT_PREFIXES[i])) { - relativePath = relativePath - .substring(ClassLoaderHandlerRegistry.AUTOMATIC_PACKAGE_ROOT_PREFIXES[i].length()); - strippedAutomaticPackageRootPrefixes - .add(ClassLoaderHandlerRegistry.AUTOMATIC_PACKAGE_ROOT_PREFIXES[i]); + final String packageRoot = ClassLoaderHandlerRegistry.AUTOMATIC_PACKAGE_ROOT_PREFIXES[i]; + if (relativePath.startsWith(packageRoot)) { + // Strip package root + relativePath = relativePath.substring(packageRoot.length()); + // Strip final slash from package root + String packageRootWithoutFinalSlash = packageRoot.endsWith("/") + ? packageRoot.substring(0, packageRoot.length() - 1) + : packageRoot; + // Store package root for use by getAllURIs() + strippedAutomaticPackageRootPrefixes.add(packageRootWithoutFinalSlash); + // if (strippedPackageRootPrefix.isEmpty()) { - strippedPackageRootPrefix = ClassLoaderHandlerRegistry.AUTOMATIC_PACKAGE_ROOT_PREFIXES[i]; + strippedPackageRootPrefix = packageRootWithoutFinalSlash; } else { - strippedPackageRootPrefix += "/" - + ClassLoaderHandlerRegistry.AUTOMATIC_PACKAGE_ROOT_PREFIXES[i]; + strippedPackageRootPrefix += "/" + packageRootWithoutFinalSlash; } } } From ec44a2a6191e12f64ddd59fc7c4fa6f33d513ee2 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 27 Nov 2020 23:37:26 -0700 Subject: [PATCH 0931/1778] Source > Cleanup --- src/main/java/io/github/classgraph/ClasspathElementZip.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index 8dc84d7ce..779babfad 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -586,7 +586,7 @@ void scanPaths(final LogNode log) { // Strip package root relativePath = relativePath.substring(packageRoot.length()); // Strip final slash from package root - String packageRootWithoutFinalSlash = packageRoot.endsWith("/") + final String packageRootWithoutFinalSlash = packageRoot.endsWith("/") ? packageRoot.substring(0, packageRoot.length() - 1) : packageRoot; // Store package root for use by getAllURIs() From 309ff1990459bdfa76e7fc88799f990a2a7eff1e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 27 Nov 2020 23:37:50 -0700 Subject: [PATCH 0932/1778] [maven-release-plugin] prepare release classgraph-4.8.92 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e5f085d6b..7287c03c6 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.92-SNAPSHOT + 4.8.92 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.90 + classgraph-4.8.92 From cccd8b4cbdf19eac804dea95db8dad67a324ce5a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 27 Nov 2020 23:37:56 -0700 Subject: [PATCH 0933/1778] [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 7287c03c6..eb8fbe5f3 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.92 + 4.8.93-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.92 + classgraph-4.8.90 From 16a53f628e601f75ae19627190a526b35211c55b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 1 Dec 2020 23:16:43 -0700 Subject: [PATCH 0934/1778] Don't call findLoadedClass, it's the job of loadClass, not findClass --- .../java/io/github/classgraph/ClassGraphClassLoader.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java index c4ab044f3..4d12a2ac7 100644 --- a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java +++ b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java @@ -130,12 +130,6 @@ class ClassGraphClassLoader extends ClassLoader { @Override protected Class findClass(final String className) throws ClassNotFoundException, LinkageError, SecurityException { - // Use cached class, if it is already loaded - final Class loadedClass = findLoadedClass(className); - if (loadedClass != null) { - return loadedClass; - } - // Try environment classloader(s) if (!environmentClassLoaderDelegationOrder.isEmpty()) { for (final ClassLoader envClassLoader : environmentClassLoaderDelegationOrder) { @@ -226,7 +220,7 @@ protected Class findClass(final String className) } throw new ClassNotFoundException("Could not load classfile for class " + className); } - + /* (non-Javadoc) * @see java.lang.ClassLoader#getResource(java.lang.String) */ From 3eda4b16bddbdf52ed83f646cf6eda9c00907189 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 1 Dec 2020 23:24:17 -0700 Subject: [PATCH 0935/1778] If overrideClassLoaders is set, don't try other classloaders --- .../classgraph/ClassGraphClassLoader.java | 51 ++++++++++++------- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java index 4d12a2ac7..c9a8bef52 100644 --- a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java +++ b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java @@ -36,6 +36,7 @@ import java.util.Collections; import java.util.Enumeration; import java.util.LinkedHashSet; +import java.util.List; import java.util.Set; import nonapi.io.github.classgraph.scanspec.ScanSpec; @@ -53,8 +54,11 @@ class ClassGraphClassLoader extends ClassLoader { /** The ordered set of environment classloaders to try delegating to. */ private final Set environmentClassLoaderDelegationOrder; + /** Any override classloader(s). */ + private final List overrideClassLoaders; + /** The ordered set of overridden or added classloaders to try delegating to. */ - private final Set overriddenOrAddedClassLoaderDelegationOrder; + private final Set addedClassLoaderDelegationOrder; /** * Constructor. @@ -77,6 +81,8 @@ class ClassGraphClassLoader extends ClassLoader { final boolean clasloadersAdded = scanSpec.addedClassLoaders != null && !scanSpec.addedClassLoaders.isEmpty(); + overrideClassLoaders = classloadersOverridden ? scanSpec.overrideClassLoaders : null; + // Uniquified order of classloaders to delegate to environmentClassLoaderDelegationOrder = new LinkedHashSet<>(); @@ -107,21 +113,18 @@ class ClassGraphClassLoader extends ClassLoader { environmentClassLoaderDelegationOrder.add(classpathClassLoader); } - // If classloaders are overridden or added, try loading through those classloaders - overriddenOrAddedClassLoaderDelegationOrder = new LinkedHashSet<>(); - if (classloadersOverridden) { - overriddenOrAddedClassLoaderDelegationOrder.addAll(scanSpec.overrideClassLoaders); - } + // If classloaders were added, try loading through those classloaders + addedClassLoaderDelegationOrder = new LinkedHashSet<>(); if (clasloadersAdded) { - overriddenOrAddedClassLoaderDelegationOrder.addAll(scanSpec.addedClassLoaders); + addedClassLoaderDelegationOrder.addAll(scanSpec.addedClassLoaders); } if (!classpathOverridden) { // If the classpath was not overridden, now that override classloaders have been attempted and failed, // try to load the class from the classpath URLs before attempting direct classloading from resources - overriddenOrAddedClassLoaderDelegationOrder.add(classpathClassLoader); + addedClassLoaderDelegationOrder.add(classpathClassLoader); } // Remove duplicates - overriddenOrAddedClassLoaderDelegationOrder.removeAll(environmentClassLoaderDelegationOrder); + addedClassLoaderDelegationOrder.removeAll(environmentClassLoaderDelegationOrder); } /* (non-Javadoc) @@ -130,6 +133,18 @@ class ClassGraphClassLoader extends ClassLoader { @Override protected Class findClass(final String className) throws ClassNotFoundException, LinkageError, SecurityException { + // If overrideClassLoaders is set, only use the override loaders + if (overrideClassLoaders != null) { + for (final ClassLoader overrideClassLoader : overrideClassLoaders) { + try { + return Class.forName(className, initializeLoadedClasses, overrideClassLoader); + } catch (ClassNotFoundException | LinkageError e) { + // Ignore + } + } + throw new ClassNotFoundException("Could not load classfile for class " + className); + } + // Try environment classloader(s) if (!environmentClassLoaderDelegationOrder.isEmpty()) { for (final ClassLoader envClassLoader : environmentClassLoaderDelegationOrder) { @@ -174,8 +189,8 @@ protected Class findClass(final String className) } // Try overridden or added classloader(s) - if (!overriddenOrAddedClassLoaderDelegationOrder.isEmpty()) { - for (final ClassLoader additionalClassLoader : overriddenOrAddedClassLoaderDelegationOrder) { + if (!addedClassLoaderDelegationOrder.isEmpty()) { + for (final ClassLoader additionalClassLoader : addedClassLoaderDelegationOrder) { if (additionalClassLoader != classInfoClassLoader) { try { return Class.forName(className, initializeLoadedClasses, additionalClassLoader); @@ -220,7 +235,7 @@ protected Class findClass(final String className) } throw new ClassNotFoundException("Could not load classfile for class " + className); } - + /* (non-Javadoc) * @see java.lang.ClassLoader#getResource(java.lang.String) */ @@ -239,8 +254,8 @@ public URL getResource(final String path) { } // Try loading resource from overridden or added classloader(s) - if (!overriddenOrAddedClassLoaderDelegationOrder.isEmpty()) { - for (final ClassLoader additionalClassLoader : overriddenOrAddedClassLoaderDelegationOrder) { + if (!addedClassLoaderDelegationOrder.isEmpty()) { + for (final ClassLoader additionalClassLoader : addedClassLoaderDelegationOrder) { final URL resource = additionalClassLoader.getResource(path); if (resource != null) { return resource; @@ -276,8 +291,8 @@ public Enumeration getResources(final String path) throws IOException { } // Try loading resources from overridden or added classloader(s) - if (!overriddenOrAddedClassLoaderDelegationOrder.isEmpty()) { - for (final ClassLoader additionalClassLoader : overriddenOrAddedClassLoaderDelegationOrder) { + if (!addedClassLoaderDelegationOrder.isEmpty()) { + for (final ClassLoader additionalClassLoader : addedClassLoaderDelegationOrder) { final Enumeration resources = additionalClassLoader.getResources(path); if (resources != null && resources.hasMoreElements()) { return resources; @@ -326,8 +341,8 @@ public InputStream getResourceAsStream(final String path) { } // Try opening resource from overridden or added classloader(s) - if (!overriddenOrAddedClassLoaderDelegationOrder.isEmpty()) { - for (final ClassLoader additionalClassLoader : overriddenOrAddedClassLoaderDelegationOrder) { + if (!addedClassLoaderDelegationOrder.isEmpty()) { + for (final ClassLoader additionalClassLoader : addedClassLoaderDelegationOrder) { final InputStream inputStream = additionalClassLoader.getResourceAsStream(path); if (inputStream != null) { return inputStream; From c77fbff9beb53fa1baaf602eba72b992f4ceda05 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 1 Dec 2020 23:24:30 -0700 Subject: [PATCH 0936/1778] Update previous commit --- src/main/java/io/github/classgraph/ClassGraphClassLoader.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java index c9a8bef52..e8f958e0d 100644 --- a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java +++ b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java @@ -145,7 +145,7 @@ protected Class findClass(final String className) throw new ClassNotFoundException("Could not load classfile for class " + className); } - // Try environment classloader(s) + // Try environment classloader(s) first, since this is the usual default if (!environmentClassLoaderDelegationOrder.isEmpty()) { for (final ClassLoader envClassLoader : environmentClassLoaderDelegationOrder) { try { @@ -188,7 +188,7 @@ protected Class findClass(final String className) } } - // Try overridden or added classloader(s) + // Try any added classloader(s) if (!addedClassLoaderDelegationOrder.isEmpty()) { for (final ClassLoader additionalClassLoader : addedClassLoaderDelegationOrder) { if (additionalClassLoader != classInfoClassLoader) { From b32aa0ffd48d886918941bed204f3d69ee253e84 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 1 Dec 2020 23:39:32 -0700 Subject: [PATCH 0937/1778] Clean up classloader delegation logic --- .../classgraph/ClassGraphClassLoader.java | 66 +++++++++++-------- 1 file changed, 39 insertions(+), 27 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java index e8f958e0d..026464937 100644 --- a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java +++ b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java @@ -52,13 +52,16 @@ class ClassGraphClassLoader extends ClassLoader { private final boolean initializeLoadedClasses; /** The ordered set of environment classloaders to try delegating to. */ - private final Set environmentClassLoaderDelegationOrder; + private Set environmentClassLoaderDelegationOrder; /** Any override classloader(s). */ - private final List overrideClassLoaders; + private List overrideClassLoaders; + + /** A {@link URLClassLoader} consisting of URLs on the classpath. */ + private final URLClassLoader classpathClassLoader; /** The ordered set of overridden or added classloaders to try delegating to. */ - private final Set addedClassLoaderDelegationOrder; + private Set addedClassLoaderDelegationOrder; /** * Constructor. @@ -81,15 +84,11 @@ class ClassGraphClassLoader extends ClassLoader { final boolean clasloadersAdded = scanSpec.addedClassLoaders != null && !scanSpec.addedClassLoaders.isEmpty(); - overrideClassLoaders = classloadersOverridden ? scanSpec.overrideClassLoaders : null; - - // Uniquified order of classloaders to delegate to - environmentClassLoaderDelegationOrder = new LinkedHashSet<>(); - // 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) + environmentClassLoaderDelegationOrder = new LinkedHashSet<>(); environmentClassLoaderDelegationOrder.add(null); // Try environment classloaders @@ -102,29 +101,33 @@ class ClassGraphClassLoader extends ClassLoader { } } - // If the classpath is overridden, try loading class from the classpath URLs (this is done before - // checking classloader overrides, since classpath override takes precedence over classloader - // overrides in the ClasspathFinder class). Some of these URLs might be invalid if the ScanResult - // has been closed (e.g. in the rare case that an inner jar had to be extracted to a temporary file - // on disk). - final URLClassLoader classpathClassLoader = new URLClassLoader( - scanResult.getClasspathURLs().toArray(new URL[0])); - if (classpathOverridden) { - environmentClassLoaderDelegationOrder.add(classpathClassLoader); + // Create classloader from URLs on classpath + final List classpathURLs = scanResult.getClasspathURLs(); + classpathClassLoader = classpathURLs.isEmpty() ? null + : new URLClassLoader(classpathURLs.toArray(new URL[0])); + + // If the classloaders were overridden, just use the override classloaders, and then fail if the + // class couldn't be found. + // + // If the classpath is overridden, and classloaders are not overridden, try loading class from + // classpath URLs, as the override classloader, then fail if the class couldn't be found. + // + // N.B. Some classpath URLs might be invalid if the ScanResult has been closed (e.g. in the rare + // case that an inner jar had to be extracted to a temporary file on disk). + overrideClassLoaders = classloadersOverridden ? scanSpec.overrideClassLoaders : null; + if (overrideClassLoaders == null && classpathClassLoader != null) { + overrideClassLoaders = Collections.singletonList(classpathClassLoader); } // If classloaders were added, try loading through those classloaders - addedClassLoaderDelegationOrder = new LinkedHashSet<>(); if (clasloadersAdded) { + addedClassLoaderDelegationOrder = new LinkedHashSet<>(); addedClassLoaderDelegationOrder.addAll(scanSpec.addedClassLoaders); + // Remove duplicates + if (environmentClassLoaderDelegationOrder != null) { + addedClassLoaderDelegationOrder.removeAll(environmentClassLoaderDelegationOrder); + } } - if (!classpathOverridden) { - // If the classpath was not overridden, now that override classloaders have been attempted and failed, - // try to load the class from the classpath URLs before attempting direct classloading from resources - addedClassLoaderDelegationOrder.add(classpathClassLoader); - } - // Remove duplicates - addedClassLoaderDelegationOrder.removeAll(environmentClassLoaderDelegationOrder); } /* (non-Javadoc) @@ -146,7 +149,7 @@ protected Class findClass(final String className) } // Try environment classloader(s) first, since this is the usual default - if (!environmentClassLoaderDelegationOrder.isEmpty()) { + if (environmentClassLoaderDelegationOrder != null && !environmentClassLoaderDelegationOrder.isEmpty()) { for (final ClassLoader envClassLoader : environmentClassLoaderDelegationOrder) { try { return Class.forName(className, initializeLoadedClasses, envClassLoader); @@ -188,8 +191,17 @@ protected Class findClass(final String className) } } + // Try loading from classpath URLs + if (classpathClassLoader != null) { + try { + return Class.forName(className, initializeLoadedClasses, classpathClassLoader); + } catch (ClassNotFoundException | LinkageError e) { + // Ignore + } + } + // Try any added classloader(s) - if (!addedClassLoaderDelegationOrder.isEmpty()) { + if (addedClassLoaderDelegationOrder != null && !addedClassLoaderDelegationOrder.isEmpty()) { for (final ClassLoader additionalClassLoader : addedClassLoaderDelegationOrder) { if (additionalClassLoader != classInfoClassLoader) { try { From 012e6c39c77b4af833becb1e09cda72c9d99c377 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 2 Dec 2020 00:47:14 -0700 Subject: [PATCH 0938/1778] Make ClassGraph extend `URLClassLoader` (#485) --- .../io/github/classgraph/ClassGraphClassLoader.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java index 026464937..47f286cfa 100644 --- a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java +++ b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java @@ -33,6 +33,7 @@ import java.net.URL; import java.net.URLClassLoader; import java.nio.ByteBuffer; +import java.security.ProtectionDomain; import java.util.Collections; import java.util.Enumeration; import java.util.LinkedHashSet; @@ -43,7 +44,7 @@ import nonapi.io.github.classgraph.utils.JarUtils; /** {@link ClassLoader} for classes found by ClassGraph during scanning. */ -class ClassGraphClassLoader extends ClassLoader { +class ClassGraphClassLoader extends URLClassLoader { /** The scan result. */ private final ScanResult scanResult; @@ -234,7 +235,7 @@ protected Class findClass(final String className) // 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, null); + return defineClass(className, resourceByteBuffer, (ProtectionDomain) null); } finally { resource.close(); } @@ -248,6 +249,12 @@ protected Class findClass(final String className) throw new ClassNotFoundException("Could not load classfile for class " + className); } + /** Get classpath URLs. */ + @Override + public URL[] getURLs() { + return scanResult.getClasspathURLs().toArray(new URL[0]); + } + /* (non-Javadoc) * @see java.lang.ClassLoader#getResource(java.lang.String) */ From 5954fe75fde9614fef2599f83f627222195e3bfc Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 2 Dec 2020 02:08:11 -0700 Subject: [PATCH 0939/1778] Allow overrideClassLoaders(ClassGraphClassLoader) (#485) --- .../classgraph/ClassGraphClassLoader.java | 32 ++++-- .../java/io/github/classgraph/ScanResult.java | 20 ++-- .../java/io/github/classgraph/Scanner.java | 25 ++-- .../ClassGraphClassLoaderHandler.java | 108 ++++++++++++++++++ .../ClassLoaderHandlerRegistry.java | 5 +- .../classgraph/classpath/ClasspathFinder.java | 24 ++++ 6 files changed, 176 insertions(+), 38 deletions(-) create mode 100644 src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassGraphClassLoaderHandler.java diff --git a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java index 47f286cfa..ee2a4fc6f 100644 --- a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java +++ b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java @@ -44,7 +44,7 @@ import nonapi.io.github.classgraph.utils.JarUtils; /** {@link ClassLoader} for classes found by ClassGraph during scanning. */ -class ClassGraphClassLoader extends URLClassLoader { +public class ClassGraphClassLoader extends ClassLoader { /** The scan result. */ private final ScanResult scanResult; @@ -59,7 +59,7 @@ class ClassGraphClassLoader extends URLClassLoader { private List overrideClassLoaders; /** A {@link URLClassLoader} consisting of URLs on the classpath. */ - private final URLClassLoader classpathClassLoader; + private final ClassLoader classpathClassLoader; /** The ordered set of overridden or added classloaders to try delegating to. */ private Set addedClassLoaderDelegationOrder; @@ -109,14 +109,14 @@ class ClassGraphClassLoader extends URLClassLoader { // If the classloaders were overridden, just use the override classloaders, and then fail if the // class couldn't be found. - // + overrideClassLoaders = classloadersOverridden ? scanSpec.overrideClassLoaders : null; + // If the classpath is overridden, and classloaders are not overridden, try loading class from // classpath URLs, as the override classloader, then fail if the class couldn't be found. // // N.B. Some classpath URLs might be invalid if the ScanResult has been closed (e.g. in the rare // case that an inner jar had to be extracted to a temporary file on disk). - overrideClassLoaders = classloadersOverridden ? scanSpec.overrideClassLoaders : null; - if (overrideClassLoaders == null && classpathClassLoader != null) { + if (overrideClassLoaders == null && classpathOverridden && classpathClassLoader != null) { overrideClassLoaders = Collections.singletonList(classpathClassLoader); } @@ -137,6 +137,17 @@ class ClassGraphClassLoader extends URLClassLoader { @Override protected Class findClass(final String className) throws ClassNotFoundException, LinkageError, SecurityException { + // First delegate to outer nested ClassGraphClassLoader, if any (#485) + final ClassGraphClassLoader delegateClassGraphClassLoader = scanResult.classpathFinder + .getDelegateClassGraphClassLoader(); + if (delegateClassGraphClassLoader != null) { + try { + return Class.forName(className, initializeLoadedClasses, delegateClassGraphClassLoader); + } catch (ClassNotFoundException | LinkageError e) { + // Ignore + } + } + // If overrideClassLoaders is set, only use the override loaders if (overrideClassLoaders != null) { for (final ClassLoader overrideClassLoader : overrideClassLoaders) { @@ -146,11 +157,11 @@ protected Class findClass(final String className) // Ignore } } - throw new ClassNotFoundException("Could not load classfile for class " + className); } // Try environment classloader(s) first, since this is the usual default - if (environmentClassLoaderDelegationOrder != null && !environmentClassLoaderDelegationOrder.isEmpty()) { + if (overrideClassLoaders == null && environmentClassLoaderDelegationOrder != null + && !environmentClassLoaderDelegationOrder.isEmpty()) { for (final ClassLoader envClassLoader : environmentClassLoaderDelegationOrder) { try { return Class.forName(className, initializeLoadedClasses, envClassLoader); @@ -171,8 +182,8 @@ protected Class findClass(final String className) classInfoClassLoader = classInfo.classLoader; // Try specific classloader for the classpath element that the classfile was obtained from, // as long as it wasn't already tried - if (classInfoClassLoader != null - && !environmentClassLoaderDelegationOrder.contains(classInfoClassLoader)) { + if (classInfoClassLoader != null && (environmentClassLoaderDelegationOrder == null + || !environmentClassLoaderDelegationOrder.contains(classInfoClassLoader))) { try { return Class.forName(className, initializeLoadedClasses, classInfoClassLoader); } catch (ClassNotFoundException | LinkageError e) { @@ -193,7 +204,7 @@ protected Class findClass(final String className) } // Try loading from classpath URLs - if (classpathClassLoader != null) { + if (overrideClassLoaders == null && classpathClassLoader != null) { try { return Class.forName(className, initializeLoadedClasses, classpathClassLoader); } catch (ClassNotFoundException | LinkageError e) { @@ -250,7 +261,6 @@ protected Class findClass(final String className) } /** Get classpath URLs. */ - @Override public URL[] getURLs() { return scanResult.getClasspathURLs().toArray(new URL[0]); } diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index d58271a1d..3148c3c55 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -50,6 +50,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import nonapi.io.github.classgraph.classpath.ClasspathFinder; import nonapi.io.github.classgraph.concurrency.AutoCloseableExecutorService; import nonapi.io.github.classgraph.fastzipfilereader.NestedJarHandler; import nonapi.io.github.classgraph.json.JSONDeserializer; @@ -104,11 +105,8 @@ public final class ScanResult implements Closeable, AutoCloseable { /** A custom ClassLoader that can load classes found during the scan. */ private ClassGraphClassLoader classGraphClassLoader; - /** - * The default order in which ClassLoaders are called to load classes, respecting parent-first/parent-last - * delegation order. - */ - private ClassLoader[] classLoaderOrderRespectingParentDelegation; + /** The {@link ClasspathFinder}. */ + ClasspathFinder classpathFinder; /** The nested jar handler instance. */ private NestedJarHandler nestedJarHandler; @@ -244,8 +242,7 @@ static void init() { * the toplevel log */ ScanResult(final ScanSpec scanSpec, final List classpathOrder, - final List rawClasspathEltOrderStrs, - final ClassLoader[] classLoaderOrderRespectingParentDelegation, + final List rawClasspathEltOrderStrs, final ClasspathFinder classpathFinder, final Map classNameToClassInfo, final Map packageNameToPackageInfo, final Map moduleNameToModuleInfo, final Map fileToLastModified, @@ -253,7 +250,7 @@ static void init() { this.scanSpec = scanSpec; this.rawClasspathEltOrderStrs = rawClasspathEltOrderStrs; this.classpathOrder = classpathOrder; - this.classLoaderOrderRespectingParentDelegation = classLoaderOrderRespectingParentDelegation; + this.classpathFinder = classpathFinder; this.fileToLastModified = fileToLastModified; this.classNameToClassInfo = classNameToClassInfo; this.packageNameToPackageInfo = packageNameToPackageInfo; @@ -1179,7 +1176,7 @@ public long classpathContentsLastModifiedTime() { * @return the class loader order. */ ClassLoader[] getClassLoaderOrderRespectingParentDelegation() { - return classLoaderOrderRespectingParentDelegation; + return classpathFinder.getClassLoaderOrderRespectingParentDelegation(); } /** @@ -1295,6 +1292,7 @@ public Class loadClass(final String className, final Class superclassO * The JSON string for the serialized {@link ScanResult}. * @return The deserialized {@link ScanResult}. */ + @SuppressWarnings("null") public static ScanResult fromJSON(final String json) { final Matcher matcher = Pattern.compile("\\{[\\n\\r ]*\"format\"[ ]?:[ ]?\"([^\"]+)\"").matcher(json); if (!matcher.find()) { @@ -1310,7 +1308,7 @@ public static ScanResult fromJSON(final String json) { // Deserialize the JSON final SerializationFormat deserialized = JSONDeserializer.deserializeObject(SerializationFormat.class, json); - if (!deserialized.format.equals(CURRENT_SERIALIZATION_FORMAT)) { + if (deserialized == null || !deserialized.format.equals(CURRENT_SERIALIZATION_FORMAT)) { // Probably the deserialization failed before now anyway, if fields have changed, etc. throw new IllegalArgumentException("JSON was serialized by newer version of ClassGraph"); } @@ -1451,7 +1449,7 @@ public void close() { nestedJarHandler = null; } classGraphClassLoader = null; - classLoaderOrderRespectingParentDelegation = null; + classpathFinder = null; // Flush log on exit, in case additional log entries were generated after scan() completed if (topLevelLog != null) { topLevelLog.flush(); diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index f7a9b980f..87f2634ad 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -115,9 +115,6 @@ class Scanner implements Callable { /** The module order. */ private final List moduleOrder; - /** The environment classloader order, respecting parent-first or parent-last delegation order. */ - private final ClassLoader[] classLoaderOrderRespectingParentDelegation; - // ------------------------------------------------------------------------------------------------------------- /** @@ -169,8 +166,6 @@ class Scanner implements Callable { final LogNode classpathFinderLog = topLevelLog == null ? null : topLevelLog.log("Finding classpath"); this.classpathFinder = new ClasspathFinder(scanSpec, classpathFinderLog); - this.classLoaderOrderRespectingParentDelegation = classpathFinder - .getClassLoaderOrderRespectingParentDelegation(); try { this.moduleOrder = new ArrayList<>(); @@ -180,6 +175,8 @@ class Scanner implements Callable { if (moduleFinder != null) { // Add modules to start of classpath order, before traditional classpath final List systemModuleRefs = moduleFinder.getSystemModuleRefs(); + final ClassLoader[] classLoaderOrderRespectingParentDelegation = classpathFinder + .getClassLoaderOrderRespectingParentDelegation(); final ClassLoader defaultClassLoader = classLoaderOrderRespectingParentDelegation != null && classLoaderOrderRespectingParentDelegation.length != 0 ? classLoaderOrderRespectingParentDelegation[0] @@ -919,8 +916,7 @@ private void maskClassfiles(final List classpathElementOrder, * if the scan threw an uncaught exception */ private ScanResult performScan(final List finalClasspathEltOrder, - final List finalClasspathEltOrderStrs, - final ClassLoader[] classLoaderOrderRespectingParentDelegation) + final List finalClasspathEltOrderStrs, final ClasspathFinder classpathFinder) throws InterruptedException, ExecutionException { // Mask classfiles (remove any classfile resources that are shadowed by an earlier definition // of the same class) @@ -1014,9 +1010,9 @@ private ScanResult performScan(final List finalClasspathEltOrd } // Return a new ScanResult - return new ScanResult(scanSpec, finalClasspathEltOrder, finalClasspathEltOrderStrs, - classLoaderOrderRespectingParentDelegation, classNameToClassInfo, packageNameToPackageInfo, - moduleNameToModuleInfo, fileToLastModified, nestedJarHandler, topLevelLog); + return new ScanResult(scanSpec, finalClasspathEltOrder, finalClasspathEltOrderStrs, classpathFinder, + classNameToClassInfo, packageNameToPackageInfo, moduleNameToModuleInfo, fileToLastModified, + nestedJarHandler, topLevelLog); } // ------------------------------------------------------------------------------------------------------------- @@ -1113,17 +1109,16 @@ public void processWorkUnit(final ClasspathElement classpathElement, if (performScan) { // Scan classpath / modules, producing a ScanResult. - return performScan(finalClasspathEltOrderFiltered, finalClasspathEltOrderStrs, - classLoaderOrderRespectingParentDelegation); + return performScan(finalClasspathEltOrderFiltered, finalClasspathEltOrderStrs, classpathFinder); } else { // Only getting classpath -- return a placeholder ScanResult to hold classpath elements if (topLevelLog != null) { topLevelLog.log("Only returning classpath elements (not performing a scan)"); } return new ScanResult(scanSpec, finalClasspathEltOrderFiltered, finalClasspathEltOrderStrs, - classLoaderOrderRespectingParentDelegation, /* classNameToClassInfo = */ null, - /* packageNameToPackageInfo = */ null, /* moduleNameToModuleInfo = */ null, - /* fileToLastModified = */ null, nestedJarHandler, topLevelLog); + classpathFinder, /* classNameToClassInfo = */ null, /* packageNameToPackageInfo = */ null, + /* moduleNameToModuleInfo = */ null, /* fileToLastModified = */ null, nestedJarHandler, + topLevelLog); } } diff --git a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassGraphClassLoaderHandler.java b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassGraphClassLoaderHandler.java new file mode 100644 index 000000000..ca3a054f5 --- /dev/null +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassGraphClassLoaderHandler.java @@ -0,0 +1,108 @@ +/* + * 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.classloaderhandler; + +import java.net.URL; + +import io.github.classgraph.ClassGraphClassLoader; +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; + +/** + * Allow for overrideClassLoaders to be called with a ClassGraphClassLoader as a parameter, so that nested scans can + * share a single classloader (#485). + */ +class ClassGraphClassLoaderHandler implements ClassLoaderHandler { + /** Class cannot be constructed. */ + private ClassGraphClassLoaderHandler() { + } + + /** + * 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) { + final boolean matches = "io.github.classgraph.ClassGraphClassLoader".equals(classLoaderClass.getName()); + if (matches && log != null) { + log.log("Sharing a `ClassGraphClassLoader` between multiple nested scans is not advisable, " + + "because scan criteria may differ between scans. " + + "See: https://github.com/classgraph/classgraph/issues/485"); + } + return matches; + } + + /** + * 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) { + 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) { + // ClassGraphClassLoader overrides URLClassLoader, so we can get the basic classpath URLs the same + // way as for URLClassLoader. However, classloading will try to preferentially reuse the older + // ClassGraphClassLoader before loading with the new ClassGraphClassLoader from the current scan, + // so the following URLs will be scanned by the current scan, but classes will only be loaded from + // these URLs if the older classloader fails. + for (final URL url : ((ClassGraphClassLoader) classLoader).getURLs()) { + if (url != null) { + classpathOrder.addClasspathEntry(url, classLoader, scanSpec, log); + } + } + } +} 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 57b82a284..7cb427b85 100644 --- a/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java +++ b/src/main/java/nonapi/io/github/classgraph/classloaderhandler/ClassLoaderHandlerRegistry.java @@ -70,7 +70,10 @@ public class ClassLoaderHandlerRegistry { // Java 7/8 URLClassLoader support (should be second-to-last, so that subclasses of // URLClassLoader are handled by more specific handlers above) - new ClassLoaderHandlerRegistryEntry(URLClassLoaderHandler.class))); + new ClassLoaderHandlerRegistryEntry(URLClassLoaderHandler.class), + + // Placeholder for delegation to a ClassGraphClassLoader instance from an outer nested scan + new ClassLoaderHandlerRegistryEntry(ClassGraphClassLoaderHandler.class))); /** Fallback ClassLoaderHandler. */ public static final ClassLoaderHandlerRegistryEntry FALLBACK_HANDLER = new ClassLoaderHandlerRegistryEntry( 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 5eb15b9ea..16c727158 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathFinder.java @@ -33,6 +33,7 @@ import java.util.Map.Entry; import java.util.Set; +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.scanspec.ScanSpec; @@ -55,6 +56,13 @@ public class ClasspathFinder { */ private ClassLoader[] classLoaderOrderRespectingParentDelegation; + /** + * If one of the classloaders that was found was an existing instance of {@link ClassGraphClassLoader}, then + * delegate to that classloader first rather than trying to load from the {@link ClassGraphClassLoader} of the + * current scan, so that classes are compatible between nested scans (#485). + */ + private ClassGraphClassLoader delegateClassGraphClassLoader; + // ------------------------------------------------------------------------------------------------------------- /** @@ -84,6 +92,18 @@ public ClassLoader[] getClassLoaderOrderRespectingParentDelegation() { return classLoaderOrderRespectingParentDelegation; } + /** + * If one of the classloaders that was found was an existing instance of {@link ClassGraphClassLoader}, then + * delegate to that classloader first rather than trying to load from the {@link ClassGraphClassLoader} of the + * current scan, so that classes are compatible between nested scans (#485). + * + * @return the {@link ClassGraphClassLoader} to delegate to before loading classes with this scan's own + * {@link ClassGraphClassLoader} (or null if none). + */ + public ClassGraphClassLoader getDelegateClassGraphClassLoader() { + return delegateClassGraphClassLoader; + } + // ------------------------------------------------------------------------------------------------------------- /** @@ -248,6 +268,10 @@ public ClasspathFinder(final ScanSpec scanSpec, final LogNode log) { 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; + } } // Need to record the classloader delegation order, in particular to respect parent-last delegation From 441713ffcd5fa605b9e0958d2d6611ece715f924 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 2 Dec 2020 02:10:58 -0700 Subject: [PATCH 0940/1778] [maven-release-plugin] prepare release classgraph-4.8.93 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index eb8fbe5f3..06a5d2325 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.93-SNAPSHOT + 4.8.93 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.90 + classgraph-4.8.93 From c0a19be980e1b61c923978cb812429a5af0ddfd1 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 2 Dec 2020 02:11:06 -0700 Subject: [PATCH 0941/1778] [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 06a5d2325..494cac2e4 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.93 + 4.8.94-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.93 + classgraph-4.8.90 From 669d4c229da74a67ea5e1f05a21e87775d4c1494 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 8 Dec 2020 23:23:26 -0700 Subject: [PATCH 0942/1778] Fix extendsSuperclass() and getSubclasses() to work with Object (#486) --- src/main/java/io/github/classgraph/ClassInfo.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index 4d668f818..04f0e79b4 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -1249,7 +1249,7 @@ public boolean isArrayClass() { * @return true if this class extends the named superclass. */ public boolean extendsSuperclass(final String superclassName) { - return getSuperclasses().containsName(superclassName); + return superclassName.equals("java.lang.Object") || getSuperclasses().containsName(superclassName); } /** @@ -1514,13 +1514,16 @@ private List getOverrideOrder() { /** * Get the subclasses of this class, sorted in order of name. Call {@link ClassInfoList#directOnly()} to get * direct subclasses. + * + * If this class represents {@link Object}, then returns only standard classes, not interfaces, since interfaces + * don't extend {@link Object}. * * @return the list of subclasses of this class, or the empty list if none. */ public ClassInfoList getSubclasses() { if (getName().equals("java.lang.Object")) { // Make an exception for querying all subclasses of java.lang.Object - return scanResult.getAllClasses(); + return scanResult.getAllStandardClasses(); } else { return new ClassInfoList( this.filterClassInfo(RelType.SUBCLASSES, /* strictAccept = */ !isExternalClass), @@ -1529,9 +1532,11 @@ public ClassInfoList getSubclasses() { } /** - * Get all superclasses of this class, in ascending order in the class hierarchy. Does not include - * superinterfaces, if this is an interface (use {@link #getInterfaces()} to get superinterfaces of an - * interface.} + * Get all superclasses of this class, in ascending order in the class hierarchy, not including {@link Object} + * for simplicity, since that is the superclass of all classes. + * + * Also does not include superinterfaces, if this is an interface (use {@link #getInterfaces()} to get + * superinterfaces of an interface.} * * @return the list of all superclasses of this class, or the empty list if none. */ From 8e4fb6138d19d0490c4c6b681a4e9d09b3432c5a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 9 Dec 2020 09:04:31 -0700 Subject: [PATCH 0943/1778] [maven-release-plugin] prepare release classgraph-4.8.94 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 494cac2e4..1370281da 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.94-SNAPSHOT + 4.8.94 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.90 + classgraph-4.8.94 From 47bafbab60bc3da79f845516638a91969c3b91c9 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 9 Dec 2020 09:05:40 -0700 Subject: [PATCH 0944/1778] [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 1370281da..7601abb3b 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.94 + 4.8.95-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 1844fdca36bb183240a38ac050466365e9f25838 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 9 Dec 2020 09:15:17 -0700 Subject: [PATCH 0945/1778] extendsSuperclass("java.lang.Object") should return false for interfaces (#486) --- src/main/java/io/github/classgraph/ClassInfo.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index 04f0e79b4..4d44fb9ef 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -1249,7 +1249,8 @@ public boolean isArrayClass() { * @return true if this class extends the named superclass. */ public boolean extendsSuperclass(final String superclassName) { - return superclassName.equals("java.lang.Object") || getSuperclasses().containsName(superclassName); + return (superclassName.equals("java.lang.Object") && isStandardClass()) + || getSuperclasses().containsName(superclassName); } /** From 86512c81696aca35a857602bef3d45ac42e4aaa9 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 9 Dec 2020 18:38:55 -0700 Subject: [PATCH 0946/1778] Bump version back down to retry release --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7601abb3b..17098b0da 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.95-SNAPSHOT + 4.8.94-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From d11d702456a68de85b5040e95e65b1a803008df3 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 9 Dec 2020 18:39:41 -0700 Subject: [PATCH 0947/1778] [maven-release-plugin] prepare release classgraph-4.8.94 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 17098b0da..1370281da 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.94-SNAPSHOT + 4.8.94 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From ff5ff68abeda9fb4219b224ed19be081a5d950a4 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 9 Dec 2020 18:39:47 -0700 Subject: [PATCH 0948/1778] [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 1370281da..7601abb3b 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.94 + 4.8.95-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From c6062a5ef65945fbdc129c040ae3b32f1107ec44 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 10 Dec 2020 02:00:59 +0000 Subject: [PATCH 0949/1778] Bump jmh-core from 1.26 to 1.27 Bumps jmh-core from 1.26 to 1.27. Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7601abb3b..8cc76946b 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ org.openjdk.jmh jmh-core - 1.26 + 1.27 test From dbbefc143ae55438a91a71d99173caeaedd58a24 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 10 Dec 2020 02:01:00 +0000 Subject: [PATCH 0950/1778] Bump jmh-generator-annprocess from 1.26 to 1.27 Bumps jmh-generator-annprocess from 1.26 to 1.27. Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7601abb3b..73eae0d64 100644 --- a/pom.xml +++ b/pom.xml @@ -81,7 +81,7 @@ org.openjdk.jmh jmh-generator-annprocess - 1.26 + 1.27 test From 1505324351c55a6af640bf958dedee536442541d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 10 Dec 2020 09:18:45 -0700 Subject: [PATCH 0951/1778] Fix Javadoc --- src/main/java/io/github/classgraph/ScanResult.java | 4 ++-- src/main/java/io/github/classgraph/Scanner.java | 4 ++-- 2 files 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 3148c3c55..e4bd9abdc 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -226,8 +226,8 @@ static void init() { * the classpath order * @param rawClasspathEltOrderStrs * the raw classpath element order - * @param classLoaderOrderRespectingParentDelegation - * the environment classloader order, respecting parent-first or parent-last delegation order + * @param classpathFinder + * the {@link ClasspathFinder} * @param classNameToClassInfo * a map from class name to class info * @param packageNameToPackageInfo diff --git a/src/main/java/io/github/classgraph/Scanner.java b/src/main/java/io/github/classgraph/Scanner.java index 87f2634ad..6f452d552 100644 --- a/src/main/java/io/github/classgraph/Scanner.java +++ b/src/main/java/io/github/classgraph/Scanner.java @@ -907,8 +907,8 @@ private void maskClassfiles(final List classpathElementOrder, * the final classpath elt order * @param finalClasspathEltOrderStrs * the final classpath elt order strs - * @param classLoaderOrderRespectingParentDelegation - * the environment classloader order, respecting parent-first or parent-last delegation order + * @param classpathFinder + * the {@link ClasspathFinder} * @return the scan result * @throws InterruptedException * if the scan was interrupted From 33b67257e1ba84f16b950fb5ba5bd30ef19be60c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sun, 13 Dec 2020 13:46:34 -0700 Subject: [PATCH 0952/1778] Fix error message --- src/main/java/io/github/classgraph/MethodInfoList.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/MethodInfoList.java b/src/main/java/io/github/classgraph/MethodInfoList.java index 8d0a12f09..5d8495901 100644 --- a/src/main/java/io/github/classgraph/MethodInfoList.java +++ b/src/main/java/io/github/classgraph/MethodInfoList.java @@ -198,7 +198,7 @@ public MethodInfo getSingleMethod(final String methodName) { return lastFoundMethod; } else { throw new IllegalArgumentException("There are multiple methods named \"" + methodName + "\" in class " - + iterator().next().getName()); + + iterator().next().getClassInfo().getName()); } } From a21bf7dc182c7e534540959b41c92abc1b64839d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 14 Dec 2020 00:12:39 -0700 Subject: [PATCH 0953/1778] Make getTypeParameters() public (#490) --- .../java/io/github/classgraph/MethodTypeSignature.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/github/classgraph/MethodTypeSignature.java b/src/main/java/io/github/classgraph/MethodTypeSignature.java index 5feef8554..f6ae852c3 100644 --- a/src/main/java/io/github/classgraph/MethodTypeSignature.java +++ b/src/main/java/io/github/classgraph/MethodTypeSignature.java @@ -78,13 +78,12 @@ private MethodTypeSignature(final List typeParameters, final List // ------------------------------------------------------------------------------------------------------------- /** - * Get the type parameters for the method. N.B. this is non-public, since the types have to be aligned with - * other parameter metadata. The type of a parameter can be obtained post-alignment from the parameter's - * {@link MethodParameterInfo} object. + * Get the type parameters for the method, if this is a + * generic method. * - * @return The type parameters for the method. + * @return The type parameters for the method, if any, otherwise null. */ - List getTypeParameters() { + public List getTypeParameters() { return typeParameters; } From d7cc7c7ad704376f6ef03a3edb33a5ffd183ccdc Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 14 Dec 2020 00:24:52 -0700 Subject: [PATCH 0954/1778] [maven-release-plugin] prepare release classgraph-4.8.95 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 9a452b00b..62b0d0968 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.95-SNAPSHOT + 4.8.95 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.94 + classgraph-4.8.95 From ccd93b33a287d5ad38d73365f7f94fb2a954b65d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 14 Dec 2020 00:24:59 -0700 Subject: [PATCH 0955/1778] [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 62b0d0968..2222dab55 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.95 + 4.8.96-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From bb95b549d0e0734cb1e53b3607765191f76f681f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 17 Dec 2020 16:10:41 -0700 Subject: [PATCH 0956/1778] Properly handle non-URL paths in `class.path` --- .../github/classgraph/classpath/ClasspathOrder.java | 11 +++++++++-- 1 file changed, 9 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 489bf2022..3b819d772 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java @@ -313,8 +313,15 @@ public boolean addClasspathEntry(final Object pathElement, final ClassLoader cla 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() - : new URL(pathElement.toString()); + : pathElement instanceof File ? ((File) pathElement).toURI().toURL() : null; + if (pathElementURL == null) { + final String pathElementToStr = pathElement.toString(); + try { + pathElementURL = new URL(pathElementToStr); + } catch (MalformedURLException e) { + pathElementURL = new URL("file:" + pathElementToStr); + } + } } catch (final MalformedURLException e1) { if (log != null) { log.log("Cannot convert to URL: " + pathElement); From b8f1cc4ae14b0f738eaea30803aadaa6840c4de4 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 17 Dec 2020 16:13:40 -0700 Subject: [PATCH 0957/1778] Fix previous commit --- .../nonapi/io/github/classgraph/classpath/ClasspathOrder.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 3b819d772..954fed937 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java @@ -315,10 +315,12 @@ 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 final String pathElementToStr = pathElement.toString(); try { - pathElementURL = new URL(pathElementToStr); + pathElementURL = new File(pathElementToStr).toURI().toURL(); } catch (MalformedURLException e) { + // Final fallback -- try prepending "file:" to create a URL pathElementURL = new URL("file:" + pathElementToStr); } } From 025bd57ab8b08f7d256971bf53014b45a10098ba Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 17 Dec 2020 17:21:26 -0700 Subject: [PATCH 0958/1778] Initial implementation of type attributes (#402) --- .../github/classgraph/ArrayTypeSignature.java | 181 ++++++----- .../github/classgraph/BaseTypeSignature.java | 260 ++++++++------- .../java/io/github/classgraph/ClassInfo.java | 72 ++++- .../classgraph/ClassRefTypeSignature.java | 249 +++++++++++---- .../github/classgraph/ClassTypeSignature.java | 53 +++- .../java/io/github/classgraph/Classfile.java | 300 ++++++++++++++++-- .../java/io/github/classgraph/FieldInfo.java | 21 +- .../classgraph/HierarchicalTypeSignature.java | 93 ++++++ .../java/io/github/classgraph/MethodInfo.java | 27 +- .../classgraph/MethodTypeSignature.java | 25 +- .../classgraph/ObjectTypedValueWrapper.java | 3 +- .../io/github/classgraph/TypeArgument.java | 71 +++-- .../io/github/classgraph/TypeParameter.java | 45 ++- .../io/github/classgraph/TypeSignature.java | 43 +-- .../classgraph/TypeVariableSignature.java | 34 +- .../classgraph/classpath/ClasspathOrder.java | 2 +- .../io/github/classgraph/types/TypeUtils.java | 3 +- .../issues/GenericInnerClassTypedField.java | 4 +- .../issues/issue152/Issue152Test.java | 6 +- .../issues/issue175/Issue175Test.java | 40 +-- .../classgraph/issues/issue402/Foo.java | 6 + .../classgraph/issues/issue402/Outer.java | 24 ++ .../issues/issue402/TypeAnnotationTest.java | 155 +++++++++ .../github/classgraph/issues/issue402/X.java | 8 + .../test/methodinfo/MethodInfoTest.java | 9 +- 25 files changed, 1296 insertions(+), 438 deletions(-) create mode 100644 src/test/java/io/github/classgraph/issues/issue402/Foo.java create mode 100644 src/test/java/io/github/classgraph/issues/issue402/Outer.java create mode 100644 src/test/java/io/github/classgraph/issues/issue402/TypeAnnotationTest.java create mode 100644 src/test/java/io/github/classgraph/issues/issue402/X.java diff --git a/src/main/java/io/github/classgraph/ArrayTypeSignature.java b/src/main/java/io/github/classgraph/ArrayTypeSignature.java index f25f35d61..8cc82da4e 100644 --- a/src/main/java/io/github/classgraph/ArrayTypeSignature.java +++ b/src/main/java/io/github/classgraph/ArrayTypeSignature.java @@ -29,19 +29,16 @@ package io.github.classgraph; import java.lang.reflect.Array; +import java.util.List; +import java.util.Objects; import java.util.Set; +import io.github.classgraph.Classfile.TypePathNode; import nonapi.io.github.classgraph.types.ParseException; import nonapi.io.github.classgraph.types.Parser; /** An array type signature. */ public class ArrayTypeSignature extends ReferenceTypeSignature { - /** The array element type signature. */ - private final TypeSignature elementTypeSignature; - - /** The number of array dimensions. */ - private final int numDims; - /** The raw type signature string for the array type. */ private final String typeSignatureStr; @@ -54,6 +51,9 @@ public class ArrayTypeSignature extends ReferenceTypeSignature { /** The element class. */ private Class elementClassRef; + /** The nested type (another {@link ArrayTypeSignature}, or the base element type). */ + private final TypeSignature nestedType; + // ------------------------------------------------------------------------------------------------------------- /** @@ -68,50 +68,18 @@ public class ArrayTypeSignature extends ReferenceTypeSignature { */ ArrayTypeSignature(final TypeSignature elementTypeSignature, final int numDims, final String typeSignatureStr) { super(); - this.elementTypeSignature = elementTypeSignature; - this.numDims = numDims; - this.typeSignatureStr = typeSignatureStr; - } - - /** - * Constructor. - * - * @param eltClassName - * The type signature of the array elements. - * @param numDims - * The number of array dimensions. - */ - ArrayTypeSignature(final String eltClassName, final int numDims) { - super(); - final BaseTypeSignature baseTypeSignature = BaseTypeSignature.getTypeSignature(eltClassName); - String eltTypeSigStr; - if (baseTypeSignature != null) { - // Element type is a base (primitive) type - eltTypeSigStr = baseTypeSignature.getTypeSignatureChar(); - this.elementTypeSignature = baseTypeSignature; - } else { - // Element type is not a base (primitive) type -- create a type signature for element type - eltTypeSigStr = "L" + eltClassName.replace('.', '/') + ";"; - try { - this.elementTypeSignature = ClassRefTypeSignature.parse(new Parser(eltTypeSigStr), - // No type variables to resolve for generic types - /* definingClassName = */ null); - if (this.elementTypeSignature == null) { - throw new IllegalArgumentException( - "Could not form array base type signature for class " + eltClassName); - } - } catch (final ParseException e) { - throw new IllegalArgumentException( - "Could not form array base type signature for class " + eltClassName); - } + final boolean typeSigHasTwoOrMoreDims = typeSignatureStr.startsWith("[["); + if (numDims < 1) { + throw new IllegalArgumentException("numDims < 1"); + } else if ((numDims >= 2) != typeSigHasTwoOrMoreDims) { + throw new IllegalArgumentException("numDims does not match type signature"); } - final StringBuilder buf = new StringBuilder(numDims + eltTypeSigStr.length()); - for (int i = 0; i < numDims; i++) { - buf.append('['); - } - buf.append(eltTypeSigStr); - this.typeSignatureStr = buf.toString(); - this.numDims = numDims; + this.typeSignatureStr = typeSignatureStr; + this.nestedType = typeSigHasTwoOrMoreDims + // Strip one array dimension for nested type + ? new ArrayTypeSignature(elementTypeSignature, numDims - 1, typeSignatureStr.substring(1)) + // Nested type for innermost dimension is element type + : elementTypeSignature; } /** @@ -124,12 +92,16 @@ public String getTypeSignatureStr() { } /** - * Get the type signature of the array elements. + * Get the type signature of the innermost element type of the array. * - * @return The type signature of the array elements. + * @return The type signature of the innermost element type. */ public TypeSignature getElementTypeSignature() { - return elementTypeSignature; + ArrayTypeSignature curr = this; + while (curr.nestedType instanceof ArrayTypeSignature) { + curr = (ArrayTypeSignature) curr.nestedType; + } + return curr.getNestedType(); } /** @@ -138,9 +110,51 @@ public TypeSignature getElementTypeSignature() { * @return The number of dimensions of the array. */ public int getNumDimensions() { + int numDims = 1; + ArrayTypeSignature curr = this; + while (curr.nestedType instanceof ArrayTypeSignature) { + curr = (ArrayTypeSignature) curr.nestedType; + numDims++; + } return numDims; } + /** + * Get the nested type, which is another {@link ArrayTypeSignature} with one dimension fewer, if this array has + * 2 or more dimensions, otherwise this returns the element type. + * + * @return The nested type. + */ + public TypeSignature getNestedType() { + return nestedType; + } + + @Override + protected void addTypeAnnotation(final List typePath, final AnnotationInfo annotationInfo) { + if (typePath.isEmpty()) { + addTypeAnnotation(annotationInfo); + } else { + final TypePathNode head = typePath.get(0); + if (head.typePathKind != 0 || head.typeArgumentIdx != 0) { + throw new IllegalArgumentException("typePath element contains bad values: " + head); + } + nestedType.addTypeAnnotation(typePath.subList(1, typePath.size()), annotationInfo); + } + } + + /** + * Get a list of {@link AnnotationInfo} objects for the type annotations on this array type, or null if none. + * + * @see #getNestedType() if you want to read for type annotations on inner (nested) dimensions of the array + * type. + * @return a list of {@link AnnotationInfo} objects for the type annotations of on this array type, or null if + * none. + */ + @Override + public AnnotationInfoList getTypeAnnotationInfo() { + return super.getTypeAnnotationInfo(); + } + // ------------------------------------------------------------------------------------------------------------- /* (non-Javadoc) @@ -149,7 +163,7 @@ public int getNumDimensions() { @Override protected String getClassName() { if (className == null) { - className = toStringInternal(/* useSimpleNames = */ false); + className = toString(); } return className; } @@ -191,9 +205,7 @@ public ArrayClassInfo getArrayClassInfo() { @Override void setScanResult(final ScanResult scanResult) { super.setScanResult(scanResult); - if (elementTypeSignature != null) { - elementTypeSignature.setScanResult(scanResult); - } + nestedType.setScanResult(scanResult); if (arrayClassInfo != null) { arrayClassInfo.setScanResult(scanResult); } @@ -207,23 +219,24 @@ void setScanResult(final ScanResult scanResult) { */ @Override protected void findReferencedClassNames(final Set refdClassNames) { - elementTypeSignature.findReferencedClassNames(refdClassNames); + nestedType.findReferencedClassNames(refdClassNames); } // ------------------------------------------------------------------------------------------------------------- /** - * Get a {@code Class} reference for the array element type. Causes the ClassLoader to load the element + * Get a {@code Class} reference for the innermost array element type. Causes the ClassLoader to load the * class, if it is not already loaded. * * @param ignoreExceptions * Whether or not to ignore exceptions. - * @return a {@code Class} reference for the array element type. Also works for arrays of primitive element - * type. + * @return a {@code Class} reference for the innermost array element type. Also works for arrays of primitive + * element type. */ public Class loadElementClass(final boolean ignoreExceptions) { if (elementClassRef == null) { // Try resolving element type against base types (int, etc.) + final TypeSignature elementTypeSignature = getElementTypeSignature(); if (elementTypeSignature instanceof BaseTypeSignature) { elementClassRef = ((BaseTypeSignature) elementTypeSignature).getType(); } else { @@ -231,8 +244,7 @@ public Class loadElementClass(final boolean ignoreExceptions) { elementClassRef = elementTypeSignature.loadClass(ignoreExceptions); } else { // Fallback, if scanResult is not set - final String elementTypeName = ((ClassRefTypeSignature) elementTypeSignature) - .getFullyQualifiedClassName(); + final String elementTypeName = ((ClassRefTypeSignature) elementTypeSignature).getClassName(); try { elementClassRef = Class.forName(elementTypeName); } catch (final Throwable t) { @@ -284,10 +296,11 @@ public Class loadClass(final boolean ignoreExceptions) { eltClassRef = loadElementClass(); } if (eltClassRef == null) { - throw new IllegalArgumentException("Could not load array element class " + elementTypeSignature); + throw new IllegalArgumentException( + "Could not load array element class " + getElementTypeSignature()); } // Create an array of the target number of dimensions, with size zero in each dimension - final Object eltArrayInstance = Array.newInstance(eltClassRef, new int[numDims]); + final Object eltArrayInstance = Array.newInstance(eltClassRef, new int[getNumDimensions()]); // Get the class reference from the array instance classRef = eltArrayInstance.getClass(); } @@ -314,7 +327,7 @@ public Class loadClass() { */ @Override public int hashCode() { - return elementTypeSignature.hashCode() + numDims * 15; + return 1 + nestedType.hashCode(); } /* (non-Javadoc) @@ -328,7 +341,8 @@ public boolean equals(final Object obj) { return false; } final ArrayTypeSignature other = (ArrayTypeSignature) obj; - return other.elementTypeSignature.equals(this.elementTypeSignature) && other.numDims == this.numDims; + return Objects.equals(this.typeAnnotationInfo, other.typeAnnotationInfo) + && this.nestedType.equals(other.nestedType); } /* (non-Javadoc) @@ -343,22 +357,35 @@ public boolean equalsIgnoringTypeParams(final TypeSignature other) { return false; } final ArrayTypeSignature o = (ArrayTypeSignature) other; - return o.elementTypeSignature.equalsIgnoringTypeParams(this.elementTypeSignature) - && o.numDims == this.numDims; + return this.nestedType.equalsIgnoringTypeParams(o.nestedType); } - /* (non-Javadoc) - * @see io.github.classgraph.TypeSignature#toStringInternal(boolean) - */ @Override - protected String toStringInternal(final boolean useSimpleNames) { - final StringBuilder buf = new StringBuilder(); - buf.append( - useSimpleNames ? elementTypeSignature.toStringWithSimpleNames() : elementTypeSignature.toString()); - for (int i = 0; i < numDims; i++) { + protected void toStringInternal(final boolean useSimpleNames, final AnnotationInfoList annotationsToExclude, + final StringBuilder buf) { + // Start with innermost array element type + getElementTypeSignature().toStringInternal(useSimpleNames, annotationsToExclude, buf); + + // Append array dimensions + for (ArrayTypeSignature curr = this;;) { + if (curr.typeAnnotationInfo != null && !curr.typeAnnotationInfo.isEmpty()) { + for (final AnnotationInfo annotationInfo : curr.typeAnnotationInfo) { + if (buf.length() == 0 || buf.charAt(buf.length() - 1) != ' ') { + buf.append(' '); + } + buf.append(annotationInfo); + } + buf.append(' '); + } + buf.append("[]"); + + if (curr.nestedType instanceof ArrayTypeSignature) { + curr = (ArrayTypeSignature) curr.nestedType; + } else { + break; + } } - return buf.toString(); } /** diff --git a/src/main/java/io/github/classgraph/BaseTypeSignature.java b/src/main/java/io/github/classgraph/BaseTypeSignature.java index ffe11d1d5..6c1eb5488 100644 --- a/src/main/java/io/github/classgraph/BaseTypeSignature.java +++ b/src/main/java/io/github/classgraph/BaseTypeSignature.java @@ -28,141 +28,179 @@ */ package io.github.classgraph; +import java.util.List; +import java.util.Objects; import java.util.Set; +import io.github.classgraph.Classfile.TypePathNode; import nonapi.io.github.classgraph.types.Parser; /** A type signature for a base type (byte, char, double, float, int, long, short, boolean, or void). */ public class BaseTypeSignature extends TypeSignature { - /** A base type (byte, char, double, float, int, long, short, boolean, or void). */ - private final String baseType; - /** The type signature character used to represent the base type. */ - private final String typeSignatureChar; - - /** byte type signature. */ - private static final BaseTypeSignature BYTE = new BaseTypeSignature("byte", 'B'); - - /** char type signature. */ - private static final BaseTypeSignature CHAR = new BaseTypeSignature("char", 'C'); - - /** double type signature. */ - private static final BaseTypeSignature DOUBLE = new BaseTypeSignature("double", 'D'); - - /** float type signature. */ - private static final BaseTypeSignature FLOAT = new BaseTypeSignature("float", 'F'); - - /** int type signature. */ - private static final BaseTypeSignature INT = new BaseTypeSignature("int", 'I'); - - /** long type signature. */ - private static final BaseTypeSignature LONG = new BaseTypeSignature("long", 'J'); - - /** short type signature. */ - private static final BaseTypeSignature SHORT = new BaseTypeSignature("short", 'S'); - - /** boolean type signature. */ - private static final BaseTypeSignature BOOLEAN = new BaseTypeSignature("boolean", 'Z'); - - /** void type signature. */ - static final BaseTypeSignature VOID = new BaseTypeSignature("void", 'V'); + private final char typeSignatureChar; // ------------------------------------------------------------------------------------------------------------- /** * Constructor. - * - * @param baseType - * the base type */ - private BaseTypeSignature(final String baseType, final char typeSignatureChar) { + BaseTypeSignature(final char typeSignatureChar) { super(); - this.baseType = baseType; - this.typeSignatureChar = Character.toString(typeSignatureChar); + switch (typeSignatureChar) { + case 'B': + case 'C': + case 'D': + case 'F': + case 'I': + case 'J': + case 'S': + case 'Z': + case 'V': + this.typeSignatureChar = typeSignatureChar; + break; + default: + throw new IllegalArgumentException( + "Illegal " + BaseTypeSignature.class.getSimpleName() + " type: '" + typeSignatureChar + "'"); + } } // ------------------------------------------------------------------------------------------------------------- /** - * Get the type as a string. + * Get the name of the type as a string. * - * @return The base type, such as "int", "float", or "void". + * @param typeChar + * the type character, e.g. 'I'. + * @return The name of the type, e.g. "int", or null if there was no match. */ - public String getTypeStr() { - return baseType; + static String getTypeStr(final char typeChar) { + switch (typeChar) { + case 'B': + return "byte"; + case 'C': + return "char"; + case 'D': + return "double"; + case 'F': + return "float"; + case 'I': + return "int"; + case 'J': + return "long"; + case 'S': + return "short"; + case 'Z': + return "boolean"; + case 'V': + return "void"; + default: + return null; + } } /** - * Get the type signature char used to represent the type, e.g. "Z" for int. - * - * @return the type signature char, as a one-char String. + * Get the name of the type as a string. + * + * @param typeStr + * the type character, e.g. "int". + * @return The type, character, e.g. 'I', or '\0' if there was no match. */ - public String getTypeSignatureChar() { - return typeSignatureChar; + static char getTypeChar(final String typeStr) { + switch (typeStr) { + case "byte": + return 'B'; + case "char": + return 'C'; + case "double": + return 'D'; + case "float": + return 'F'; + case "int": + return 'I'; + case "long": + return 'J'; + case "short": + return 'S'; + case "boolean": + return 'Z'; + case "void": + return 'V'; + default: + return '\0'; + } } /** - * Get the type. + * Get the type for a type character. * - * @return The class of the base type, such as int.class, float.class, or void.class. + * @param typeChar + * the type character, e.g. 'I'. + * @return The type class, e.g. int.class, or null if there was no match. */ - public Class getType() { - switch (baseType) { - case "byte": + static Class getType(final char typeChar) { + switch (typeChar) { + case 'B': return byte.class; - case "char": + case 'C': return char.class; - case "double": + case 'D': return double.class; - case "float": + case 'F': return float.class; - case "int": + case 'I': return int.class; - case "long": + case 'J': return long.class; - case "short": + case 'S': return short.class; - case "boolean": + case 'Z': return boolean.class; - case "void": + case 'V': return void.class; default: - throw new IllegalArgumentException("Unknown base type " + baseType); + return null; } } + // ------------------------------------------------------------------------------------------------------------- + + /** + * Get the type signature char used to represent the type, e.g. 'I' for int. + * + * @return the type signature char, as a one-char String. + */ + public char getTypeSignatureChar() { + return typeSignatureChar; + } + /** - * Get the {@link BaseTypeSignature} for a given type name. + * Get the name of the type as a string. * - * @param typeName - * the name of the type. - * @return The {@link BaseTypeSignature} of the named base type, or null if typeName is not a base type. + * @return The name of the type, such as "int", "float", or "void". */ - public static BaseTypeSignature getTypeSignature(final String typeName) { - switch (typeName) { - case "byte": - return BYTE; - case "char": - return CHAR; - case "double": - return DOUBLE; - case "float": - return FLOAT; - case "int": - return INT; - case "long": - return LONG; - case "short": - return SHORT; - case "boolean": - return BOOLEAN; - case "void": - return VOID; - default: - return null; - } + public String getTypeStr() { + return getTypeStr(typeSignatureChar); + } + + /** + * Get the type. + * + * @return The class of the base type, such as int.class, float.class, or void.class. + */ + public Class getType() { + return getType(typeSignatureChar); + } + + // ------------------------------------------------------------------------------------------------------------- + + @Override + protected void addTypeAnnotation(final List typePath, final AnnotationInfo annotationInfo) { + addTypeAnnotation(annotationInfo); } + // ------------------------------------------------------------------------------------------------------------- + /* (non-Javadoc) * @see io.github.classgraph.ScanResultObject#loadClass() */ @@ -178,8 +216,8 @@ Class loadClass() { Class loadClass(final Class superclassOrInterfaceType) { final Class type = getType(); if (!superclassOrInterfaceType.isAssignableFrom(type)) { - throw new IllegalArgumentException( - "Primitive class " + baseType + " cannot be cast to " + superclassOrInterfaceType.getName()); + throw new IllegalArgumentException("Primitive class " + getTypeStr() + " cannot be cast to " + + superclassOrInterfaceType.getName()); } @SuppressWarnings("unchecked") final Class classT = (Class) type; @@ -199,31 +237,31 @@ static BaseTypeSignature parse(final Parser parser) { switch (parser.peek()) { case 'B': parser.next(); - return BYTE; + return new BaseTypeSignature('B'); case 'C': parser.next(); - return CHAR; + return new BaseTypeSignature('C'); case 'D': parser.next(); - return DOUBLE; + return new BaseTypeSignature('D'); case 'F': parser.next(); - return FLOAT; + return new BaseTypeSignature('F'); case 'I': parser.next(); - return INT; + return new BaseTypeSignature('I'); case 'J': parser.next(); - return LONG; + return new BaseTypeSignature('J'); case 'S': parser.next(); - return SHORT; + return new BaseTypeSignature('S'); case 'Z': parser.next(); - return BOOLEAN; + return new BaseTypeSignature('Z'); case 'V': parser.next(); - return VOID; + return new BaseTypeSignature('V'); default: return null; } @@ -236,7 +274,7 @@ static BaseTypeSignature parse(final Parser parser) { */ @Override protected String getClassName() { - return baseType; + return getTypeStr(); } /* (non-Javadoc) @@ -279,7 +317,7 @@ void setScanResult(final ScanResult scanResult) { */ @Override public int hashCode() { - return baseType.hashCode(); + return typeSignatureChar; } /* (non-Javadoc) @@ -292,7 +330,9 @@ public boolean equals(final Object obj) { } else if (!(obj instanceof BaseTypeSignature)) { return false; } - return ((BaseTypeSignature) obj).baseType.equals(this.baseType); + final BaseTypeSignature other = (BaseTypeSignature) obj; + return Objects.equals(this.typeAnnotationInfo, other.typeAnnotationInfo) + && other.typeSignatureChar == this.typeSignatureChar; } /* (non-Javadoc) @@ -303,14 +343,20 @@ public boolean equalsIgnoringTypeParams(final TypeSignature other) { if (!(other instanceof BaseTypeSignature)) { return false; } - return baseType.equals(((BaseTypeSignature) other).baseType); + return typeSignatureChar == ((BaseTypeSignature) other).typeSignatureChar; } - /* (non-Javadoc) - * @see io.github.classgraph.TypeSignature#toStringInternal(boolean) - */ @Override - protected String toStringInternal(final boolean useSimpleNames) { - return baseType; + protected void toStringInternal(final boolean useSimpleNames, final AnnotationInfoList annotationsToExclude, + final StringBuilder buf) { + if (typeAnnotationInfo != null) { + for (final AnnotationInfo annotationInfo : typeAnnotationInfo) { + if (annotationsToExclude == null || !annotationsToExclude.contains(annotationInfo)) { + buf.append(annotationInfo); + buf.append(' '); + } + } + } + buf.append(getTypeStr()); } } \ No newline at end of file diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index 4d44fb9ef..d36dae002 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -49,9 +49,11 @@ import java.util.Set; import io.github.classgraph.Classfile.ClassContainment; +import io.github.classgraph.Classfile.ClassTypeAnnotationDecorator; import nonapi.io.github.classgraph.json.Id; import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.types.ParseException; +import nonapi.io.github.classgraph.types.Parser; import nonapi.io.github.classgraph.types.TypeUtils; import nonapi.io.github.classgraph.types.TypeUtils.ModifierType; @@ -131,6 +133,9 @@ public class ClassInfo extends ScanResultObject implements Comparable /** For annotations, the default values of parameters. */ AnnotationParameterValueList annotationDefaultParamValues; + /** The type annotation decorators for the {@link ClassTypeSignature} instance. */ + List typeAnnotationDecorators; + /** * Names of classes referenced by this class in class refs and type signatures in the constant pool of the * classfile. @@ -346,10 +351,40 @@ static ClassInfo getOrCreateClassInfo(final String className, ClassInfo classInfo = classNameToClassInfo.get(className); if (classInfo == null) { - classNameToClassInfo.put(className, // - classInfo = numArrayDims == 0 // - ? new ClassInfo(baseClassName, /* classModifiers = */ 0, /* classfileResource = */ null) - : new ArrayClassInfo(new ArrayTypeSignature(baseClassName, numArrayDims))); + if (numArrayDims == 0) { + classInfo = new ClassInfo(baseClassName, /* classModifiers = */ 0, /* classfileResource = */ null); + } else { + final StringBuilder arrayTypeSigStrBuf = new StringBuilder(); + for (int i = 0; i < numArrayDims; i++) { + arrayTypeSigStrBuf.append('['); + } + TypeSignature elementTypeSignature; + final char baseTypeChar = BaseTypeSignature.getTypeChar(baseClassName); + if (baseTypeChar != '\0') { + // Element type is a base (primitive) type + arrayTypeSigStrBuf.append(baseTypeChar); + elementTypeSignature = new BaseTypeSignature(baseTypeChar); + } else { + // Element type is not a base (primitive) type -- create a type signature for element type + final String eltTypeSigStr = "L" + baseClassName.replace('.', '/') + ";"; + arrayTypeSigStrBuf.append(eltTypeSigStr); + try { + elementTypeSignature = ClassRefTypeSignature.parse(new Parser(eltTypeSigStr), + // No type variables to resolve for generic types + /* definingClassName = */ null); + if (elementTypeSignature == null) { + throw new IllegalArgumentException( + "Could not form array base type signature for class " + baseClassName); + } + } catch (final ParseException e) { + throw new IllegalArgumentException( + "Could not form array base type signature for class " + baseClassName); + } + } + classInfo = new ArrayClassInfo( + new ArrayTypeSignature(elementTypeSignature, numArrayDims, arrayTypeSigStrBuf.toString())); + } + classNameToClassInfo.put(className, classInfo); } return classInfo; } @@ -413,6 +448,19 @@ void setIsRecord(final boolean isRecord) { } } + /** + * Add {@link ClassTypeAnnotationDecorator} instances. + * + * @param classTypeAnnotationDecorators + * {@link ClassTypeAnnotationDecorator} instances. + */ + void addTypeDecorators(final List classTypeAnnotationDecorators) { + if (typeAnnotationDecorators == null) { + typeAnnotationDecorators = new ArrayList<>(); + } + typeAnnotationDecorators.addAll(classTypeAnnotationDecorators); + } + // ------------------------------------------------------------------------------------------------------------- /** @@ -1029,16 +1077,16 @@ public String getName() { } /** - * Get simple name from fully-qualified class name. Returns everything after the last '.' in the class name, or - * the whole string if the class is in the root package. (Note that this is not the same as the result of - * {@link Class#getSimpleName()}, which returns "" for anonymous classes.) + * Get simple name from fully-qualified class name. Returns everything after the last '.' or the last '$' in the + * class name, or the whole string if the class is in the root package. (Note that this is not the same as the + * result of {@link Class#getSimpleName()}, which returns "" for anonymous classes.) * * @param className * the class name * @return The simple name of the class. */ static String getSimpleName(final String className) { - return className.substring(className.lastIndexOf('.') + 1); + return className.substring(Math.max(className.lastIndexOf('.'), className.lastIndexOf('$')) + 1); } /** @@ -2586,6 +2634,11 @@ public ClassTypeSignature getTypeSignature() { 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(e); } @@ -2969,7 +3022,8 @@ protected String toString(final boolean typeNameOnly) { final ClassTypeSignature typeSig = getTypeSignature(); if (typeSig != null) { // Generic classes - return typeSig.toString(name, typeNameOnly, modifiers, isAnnotation(), isInterface()); + return typeSig.toString(name, /* useSimpleNames = */ false, typeNameOnly, modifiers, isAnnotation(), + isInterface(), /* annotationsToExclude = */ null); } else { // Non-generic classes final StringBuilder buf = new StringBuilder(); diff --git a/src/main/java/io/github/classgraph/ClassRefTypeSignature.java b/src/main/java/io/github/classgraph/ClassRefTypeSignature.java index e1cebf573..d20a6732e 100644 --- a/src/main/java/io/github/classgraph/ClassRefTypeSignature.java +++ b/src/main/java/io/github/classgraph/ClassRefTypeSignature.java @@ -31,8 +31,10 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.Set; +import io.github.classgraph.Classfile.TypePathNode; import nonapi.io.github.classgraph.types.ParseException; import nonapi.io.github.classgraph.types.Parser; import nonapi.io.github.classgraph.types.TypeUtils; @@ -42,21 +44,18 @@ public final class ClassRefTypeSignature extends ClassRefOrTypeVariableSignature /** The class name. */ final String className; - /** The class name and suffixes, without type arguments. */ - private String fullyQualifiedClassName; - /** The class type arguments. */ private final List typeArguments; - /** The class type signature suffix(es), or the empty list if no suffixes. */ + /** Type suffixes. */ private final List suffixes; - /** - * The suffix type arguments, one per suffix, or the empty list if no suffixes. The element value will be the - * empty list if there is no type argument for a given suffix. - */ + /** The suffix type arguments. */ private final List> suffixTypeArguments; + /** The suffix type annotations. */ + private List suffixTypeAnnotations; + // ------------------------------------------------------------------------------------------------------------- /** @@ -83,9 +82,10 @@ private ClassRefTypeSignature(final String className, final List t // ------------------------------------------------------------------------------------------------------------- /** - * Get the name of the base class. + * Get the name of the class, without any suffixes. * - * @return The name of the base class. + * @see #getFullyQualifiedClassName() + * @return The name of the class. */ public String getBaseClassName() { return className; @@ -105,16 +105,17 @@ public String getBaseClassName() { * @return The fully-qualified name of the class, including suffixes but without type arguments. */ public String getFullyQualifiedClassName() { - if (fullyQualifiedClassName == null) { + if (suffixes.isEmpty()) { + return className; + } else { final StringBuilder buf = new StringBuilder(); buf.append(className); for (final String suffix : suffixes) { buf.append('$'); buf.append(suffix); } - fullyQualifiedClassName = buf.toString(); + return buf.toString(); } - return fullyQualifiedClassName; } /** @@ -127,23 +128,111 @@ public List getTypeArguments() { } /** - * Get any suffixes of the class (typically nested inner class names). + * Get all nested suffixes of the class (typically nested inner class names). * - * @return The class suffixes (for inner classes). + * @return The class suffixes (for inner classes), or the empty list if none. */ public List getSuffixes() { return suffixes; } /** - * Get any type arguments for any suffixes of the class, one list per suffix. + * Get a list of type arguments for all nested suffixes of the class, one list per suffix. * - * @return The type arguments for the inner classes, one list per suffix. + * @return The list of type arguments for the suffixes (nested inner classes), one list per suffix, or the empty + * list if none. */ public List> getSuffixTypeArguments() { return suffixTypeArguments; } + /** + * Get a list of lists of type annotations for all nested suffixes of the class, one list per suffix. + * + * @return The list of lists of type annotations for the suffixes (nested inner classes), one list per suffix, + * or null if none. + */ + public List getSuffixTypeAnnotations() { + return suffixTypeAnnotations; + } + + private void addSuffixTypeAnnotation(final int suffixIdx, final AnnotationInfo annotationInfo) { + if (suffixTypeAnnotations == null) { + suffixTypeAnnotations = new ArrayList<>(suffixes.size()); + for (int i = 0; i < suffixes.size(); i++) { + suffixTypeAnnotations.add(new AnnotationInfoList(1)); + } + } + suffixTypeAnnotations.get(suffixIdx).add(annotationInfo); + } + + @Override + protected void addTypeAnnotation(final List typePath, final AnnotationInfo annotationInfo) { + // 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); + if (typePathNode.typePathKind == 1) { + numDeeperNestedLevels++; + } else if (typePathNode.typePathKind == 3) { + nextTypeArgIdx = typePathNode.typeArgumentIdx; + break; + } else { + throw new IllegalArgumentException("Bad typePathKind: " + typePathNode.typePathKind); + } + } + + // Figure out whether to index the base type or a suffix, skipping over non-nested class pairs + int suffixIdx = -1; + int nestingLevel = -1; + String typePrefix = className; + for (;;) { + boolean skipSuffix; + if (suffixIdx >= suffixes.size()) { + throw new IllegalArgumentException("Ran out of nested types while trying to add type annotation"); + } else if (suffixIdx == suffixes.size() - 1) { + // The suffix to the right cannot be static, because there are no suffixes to the right, + // so this suffix doesn't need to be skipped + skipSuffix = false; + } else { + // For suffix path X.Y, classes are not nested if Y is static + final ClassInfo outerClassInfo = scanResult.getClassInfo(typePrefix.toString()); + typePrefix = typePrefix + '$' + suffixes.get(suffixIdx + 1); + final ClassInfo innerClassInfo = scanResult.getClassInfo(typePrefix.toString()); + skipSuffix = outerClassInfo == null || innerClassInfo == null + || outerClassInfo.isInterfaceOrAnnotation() // + || innerClassInfo.isInterfaceOrAnnotation() // + || innerClassInfo.isStatic() // + || !outerClassInfo.getInnerClasses().contains(innerClassInfo); + } + if (!skipSuffix) { + // Found nested classes + nestingLevel++; + if (nestingLevel >= numDeeperNestedLevels) { + break; + } + } + suffixIdx++; + } + + if (nextTypeArgIdx == -1) { + // Reached end of path -- add type annotation + if (suffixIdx == -1) { + // Add type annotation to base type + addTypeAnnotation(annotationInfo); + } else { + // Add type annotation to suffix type + addSuffixTypeAnnotation(suffixIdx, annotationInfo); + } + } else { + final List typeArgumentList = suffixIdx == -1 ? typeArguments + : suffixTypeArguments.get(suffixIdx); + typeArgumentList.get(nextTypeArgIdx).addTypeAnnotation( + typePath.subList(numDeeperNestedLevels + 1, typePath.size()), annotationInfo); + } + } + // ------------------------------------------------------------------------------------------------------------- /** @@ -176,11 +265,7 @@ public Class loadClass() { // ------------------------------------------------------------------------------------------------------------- - /** - * Get the fully qualified class name (used by {@link #getClassInfo()} and {@link #loadClass()}. - * - * @return The fully qualified name of the class. - */ + /** @return the fully-qualified class name, for classloading. */ @Override protected String getClassName() { return getFullyQualifiedClassName(); @@ -205,16 +290,12 @@ public ClassInfo getClassInfo() { @Override void setScanResult(final ScanResult scanResult) { super.setScanResult(scanResult); - if (typeArguments != null) { - for (final TypeArgument typeArgument : typeArguments) { - typeArgument.setScanResult(scanResult); - } + for (final TypeArgument typeArgument : typeArguments) { + typeArgument.setScanResult(scanResult); } - if (suffixTypeArguments != null) { - for (final List list : suffixTypeArguments) { - for (final TypeArgument typeArgument : list) { - typeArgument.setScanResult(scanResult); - } + for (final List typeArgumentList : suffixTypeArguments) { + for (final TypeArgument typeArgument : typeArgumentList) { + typeArgument.setScanResult(scanResult); } } } @@ -231,6 +312,11 @@ protected void findReferencedClassNames(final Set refdClassNames) { for (final TypeArgument typeArgument : typeArguments) { typeArgument.findReferencedClassNames(refdClassNames); } + for (final List typeArgumentList : suffixTypeArguments) { + for (final TypeArgument typeArgument : typeArgumentList) { + typeArgument.findReferencedClassNames(refdClassNames); + } + } } // ------------------------------------------------------------------------------------------------------------- @@ -240,7 +326,15 @@ protected void findReferencedClassNames(final Set refdClassNames) { */ @Override public int hashCode() { - return className.hashCode() + 7 * typeArguments.hashCode() + 15 * suffixes.hashCode(); + return className.hashCode() + 7 * typeArguments.hashCode() + 15 * suffixTypeArguments.hashCode() + + 31 * (typeAnnotationInfo == null ? 0 : typeAnnotationInfo.hashCode()) + + 64 * (suffixTypeAnnotations == null ? 0 : suffixTypeAnnotations.hashCode()); + } + + private static boolean suffixesMatch(final ClassRefTypeSignature a, final ClassRefTypeSignature b) { + return a.suffixes.equals(b.suffixes) // + && a.suffixTypeArguments.equals(b.suffixTypeArguments) // + && Objects.equals(a.suffixTypeAnnotations, b.suffixTypeAnnotations); } /* (non-Javadoc) @@ -255,7 +349,7 @@ public boolean equals(final Object obj) { } final ClassRefTypeSignature o = (ClassRefTypeSignature) obj; return o.className.equals(this.className) && o.typeArguments.equals(this.typeArguments) - && o.suffixes.equals(this.suffixes); + && Objects.equals(this.typeAnnotationInfo, o.typeAnnotationInfo) && suffixesMatch(o, this); } /* (non-Javadoc) @@ -272,56 +366,77 @@ public boolean equalsIgnoringTypeParams(final TypeSignature other) { return false; } final ClassRefTypeSignature o = (ClassRefTypeSignature) other; - if (o.suffixes.equals(this.suffixes)) { - return o.className.equals(this.className); - } else { - return o.getFullyQualifiedClassName().equals(this.getFullyQualifiedClassName()); - } + return o.className.equals(this.className) && Objects.equals(this.typeAnnotationInfo, o.typeAnnotationInfo) + && suffixesMatch(o, this); } - /* (non-Javadoc) - * @see io.github.classgraph.TypeSignature#toStringInternal(boolean) - */ @Override - protected String toStringInternal(final boolean useSimpleNames) { - final StringBuilder buf = new StringBuilder(); - // Only append the base class name if not using simple names, or if there are no suffixes + protected void toStringInternal(final boolean useSimpleNames, final AnnotationInfoList annotationsToExclude, + final StringBuilder buf) { + // Only render the base class if not using simple names, or if there are no suffixes if (!useSimpleNames || suffixes.isEmpty()) { + // Append type annotations + if (typeAnnotationInfo != null) { + for (final AnnotationInfo annotationInfo : typeAnnotationInfo) { + if (annotationsToExclude == null || !annotationsToExclude.contains(annotationInfo)) { + buf.append(annotationInfo); + buf.append(' '); + } + } + } + // Append base class name buf.append(useSimpleNames ? ClassInfo.getSimpleName(className) : className); + // Append base class type arguments if (!typeArguments.isEmpty()) { buf.append('<'); for (int i = 0; i < typeArguments.size(); i++) { if (i > 0) { buf.append(", "); } - buf.append(useSimpleNames ? typeArguments.get(i).toStringWithSimpleNames() - : typeArguments.get(i).toString()); + buf.append(typeArguments.get(i).toString(useSimpleNames)); } buf.append('>'); } } - // Only use the last suffix if using simple names - for (int i = useSimpleNames && !suffixes.isEmpty() ? suffixes.size() - 1 : 0; i < suffixes.size(); 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 - buf.append('.'); - } - buf.append(suffixes.get(i)); - final List suffixTypeArgs = suffixTypeArguments.get(i); - if (!suffixTypeArgs.isEmpty()) { - buf.append('<'); - for (int j = 0; j < suffixTypeArgs.size(); j++) { - if (j > 0) { - buf.append(", "); + + // 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('.'); } - buf.append(useSimpleNames ? suffixTypeArgs.get(j).toStringWithSimpleNames() - : suffixTypeArgs.get(j).toString()); } - buf.append('>'); + if (typeAnnotations != null && !typeAnnotations.isEmpty()) { + for (final AnnotationInfo annotationInfo : typeAnnotations) { + buf.append(annotationInfo); + buf.append(' '); + } + } + // Append suffix name + buf.append(suffixes.get(i)); + // Append suffix type arguments + final List suffixTypeArgumentsList = suffixTypeArguments.get(i); + if (!suffixTypeArgumentsList.isEmpty()) { + buf.append('<'); + for (int j = 0; j < suffixTypeArgumentsList.size(); j++) { + if (j > 0) { + buf.append(", "); + } + buf.append(suffixTypeArgumentsList.get(j).toString(useSimpleNames)); + } + buf.append('>'); + } } } - return buf.toString(); } /** @@ -345,11 +460,11 @@ static ClassRefTypeSignature parse(final Parser parser, final String definingCla final List typeArguments = TypeArgument.parseList(parser, definingClassName); List suffixes; List> suffixTypeArguments; - if (parser.peek() == '.') { + if (parser.peek() == '.' || parser.peek() == '$') { suffixes = new ArrayList<>(); suffixTypeArguments = new ArrayList<>(); - while (parser.peek() == '.') { - parser.expect('.'); + while (parser.peek() == '.' || parser.peek() == '$') { + parser.advance(1); if (!TypeUtils.getIdentifierToken(parser, /* separator = */ '/', /* separatorReplace = */ '.')) { throw new ParseException(parser, "Could not parse identifier token"); diff --git a/src/main/java/io/github/classgraph/ClassTypeSignature.java b/src/main/java/io/github/classgraph/ClassTypeSignature.java index 387ecef45..0190660f7 100644 --- a/src/main/java/io/github/classgraph/ClassTypeSignature.java +++ b/src/main/java/io/github/classgraph/ClassTypeSignature.java @@ -35,11 +35,11 @@ import java.util.Map; import java.util.Set; +import io.github.classgraph.Classfile.TypePathNode; import nonapi.io.github.classgraph.types.ParseException; 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.Join; /** A class type signature (called "ClassSignature" in the classfile documentation). */ public final class ClassTypeSignature extends HierarchicalTypeSignature { @@ -110,6 +110,13 @@ public List getSuperinterfaceSignatures() { return superinterfaceSignatures; } + @Override + protected void addTypeAnnotation(final List typePath, final AnnotationInfo annotationInfo) { + // Individual parts of a class' type each have their own addTypeAnnotation methods + throw new IllegalArgumentException( + "Cannot call this method on " + ClassTypeSignature.class.getSimpleName()); + } + // ------------------------------------------------------------------------------------------------------------- /** @@ -273,15 +280,16 @@ public boolean equals(final Object obj) { * True if the class is an interface. * @return The String representation. */ - String toString(final String className, final boolean typeNameOnly, final int modifiers, - final boolean isAnnotation, final boolean isInterface) { + String toString(final String className, final boolean useSimpleNames, final boolean typeNameOnly, + final int modifiers, final boolean isAnnotation, final boolean isInterface, + final AnnotationInfoList annotationsToExclude) { final StringBuilder buf = new StringBuilder(); if (!typeNameOnly) { if (modifiers != 0) { TypeUtils.modifiersToString(modifiers, ModifierType.CLASS, /* ignored */ false, buf); - } - if (buf.length() > 0) { - buf.append(' '); + if (buf.length() > 0) { + buf.append(' '); + } } buf.append(isAnnotation ? "@interface" : isInterface ? "interface" : (modifiers & 0x4000) != 0 ? "enum" : "class"); @@ -291,30 +299,43 @@ String toString(final String className, final boolean typeNameOnly, final int mo buf.append(className); } if (!typeParameters.isEmpty()) { - Join.join(buf, "<", ", ", ">", typeParameters); + buf.append('<'); + for (int i = 0; i < typeParameters.size(); i++) { + if (i > 0) { + buf.append(", "); + } + typeParameters.get(i).toStringInternal(useSimpleNames, null, buf); + } + buf.append('>'); } if (!typeNameOnly) { if (superclassSignature != null) { - final String superSig = superclassSignature.toString(); - if (!superSig.equals("java.lang.Object")) { + final String superSig = superclassSignature.toString(useSimpleNames); + // superSig could have a class type annotation even if the superclass is Object + if (!superSig.equals("java.lang.Object") && !(superSig.equals("Object") + && superclassSignature.className.equals("java.lang.Object"))) { buf.append(" extends "); buf.append(superSig); } } if (!superinterfaceSignatures.isEmpty()) { buf.append(isInterface ? " extends " : " implements "); - Join.join(buf, "", ", ", "", superinterfaceSignatures); + for (int i = 0; i < superinterfaceSignatures.size(); i++) { + if (i > 0) { + buf.append(", "); + } + superinterfaceSignatures.get(i).toStringInternal(useSimpleNames, null, buf); + } } } return buf.toString(); } - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ + // TODO: useSimpleNames is being ignored @Override - public String toString() { - return toString(classInfo.getName(), /* typeNameOnly = */ false, classInfo.getModifiers(), - classInfo.isAnnotation(), classInfo.isInterface()); + protected void toStringInternal(final boolean useSimpleNames, final AnnotationInfoList annotationsToExclude, + final StringBuilder buf) { + buf.append(toString(classInfo.getName(), useSimpleNames, /* typeNameOnly = */ false, + classInfo.getModifiers(), classInfo.isAnnotation(), classInfo.isInterface(), annotationsToExclude)); } } \ No newline at end of file diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index 5c1fdcb31..dbf548f57 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -33,6 +33,7 @@ import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -125,7 +126,10 @@ class Classfile { private MethodInfoList methodInfoList; /** The type signature. */ - private String typeSignature; + private String typeSignatureStr; + + /** The type annotation decorators for the {@link ClassTypeSignature} instance. */ + private List classTypeAnnotationDecorators; /** The names of accepted classes found in the classpath while scanning paths within classpath elements. */ private final Set acceptedClassNamesFound; @@ -503,12 +507,15 @@ void link(final Map classNameToClassInfo, if (methodInfoList != null) { classInfo.addMethodInfo(methodInfoList, classNameToClassInfo); } - if (typeSignature != null) { - classInfo.setTypeSignature(typeSignature); + if (typeSignatureStr != null) { + classInfo.setTypeSignature(typeSignatureStr); } if (refdClassNames != null) { classInfo.addReferencedClassNames(refdClassNames); } + if (classTypeAnnotationDecorators != null) { + classInfo.addTypeDecorators(classTypeAnnotationDecorators); + } } // Get or create PackageInfo, if this is not a module descriptor (the module descriptor's package is "") @@ -1011,6 +1018,50 @@ private Object readAnnotationElementValue() throws IOException { // ------------------------------------------------------------------------------------------------------------- + static interface ClassTypeAnnotationDecorator { + void decorate(ClassTypeSignature classTypeSignature); + } + + static interface MethodTypeAnnotationDecorator { + void decorate(MethodTypeSignature methodTypeSignature); + } + + static interface TypeAnnotationDecorator { + void decorate(TypeSignature typeSignature); + } + + static class TypePathNode { + short typePathKind; + short typeArgumentIdx; + + public TypePathNode(final int typePathKind, final int typeArgumentIdx) { + this.typePathKind = (short) typePathKind; + this.typeArgumentIdx = (short) typeArgumentIdx; + } + + @Override + public String toString() { + return "(" + typePathKind + "," + typeArgumentIdx + ")"; + } + } + + private List readTypePath() throws IOException { + final int typePathLength = reader.readUnsignedByte(); + if (typePathLength == 0) { + return Collections.emptyList(); + } else { + final List list = new ArrayList<>(typePathLength); + for (int i = 0; i < typePathLength; i++) { + final int typePathKind = reader.readUnsignedByte(); + final int typeArgumentIdx = reader.readUnsignedByte(); + list.add(new TypePathNode(typePathKind, typeArgumentIdx)); + } + return list; + } + } + + // ------------------------------------------------------------------------------------------------------------- + /** * Read constant pool entries. * @@ -1271,6 +1322,7 @@ private void readFields() throws IOException, ClassfileFormatException { final boolean fieldIsVisible = isPublicField || scanSpec.ignoreFieldVisibility; final boolean getStaticFinalFieldConstValue = scanSpec.enableStaticFinalFieldConstantInitializerValues && fieldIsVisible; + List fieldTypeAnnotationDecorators = null; if (!fieldIsVisible || (!scanSpec.enableFieldInfo && !getStaticFinalFieldConstValue)) { // Skip field reader.readUnsignedShort(); // fieldNameCpIdx @@ -1288,7 +1340,7 @@ private void readFields() throws IOException, ClassfileFormatException { final char fieldTypeDescriptorFirstChar = (char) getConstantPoolStringFirstByte( fieldTypeDescriptorCpIdx); String fieldTypeDescriptor; - String fieldTypeSignature = null; + String fieldTypeSignatureStr = null; fieldTypeDescriptor = getConstantPoolString(fieldTypeDescriptorCpIdx); Object fieldConstValue = null; @@ -1312,7 +1364,7 @@ && constantPoolStringEquals(attributeNameCpIdx, "ConstantValue")) { fieldConstValue = getFieldConstantPoolValue(entryTag[cpIdx], fieldTypeDescriptorFirstChar, cpIdx); } else if (fieldIsVisible && constantPoolStringEquals(attributeNameCpIdx, "Signature")) { - fieldTypeSignature = getConstantPoolString(reader.readUnsignedShort()); + fieldTypeSignatureStr = getConstantPoolString(reader.readUnsignedShort()); } else if (scanSpec.enableAnnotationInfo // && (constantPoolStringEquals(attributeNameCpIdx, "RuntimeVisibleAnnotations") || (!scanSpec.disableRuntimeInvisibleAnnotations && constantPoolStringEquals( @@ -1328,6 +1380,33 @@ && constantPoolStringEquals(attributeNameCpIdx, "ConstantValue")) { fieldAnnotationInfo.add(fieldAnnotation); } } + } else if (scanSpec.enableAnnotationInfo // + && (constantPoolStringEquals(attributeNameCpIdx, "RuntimeVisibleTypeAnnotations") + || (!scanSpec.disableRuntimeInvisibleAnnotations && constantPoolStringEquals( + attributeNameCpIdx, "RuntimeInvisibleTypeAnnotations")))) { + final int annotationCount = reader.readUnsignedShort(); + if (annotationCount > 0) { + fieldTypeAnnotationDecorators = new ArrayList<>(); + for (int m = 0; m < annotationCount; m++) { + final int targetType = reader.readUnsignedByte(); + if (targetType != 0x13) { + throw new ClassfileFormatException( + "Class " + className + " has unknown field type annotation target 0x" + + Integer.toHexString(targetType) + + ": element size unknown, cannot continue reading class. " + + "Please report this at " + + "https://github.com/classgraph/classgraph/issues"); + } + final List typePath = readTypePath(); + final AnnotationInfo annotationInfo = readAnnotation(); + fieldTypeAnnotationDecorators.add(new TypeAnnotationDecorator() { + @Override + public void decorate(final TypeSignature typeSignature) { + typeSignature.addTypeAnnotation(typePath, annotationInfo); + } + }); + } + } } else { // No match, just skip attribute reader.skip(attributeLength); @@ -1338,7 +1417,8 @@ && constantPoolStringEquals(attributeNameCpIdx, "ConstantValue")) { fieldInfoList = new FieldInfoList(); } fieldInfoList.add(new FieldInfo(className, fieldName, fieldModifierFlags, fieldTypeDescriptor, - fieldTypeSignature, fieldConstValue, fieldAnnotationInfo)); + fieldTypeSignatureStr, fieldConstValue, fieldAnnotationInfo, + fieldTypeAnnotationDecorators)); } } } @@ -1362,10 +1442,10 @@ private void readMethods() throws IOException, ClassfileFormatException { final int methodModifierFlags = reader.readUnsignedShort(); final boolean isPublicMethod = ((methodModifierFlags & 0x0001) == 0x0001); final boolean methodIsVisible = isPublicMethod || scanSpec.ignoreMethodVisibility; - + List methodTypeAnnotationDecorators = null; String methodName = null; String methodTypeDescriptor = null; - String methodTypeSignature = null; + String methodTypeSignatureStr = null; // Always enable MethodInfo for annotations (this is how annotation constants are defined) final boolean enableMethodInfo = scanSpec.enableMethodInfo || isAnnotation; if (enableMethodInfo || isAnnotation) { // Annotations store defaults in method_info @@ -1443,6 +1523,114 @@ private void readMethods() throws IOException, ClassfileFormatException { methodParameterAnnotations[paramIdx] = NO_ANNOTATIONS; } } + } else if (scanSpec.enableAnnotationInfo // + && (constantPoolStringEquals(attributeNameCpIdx, "RuntimeVisibleTypeAnnotations") + || (!scanSpec.disableRuntimeInvisibleAnnotations && constantPoolStringEquals( + attributeNameCpIdx, "RuntimeInvisibleTypeAnnotations")))) { + final int annotationCount = reader.readUnsignedShort(); + if (annotationCount > 0) { + methodTypeAnnotationDecorators = new ArrayList<>(annotationCount); + for (int m = 0; m < annotationCount; m++) { + final int targetType = reader.readUnsignedByte(); + int typeParameterIndex; + int boundIndex; + int formalParameterIndex; + int throwsTypeIndex; + if (targetType == 0x01) { + typeParameterIndex = reader.readUnsignedByte(); + boundIndex = -1; + formalParameterIndex = -1; + throwsTypeIndex = -1; + } else if (targetType == 0x12) { + typeParameterIndex = reader.readUnsignedByte(); + boundIndex = reader.readUnsignedByte(); + formalParameterIndex = -1; + throwsTypeIndex = -1; + } else if (targetType == 0x14) { + typeParameterIndex = -1; + boundIndex = -1; + formalParameterIndex = -1; + throwsTypeIndex = -1; + } else if (targetType == 0x15) { + typeParameterIndex = -1; + boundIndex = -1; + formalParameterIndex = -1; + throwsTypeIndex = -1; + } else if (targetType == 0x16) { + formalParameterIndex = reader.readUnsignedByte(); + typeParameterIndex = -1; + boundIndex = -1; + throwsTypeIndex = -1; + } else if (targetType == 0x17) { + throwsTypeIndex = reader.readUnsignedShort(); + typeParameterIndex = -1; + boundIndex = -1; + formalParameterIndex = -1; + } else { + throw new ClassfileFormatException( + "Class " + className + " has unknown method type annotation target 0x" + + Integer.toHexString(targetType) + + ": element size unknown, cannot continue reading class. " + + "Please report this at " + + "https://github.com/classgraph/classgraph/issues"); + } + final List typePath = readTypePath(); + final AnnotationInfo annotationInfo = readAnnotation(); + methodTypeAnnotationDecorators.add(new MethodTypeAnnotationDecorator() { + @Override + public void decorate(final MethodTypeSignature methodTypeSignature) { + if (targetType == 0x01) { + // Type parameter declaration of generic method or constructor + methodTypeSignature.getTypeParameters().get(typeParameterIndex) + .addTypeAnnotation(typePath, annotationInfo); + } else if (targetType == 0x12) { + // Type in bound of type parameter declaration of generic method or constructor + final TypeParameter typeParameter = methodTypeSignature + .getTypeParameters().get(typeParameterIndex); + // TODO: documentation does not specify if bound index should be treated this way ******************* + if (boundIndex == 0) { + typeParameter.getClassBound().addTypeAnnotation(typePath, + annotationInfo); + } else { + typeParameter.getInterfaceBounds().get(boundIndex - 1) + .addTypeAnnotation(typePath, annotationInfo); + } + } else if (targetType == 0x14) { + // Return type of method, or type of newly constructed object + methodTypeSignature.getResultType().addTypeAnnotation(typePath, + annotationInfo); + } else if (targetType == 0x15) { + // Receiver type of method or constructor (explicit receiver parameter) + final List paramTypeSignatures = methodTypeSignature + .getParameterTypeSignatures(); + if (paramTypeSignatures.size() == 0) { + throw new IllegalArgumentException( + "No explicit receiver parameter"); + } + // TODO: is this the right way to apply a receiver type annotation? + final TypeSignature receiverParamTypeSignature = paramTypeSignatures + .get(0); + receiverParamTypeSignature.addTypeAnnotation(typePath, annotationInfo); + } else if (targetType == 0x16) { + // Type in formal parameter declaration of method, constructor, + // or lambda expression + // N.B. formal parameter indices are dodgy, because not all compilers + // index parameters the same way -- so be robust here + final List parameterTypeSignatures = methodTypeSignature + .getParameterTypeSignatures(); + if (formalParameterIndex < parameterTypeSignatures.size()) { + parameterTypeSignatures.get(formalParameterIndex) + .addTypeAnnotation(typePath, annotationInfo); + } + } else if (targetType == 0x17) { + // Type in throws clause of method or constructor + methodTypeSignature.getThrowsSignatures().get(throwsTypeIndex) + .addTypeAnnotation(typePath, annotationInfo); + } + } + }); + } + } } else if (constantPoolStringEquals(attributeNameCpIdx, "MethodParameters")) { // Read method parameters. For Java, these are only produced in JDK8+, and only if the // commandline switch `-parameters` is provided at compiletime. @@ -1457,7 +1645,7 @@ private void readMethods() throws IOException, ClassfileFormatException { } } else if (constantPoolStringEquals(attributeNameCpIdx, "Signature")) { // Add type params to method type signature - methodTypeSignature = getConstantPoolString(reader.readUnsignedShort()); + methodTypeSignatureStr = getConstantPoolString(reader.readUnsignedShort()); } else if (constantPoolStringEquals(attributeNameCpIdx, "AnnotationDefault")) { if (annotationParamDefaultValues == null) { annotationParamDefaultValues = new AnnotationParameterValueList(); @@ -1478,8 +1666,9 @@ private void readMethods() throws IOException, ClassfileFormatException { methodInfoList = new MethodInfoList(); } methodInfoList.add(new MethodInfo(className, methodName, methodAnnotationInfo, - methodModifierFlags, methodTypeDescriptor, methodTypeSignature, methodParameterNames, - methodParameterModifiers, methodParameterAnnotations, methodHasBody)); + methodModifierFlags, methodTypeDescriptor, methodTypeSignatureStr, methodParameterNames, + methodParameterModifiers, methodParameterAnnotations, methodHasBody, + methodTypeAnnotationDecorators)); } } } @@ -1514,6 +1703,72 @@ private void readClassAttributes() throws IOException, ClassfileFormatException classAnnotations.add(readAnnotation()); } } + } else if (scanSpec.enableAnnotationInfo // + && (constantPoolStringEquals(attributeNameCpIdx, "RuntimeVisibleTypeAnnotations") + || (!scanSpec.disableRuntimeInvisibleAnnotations && constantPoolStringEquals( + attributeNameCpIdx, "RuntimeInvisibleTypeAnnotations")))) { + final int annotationCount = reader.readUnsignedShort(); + if (annotationCount > 0) { + classTypeAnnotationDecorators = new ArrayList<>(annotationCount); + for (int m = 0; m < annotationCount; m++) { + final int targetType = reader.readUnsignedByte(); + int typeParameterIndex; + int supertypeIndex; + int boundIndex; + if (targetType == 0x00) { + typeParameterIndex = reader.readUnsignedByte(); + supertypeIndex = -1; + boundIndex = -1; + } else if (targetType == 0x10) { + supertypeIndex = reader.readUnsignedShort(); + typeParameterIndex = -1; + boundIndex = -1; + } else if (targetType == 0x11) { + typeParameterIndex = reader.readUnsignedByte(); + boundIndex = reader.readUnsignedByte(); + supertypeIndex = -1; + } else { + throw new ClassfileFormatException("Class " + className + + " has unknown class type annotation target 0x" + + Integer.toHexString(targetType) + + ": element size unknown, cannot continue reading class. " + + "Please report this at https://github.com/classgraph/classgraph/issues"); + } + final List typePath = readTypePath(); + final AnnotationInfo annotationInfo = readAnnotation(); + classTypeAnnotationDecorators.add(new ClassTypeAnnotationDecorator() { + @Override + public void decorate(final ClassTypeSignature classTypeSignature) { + if (targetType == 0x00) { + // Type parameter declaration of generic class or interface + classTypeSignature.getTypeParameters().get(typeParameterIndex) + .addTypeAnnotation(typePath, annotationInfo); + } else if (targetType == 0x10) { + if (supertypeIndex == 65535) { + // Type in extends clause of class declaration + classTypeSignature.getSuperclassSignature().addTypeAnnotation(typePath, + annotationInfo); + } else { + // Type in implements clause of interface declaration + classTypeSignature.getSuperinterfaceSignatures().get(supertypeIndex) + .addTypeAnnotation(typePath, annotationInfo); + } + } else if (targetType == 0x11) { + // Type in bound of type parameter declaration of generic class or interface + final TypeParameter typeParameter = classTypeSignature.getTypeParameters() + .get(typeParameterIndex); + // TODO: documentation does not specify if bound index should be treated this way ******************* + if (boundIndex == 0) { + typeParameter.getClassBound().addTypeAnnotation(typePath, annotationInfo); + } else { + typeParameter.getInterfaceBounds().get(boundIndex - 1) + .addTypeAnnotation(typePath, annotationInfo); + } + } + } + }); + } + } } else if (constantPoolStringEquals(attributeNameCpIdx, "Record")) { isRecord = true; // No need to read record_components_info entries -- there is a 1:1 correspondence between @@ -1552,7 +1807,7 @@ private void readClassAttributes() throws IOException, ClassfileFormatException } } else if (constantPoolStringEquals(attributeNameCpIdx, "Signature")) { // Get class type signature, including type variables - typeSignature = getConstantPoolString(reader.readUnsignedShort()); + typeSignatureStr = getConstantPoolString(reader.readUnsignedShort()); } else if (constantPoolStringEquals(attributeNameCpIdx, "EnclosingMethod")) { final String innermostEnclosingClassName = getConstantPoolClassName(reader.readUnsignedShort()); final int enclosingMethodCpIdx = reader.readUnsignedShort(); @@ -1692,7 +1947,7 @@ private void readClassAttributes() throws IOException, ClassfileFormatException subLog.log("Interfaces: " + Join.join(", ", implementedInterfaces)); } if (classAnnotations != null) { - subLog.log("Class annotations: " + Join.join(", ", classAnnotations)); + subLog.log("Class annotations: " + Join.join(", ", classAnnotations.getNames())); } if (annotationParamDefaultValues != null) { for (final AnnotationParameterValue apv : annotationParamDefaultValues) { @@ -1701,27 +1956,16 @@ private void readClassAttributes() throws IOException, ClassfileFormatException } if (fieldInfoList != null) { for (final FieldInfo fieldInfo : fieldInfoList) { - subLog.log("Field: " + fieldInfo); + subLog.log("Field: " + fieldInfo.getName()); } } if (methodInfoList != null) { for (final MethodInfo methodInfo : methodInfoList) { - subLog.log("Method: " + methodInfo); + subLog.log("Method: " + methodInfo.getName()); } } - if (typeSignature != null) { - ClassTypeSignature typeSig = null; - try { - typeSig = ClassTypeSignature.parse(typeSignature, /* classInfo = */ null); - if (refdClassNames != null) { - typeSig.findReferencedClassNames(refdClassNames); - } - } catch (final ParseException e) { - // Ignore - } - subLog.log("Class type signature: " + (typeSig == null ? typeSignature - : typeSig.toString(className, /* typeNameOnly = */ false, classModifiers, isAnnotation, - isInterface))); + if (typeSignatureStr != null) { + subLog.log("Class type signature: " + typeSignatureStr); } if (refdClassNames != null) { final List refdClassNamesSorted = new ArrayList<>(refdClassNames); diff --git a/src/main/java/io/github/classgraph/FieldInfo.java b/src/main/java/io/github/classgraph/FieldInfo.java index 6d5a8408b..086d4b5b5 100644 --- a/src/main/java/io/github/classgraph/FieldInfo.java +++ b/src/main/java/io/github/classgraph/FieldInfo.java @@ -31,10 +31,12 @@ import java.lang.annotation.Repeatable; import java.lang.reflect.Field; import java.lang.reflect.Modifier; +import java.util.List; import java.util.Map; import java.util.Set; import io.github.classgraph.ClassInfo.RelType; +import io.github.classgraph.Classfile.TypeAnnotationDecorator; import nonapi.io.github.classgraph.types.ParseException; import nonapi.io.github.classgraph.types.TypeUtils; import nonapi.io.github.classgraph.types.TypeUtils.ModifierType; @@ -72,6 +74,9 @@ public class FieldInfo extends ScanResultObject implements Comparable /** The annotation on the field, if any. */ AnnotationInfoList annotationInfo; + /** The type annotation decorators for the {@link TypeSignature} instance of this field. */ + private List typeAnnotationDecorators; + // ------------------------------------------------------------------------------------------------------------- /** Default constructor for deserialization. */ @@ -99,7 +104,7 @@ 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 AnnotationInfoList annotationInfo, final List typeAnnotationDecorators) { super(); if (fieldName == null) { throw new IllegalArgumentException(); @@ -113,6 +118,7 @@ public class FieldInfo extends ScanResultObject implements Comparable this.constantInitializerValue = constantInitializerValue == null ? null : new ObjectTypedValueWrapper(constantInitializerValue); this.annotationInfo = annotationInfo == null || annotationInfo.isEmpty() ? null : annotationInfo; + this.typeAnnotationDecorators = typeAnnotationDecorators; } // ------------------------------------------------------------------------------------------------------------- @@ -210,6 +216,11 @@ public TypeSignature getTypeDescriptor() { 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); } @@ -242,6 +253,11 @@ public TypeSignature getTypeSignature() { 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(e); } @@ -531,7 +547,8 @@ public String toString() { if (buf.length() > 0) { buf.append(' '); } - buf.append(getTypeSignatureOrTypeDescriptor().toString()); + final TypeSignature typeSig = getTypeSignatureOrTypeDescriptor(); + typeSig.toStringInternal(/* useSimpleNames = */ false, /* annotationsToExclude = */ annotationInfo, buf); buf.append(' '); buf.append(name); diff --git a/src/main/java/io/github/classgraph/HierarchicalTypeSignature.java b/src/main/java/io/github/classgraph/HierarchicalTypeSignature.java index 35edb6dfd..712c2bb25 100644 --- a/src/main/java/io/github/classgraph/HierarchicalTypeSignature.java +++ b/src/main/java/io/github/classgraph/HierarchicalTypeSignature.java @@ -28,8 +28,101 @@ */ package io.github.classgraph; +import java.util.List; + +import io.github.classgraph.Classfile.TypePathNode; + /** * A Java type signature. Subclasses are ClassTypeSignature, MethodTypeSignature, and TypeSignature. */ public abstract class HierarchicalTypeSignature extends ScanResultObject { + protected AnnotationInfoList typeAnnotationInfo; + + /** + * 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. + * + * @param annotationInfo + * the annotation + */ + protected void addTypeAnnotation(final AnnotationInfo annotationInfo) { + if (typeAnnotationInfo == null) { + typeAnnotationInfo = new AnnotationInfoList(1); + } + typeAnnotationInfo.add(annotationInfo); + } + + @Override + void setScanResult(final ScanResult scanResult) { + super.setScanResult(scanResult); + if (typeAnnotationInfo != null) { + for (final AnnotationInfo annotationInfo : typeAnnotationInfo) { + annotationInfo.setScanResult(scanResult); + } + } + } + + /** + * Add a type annotation. + * + * @param typePath + * the type path + * @param annotationInfo + * the annotation + */ + protected abstract void addTypeAnnotation(List typePath, AnnotationInfo annotationInfo); + + /** + * {@link #toString()} internal method. + * + * @param useSimpleNames + * whether to use simple names for classes. + * @param annotationsToExclude + * toplevel annotations to exclude, to eliminate duplication (toplevel annotations are both + * class/field/method annotations and type annotations). + * @param buf + * the {@link StringBuilder} to write to. + */ + abstract void toStringInternal(final boolean useSimpleNames, AnnotationInfoList annotationsToExclude, + StringBuilder buf); + + /** + * {@link #toString()} that renders only simple names for classes. + * + * @return the string representation of the type + */ + public String toStringWithSimpleNames() { + final StringBuilder buf = new StringBuilder(); + toStringInternal(true, null, buf); + return buf.toString(); + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + final StringBuilder buf = new StringBuilder(); + toStringInternal(false, null, buf); + return buf.toString(); + } + + /** + * {@link #toString()} that renders only simple names for classes if requested. + * + * @param useSimpleNames + * if true, use just the simple name of each class. + * @return the string representation of the type + */ + public String toString(final boolean useSimpleNames) { + return useSimpleNames ? toStringWithSimpleNames() : toString(); + } } \ No newline at end of file diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index 72a8614e3..e02fb5a0d 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -38,6 +38,7 @@ import java.util.Set; import io.github.classgraph.ClassInfo.RelType; +import io.github.classgraph.Classfile.MethodTypeAnnotationDecorator; import nonapi.io.github.classgraph.types.ParseException; import nonapi.io.github.classgraph.types.TypeUtils; import nonapi.io.github.classgraph.types.TypeUtils.ModifierType; @@ -98,6 +99,9 @@ public class MethodInfo extends ScanResultObject implements Comparable typeAnnotationDecorators; + // ------------------------------------------------------------------------------------------------------------- /** Default constructor for deserialization. */ @@ -132,7 +136,8 @@ public class MethodInfo extends ScanResultObject implements Comparable methodTypeAnnotationDecorators) { super(); this.declaringClassName = definingClassName; this.name = methodName; @@ -145,6 +150,7 @@ public class MethodInfo extends ScanResultObject implements Comparable 0) { buf.append(' '); } - buf.append(methodType.getResultType().toString()); + methodType.getResultType().toStringInternal(/* useSimpleNames = */ false, + /* annotationsToExclude = */ annotationInfo, buf); } buf.append(' '); @@ -928,8 +945,10 @@ public String toString() { throw new IllegalArgumentException( "Got a zero-dimension array type for last parameter of varargs method " + name); } - buf.append(new ArrayTypeSignature(arrayType.getElementTypeSignature(), - arrayType.getNumDimensions() - 1, /* unused */ null).toString()); + buf.append(arrayType.getElementTypeSignature().toString()); + for (int j = 0; j < arrayType.getNumDimensions() - 1; j++) { + buf.append("[]"); + } buf.append("..."); } else { buf.append(paramType.toString()); diff --git a/src/main/java/io/github/classgraph/MethodTypeSignature.java b/src/main/java/io/github/classgraph/MethodTypeSignature.java index f6ae852c3..7b2904d7b 100644 --- a/src/main/java/io/github/classgraph/MethodTypeSignature.java +++ b/src/main/java/io/github/classgraph/MethodTypeSignature.java @@ -35,6 +35,7 @@ import java.util.Map; import java.util.Set; +import io.github.classgraph.Classfile.TypePathNode; import nonapi.io.github.classgraph.types.ParseException; import nonapi.io.github.classgraph.types.Parser; @@ -116,6 +117,13 @@ public List getThrowsSignatures() { return throwsSignatures; } + @Override + protected void addTypeAnnotation(final List typePath, final AnnotationInfo annotationInfo) { + // Individual parts of a class' type each have their own addTypeAnnotation methods + throw new IllegalArgumentException( + "Cannot call this method on " + MethodTypeSignature.class.getSimpleName()); + } + // ------------------------------------------------------------------------------------------------------------- /** @@ -135,7 +143,7 @@ static MethodTypeSignature parse(final String typeDescriptor, final String defin // Special case for instance initialization method signatures in a CONSTANT_NameAndType_info structure: // https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-4.html#jvms-4.4.2 return new MethodTypeSignature(Collections. emptyList(), - Collections. emptyList(), BaseTypeSignature.VOID, + Collections. emptyList(), /* void */ new BaseTypeSignature('V'), Collections. emptyList()); } final Parser parser = new Parser(typeDescriptor); @@ -313,20 +321,16 @@ public boolean equals(final Object obj) { && o.resultType.equals(this.resultType) && o.throwsSignatures.equals(this.throwsSignatures); } - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ @Override - public String toString() { - final StringBuilder buf = new StringBuilder(); - + protected void toStringInternal(final boolean useSimpleNames, final AnnotationInfoList annotationsToExclude, + final StringBuilder buf) { if (!typeParameters.isEmpty()) { buf.append('<'); for (int i = 0; i < typeParameters.size(); i++) { if (i > 0) { buf.append(", "); } - final String typeParamStr = typeParameters.get(i).toString(); + final String typeParamStr = typeParameters.get(i).toString(useSimpleNames); buf.append(typeParamStr); } buf.append('>'); @@ -342,7 +346,7 @@ public String toString() { if (i > 0) { buf.append(", "); } - buf.append(parameterTypeSignatures.get(i).toString()); + buf.append(parameterTypeSignatures.get(i).toString(useSimpleNames)); } buf.append(')'); @@ -352,9 +356,8 @@ public String toString() { if (i > 0) { buf.append(", "); } - buf.append(throwsSignatures.get(i).toString()); + buf.append(throwsSignatures.get(i).toString(useSimpleNames)); } } - return buf.toString(); } } \ No newline at end of file diff --git a/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java b/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java index 4254283ae..815745a9c 100644 --- a/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java +++ b/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java @@ -313,8 +313,7 @@ private Object getArrayValueClassOrName(final ClassInfo annotationClassInfo, fin if (elementTypeSig instanceof ClassRefTypeSignature) { // Look up the name of the element type, for non-primitive arrays final ClassRefTypeSignature classRefTypeSignature = (ClassRefTypeSignature) elementTypeSig; - return getClass ? classRefTypeSignature.loadClass() - : classRefTypeSignature.getFullyQualifiedClassName(); + return getClass ? classRefTypeSignature.loadClass() : classRefTypeSignature.getClassName(); } else if (elementTypeSig instanceof BaseTypeSignature) { // Look up the name of the primitive class, for primitive arrays final BaseTypeSignature baseTypeSignature = (BaseTypeSignature) elementTypeSig; diff --git a/src/main/java/io/github/classgraph/TypeArgument.java b/src/main/java/io/github/classgraph/TypeArgument.java index e7d8c19db..30a3920d3 100644 --- a/src/main/java/io/github/classgraph/TypeArgument.java +++ b/src/main/java/io/github/classgraph/TypeArgument.java @@ -31,8 +31,10 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.Set; +import io.github.classgraph.Classfile.TypePathNode; import nonapi.io.github.classgraph.types.ParseException; import nonapi.io.github.classgraph.types.Parser; @@ -95,6 +97,20 @@ public ReferenceTypeSignature getTypeSignature() { return typeSignature; } + @Override + protected void addTypeAnnotation(final List typePath, final AnnotationInfo annotationInfo) { + if (typePath.size() == 0 && wildcard != Wildcard.NONE) { + // 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); + } else { + // Annotation is on a type argument of a parameterized type + typeSignature.addTypeAnnotation(typePath, annotationInfo); + } + } + // ------------------------------------------------------------------------------------------------------------- /** @@ -229,48 +245,39 @@ public boolean equals(final Object obj) { } else if (!(obj instanceof TypeArgument)) { return false; } - final TypeArgument o = (TypeArgument) obj; - return (o.typeSignature.equals(this.typeSignature) && o.wildcard.equals(this.wildcard)); + final TypeArgument other = (TypeArgument) obj; + return Objects.equals(this.typeAnnotationInfo, other.typeAnnotationInfo) + && (other.typeSignature.equals(this.typeSignature) && other.wildcard.equals(this.wildcard)); } - /** - * {@link #toString()} internal method. - * - * @param useSimpleNames - * whether to use simple names for classes. - * @return the string - */ - private String toStringInternal(final boolean useSimpleNames) { + @Override + protected void toStringInternal(final boolean useSimpleNames, final AnnotationInfoList annotationsToExclude, + final StringBuilder buf) { + if (typeAnnotationInfo != null) { + for (final AnnotationInfo annotationInfo : typeAnnotationInfo) { + if (annotationsToExclude == null || !annotationsToExclude.contains(annotationInfo)) { + buf.append(annotationInfo); + buf.append(' '); + } + } + } switch (wildcard) { case ANY: - return "?"; + buf.append('?'); + break; case EXTENDS: - final String typeSigStr = typeSignature.toString(); - return typeSigStr.equals("java.lang.Object") ? "?" : "? extends " + typeSigStr; + final String typeSigStr = typeSignature.toString(useSimpleNames); + buf.append(typeSigStr.equals("java.lang.Object") ? "?" : "? extends " + typeSigStr); + break; case SUPER: - return "? super " + typeSignature.toString(); + buf.append("? super " + typeSignature.toString(useSimpleNames)); + break; case NONE: - return useSimpleNames ? typeSignature.toStringWithSimpleNames() : typeSignature.toString(); + buf.append(typeSignature.toString(useSimpleNames)); + break; default: // Should not happen throw ClassGraphException.newClassGraphException("Unknown wildcard type " + wildcard); } } - - /** - * {@link #toString()} with simple names for classes. - * - * @return the string - */ - public String toStringWithSimpleNames() { - return toStringInternal(true); - } - - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return toStringInternal(false); - } } diff --git a/src/main/java/io/github/classgraph/TypeParameter.java b/src/main/java/io/github/classgraph/TypeParameter.java index c4e47431b..1b693bbd4 100644 --- a/src/main/java/io/github/classgraph/TypeParameter.java +++ b/src/main/java/io/github/classgraph/TypeParameter.java @@ -31,8 +31,10 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.Set; +import io.github.classgraph.Classfile.TypePathNode; import nonapi.io.github.classgraph.types.ParseException; import nonapi.io.github.classgraph.types.Parser; import nonapi.io.github.classgraph.types.TypeUtils; @@ -95,6 +97,16 @@ public List getInterfaceBounds() { return interfaceBounds; } + @Override + protected void addTypeAnnotation(final List typePath, final AnnotationInfo annotationInfo) { + if (typePath.isEmpty()) { + addTypeAnnotation(annotationInfo); + } else { + // TODO is this right? + throw new IllegalArgumentException("Type parameter should have empty typePath"); + } + } + // ------------------------------------------------------------------------------------------------------------- /** @@ -218,26 +230,32 @@ public boolean equals(final Object obj) { } else if (!(obj instanceof TypeParameter)) { return false; } - final TypeParameter o = (TypeParameter) obj; - return o.name.equals(this.name) - && ((o.classBound == null && this.classBound == null) - || (o.classBound != null && o.classBound.equals(this.classBound))) - && o.interfaceBounds.equals(this.interfaceBounds); + final TypeParameter other = (TypeParameter) obj; + return other.name.equals(this.name) && Objects.equals(other.typeAnnotationInfo, this.typeAnnotationInfo) + && ((other.classBound == null && this.classBound == null) + || (other.classBound != null && other.classBound.equals(this.classBound))) + && other.interfaceBounds.equals(this.interfaceBounds); } - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ @Override - public String toString() { - final StringBuilder buf = new StringBuilder(); + protected void toStringInternal(final boolean useSimpleNames, final AnnotationInfoList annotationsToExclude, + final StringBuilder buf) { + if (typeAnnotationInfo != null) { + for (final AnnotationInfo annotationInfo : typeAnnotationInfo) { + if (annotationsToExclude == null || !annotationsToExclude.contains(annotationInfo)) { + buf.append(annotationInfo); + buf.append(' '); + } + } + } buf.append(name); String classBoundStr; if (classBound == null) { classBoundStr = null; } else { - classBoundStr = classBound.toString(); - if (classBoundStr.equals("java.lang.Object")) { + classBoundStr = classBound.toString(useSimpleNames); + if (classBoundStr.equals("java.lang.Object") || (classBoundStr.equals("Object") + && ((ClassRefTypeSignature) classBound).className.equals("java.lang.Object"))) { // Don't add "extends java.lang.Object" classBoundStr = null; } @@ -254,8 +272,7 @@ public String toString() { buf.append(" &"); } buf.append(' '); - buf.append(interfaceBounds.get(i).toString()); + buf.append(interfaceBounds.get(i).toString(useSimpleNames)); } - return buf.toString(); } } \ No newline at end of file diff --git a/src/main/java/io/github/classgraph/TypeSignature.java b/src/main/java/io/github/classgraph/TypeSignature.java index 38db3c61a..c95e45617 100644 --- a/src/main/java/io/github/classgraph/TypeSignature.java +++ b/src/main/java/io/github/classgraph/TypeSignature.java @@ -29,9 +29,11 @@ package io.github.classgraph; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; +import io.github.classgraph.Classfile.TypePathNode; import nonapi.io.github.classgraph.types.ParseException; import nonapi.io.github.classgraph.types.Parser; @@ -88,36 +90,6 @@ final protected void findReferencedClassInfo(final Map classN */ public abstract boolean equalsIgnoringTypeParams(final TypeSignature other); - /** - * {@link #toString()} method, possibly returning simple names for classes (i.e. if useSimpleNames is true, the - * package names of classes are stripped). - * - * @param useSimpleNames - * whether or not to use simple names for classes. - * @return the string representation of the type signature, with package names stripped. - */ - protected abstract String toStringInternal(boolean useSimpleNames); - - /** - * {@link #toString()} method, but returning simple names for classes (i.e. the package names of classes are - * stripped). - * - * @return the string representation of the type signature, with package names stripped. - */ - public String toStringWithSimpleNames() { - return toStringInternal(true); - } - - /** - * {@link #toString()} method for type signature. - * - * @return the string representation of the type signature. - */ - @Override - public String toString() { - return toStringInternal(false); - } - /** * Parse a type signature. * @@ -165,4 +137,15 @@ static TypeSignature parse(final String typeDescriptor, final String definingCla } return typeSignature; } + + /** + * Add a type annotation to this type. + * + * @param typePath + * The type path. + * @param annotationInfo + * The annotation to add. + */ + @Override + protected abstract void addTypeAnnotation(List typePath, AnnotationInfo annotationInfo); } \ No newline at end of file diff --git a/src/main/java/io/github/classgraph/TypeVariableSignature.java b/src/main/java/io/github/classgraph/TypeVariableSignature.java index 642f55def..06628d947 100644 --- a/src/main/java/io/github/classgraph/TypeVariableSignature.java +++ b/src/main/java/io/github/classgraph/TypeVariableSignature.java @@ -30,8 +30,10 @@ import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.Set; +import io.github.classgraph.Classfile.TypePathNode; import nonapi.io.github.classgraph.types.ParseException; import nonapi.io.github.classgraph.types.Parser; import nonapi.io.github.classgraph.types.TypeUtils; @@ -114,6 +116,18 @@ public TypeParameter resolve() { // ------------------------------------------------------------------------------------------------------------- + @Override + protected void addTypeAnnotation(final List typePath, final AnnotationInfo annotationInfo) { + if (typePath.isEmpty()) { + addTypeAnnotation(annotationInfo); + } else { + // TODO is this right? + throw new IllegalArgumentException("Type variable should have empty typePath"); + } + } + + // ------------------------------------------------------------------------------------------------------------- + /** * Parse a TypeVariableSignature. * @@ -195,8 +209,8 @@ public boolean equals(final Object obj) { } else if (!(obj instanceof TypeVariableSignature)) { return false; } - final TypeVariableSignature o = (TypeVariableSignature) obj; - return o.name.equals(this.name); + final TypeVariableSignature other = (TypeVariableSignature) obj; + return other.name.equals(this.name) && Objects.equals(other.typeAnnotationInfo, this.typeAnnotationInfo); } /* (non-Javadoc) @@ -282,11 +296,17 @@ public String toStringWithTypeBound() { } } - /* (non-Javadoc) - * @see io.github.classgraph.TypeSignature#toStringInternal(boolean) - */ @Override - protected String toStringInternal(final boolean useSimpleNames) { - return name; + protected void toStringInternal(final boolean useSimpleNames, final AnnotationInfoList annotationsToExclude, + final StringBuilder buf) { + if (typeAnnotationInfo != null) { + for (final AnnotationInfo annotationInfo : typeAnnotationInfo) { + if (annotationsToExclude == null || !annotationsToExclude.contains(annotationInfo)) { + buf.append(annotationInfo); + buf.append(' '); + } + } + } + buf.append(name); } } \ No newline at end of file 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 954fed937..97650ffb4 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ClasspathOrder.java @@ -319,7 +319,7 @@ public boolean addClasspathEntry(final Object pathElement, final ClassLoader cla final String pathElementToStr = pathElement.toString(); try { pathElementURL = new File(pathElementToStr).toURI().toURL(); - } catch (MalformedURLException e) { + } catch (final MalformedURLException e) { // Final fallback -- try prepending "file:" to create a URL pathElementURL = new URL("file:" + pathElementToStr); } 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 34869fde8..6ac79593c 100644 --- a/src/main/java/nonapi/io/github/classgraph/types/TypeUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/types/TypeUtils.java @@ -65,7 +65,8 @@ public static boolean getIdentifierToken(final Parser parser, final char separat parser.appendToToken(separatorReplace); parser.next(); consumedChar = true; - } else if (c != ';' && c != '[' && c != '<' && c != '>' && c != ':' && c != '/' && c != '.') { + } else if (c != ';' && c != '[' && c != '<' && c != '>' && c != ':' && c != '/' && c != '.' + && c != '$') { parser.appendToToken(c); parser.next(); consumedChar = true; diff --git a/src/test/java/io/github/classgraph/issues/GenericInnerClassTypedField.java b/src/test/java/io/github/classgraph/issues/GenericInnerClassTypedField.java index cde86a2a0..dc23ccbc9 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() + "<" + Integer.class.getName() + ", " + String.class.getName() + ">.B"); + assertThat(classRefTypeSignature.toString()).isEqualTo(A.class.getName().replace('$', '.') + "<" + + 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 377b727c1..bc4b6abf0 100644 --- a/src/test/java/io/github/classgraph/issues/issue152/Issue152Test.java +++ b/src/test/java/io/github/classgraph/issues/issue152/Issue152Test.java @@ -100,9 +100,9 @@ public void issue152Test() { .isEqualTo("public java.util.Set testMethod(" + "java.util.List, java.util.Map>, double[][][], int, " - + TestType.class.getName() + "[], java.util.Set, java.util.List, java.util.List, java.util.Map, java.util.Set[])"); assertThat(classInfo // .getFieldInfo("testField").toString()) // 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 b3f3650a8..cfb017f06 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()"); } } @@ -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/Foo.java b/src/test/java/io/github/classgraph/issues/issue402/Foo.java new file mode 100644 index 000000000..7fa409281 --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue402/Foo.java @@ -0,0 +1,6 @@ +package io.github.classgraph.issues.issue402; + +class Foo { + class Bar { + } +} \ No newline at end of file diff --git a/src/test/java/io/github/classgraph/issues/issue402/Outer.java b/src/test/java/io/github/classgraph/issues/issue402/Outer.java new file mode 100644 index 000000000..054d7084f --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue402/Outer.java @@ -0,0 +1,24 @@ +package io.github.classgraph.issues.issue402; + +/** + * Nested types. + */ +class Outer { + class Middle { + class Inner1 { + } + } + + static class MiddleStatic { + class Inner2 { + } + + static class InnerStatic { + } + } + + class MiddleGeneric { + class InnerGeneric { + } + } +} diff --git a/src/test/java/io/github/classgraph/issues/issue402/TypeAnnotationTest.java b/src/test/java/io/github/classgraph/issues/issue402/TypeAnnotationTest.java new file mode 100644 index 000000000..f6d5317c9 --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue402/TypeAnnotationTest.java @@ -0,0 +1,155 @@ +package io.github.classgraph.issues.issue402; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ClassInfo; +import io.github.classgraph.ScanResult; + +/** + * TypeAnnotationTest. + */ +class TypeAnnotationTest { + /** Annotation A. */ + @Retention(RetentionPolicy.RUNTIME) + private static @interface A { + } + + /** Annotation B. */ + @Retention(RetentionPolicy.RUNTIME) + private static @interface B { + } + + /** Annotation C. */ + @Retention(RetentionPolicy.RUNTIME) + private static @interface C { + } + + /** Annotation D. */ + @Retention(RetentionPolicy.RUNTIME) + private static @interface D { + } + + /** Annotation E. */ + @Retention(RetentionPolicy.RUNTIME) + private static @interface E { + } + + /** Annotation F. */ + @Retention(RetentionPolicy.RUNTIME) + private static @interface F { + } + + /** Annotation G. */ + @Retention(RetentionPolicy.RUNTIME) + private static @interface G { + } + + /** Annotation H. */ + @Retention(RetentionPolicy.RUNTIME) + private static @interface H { + } + + /** Annotation I. */ + @Retention(RetentionPolicy.RUNTIME) + private static @interface I { + } + + @A + Map<@B ? extends @C String, @D List<@E Object>> map; + + @I + String @F [] @G [] @H [] arr; + + @A + List<@B Comparable<@F Object @C [] @D [] @E []>> comparable; + + @A + Outer.@B Middle.@C Inner1 inner1; + + Outer.@A MiddleStatic.@B Inner2 inner2; + + Outer.MiddleStatic.@A InnerStatic inner3; + + Outer.MiddleGeneric<@A Foo.@B Bar>.InnerGeneric<@D String @C []> inner4; + + static class X2 { + class Y2 { + class Z2 { + } + } + } + + static class X3 { + interface Y3 { + class Z3 { + } + } + } + + static class X4 { + static class Y4 { + class Z4 { + } + } + } + + List<@A X.@B Y.@C Z> xyz; + + List<@A X2.@B Y2.@C Z2> xyz2; + + List xyz3; + + List xyz4; + + private static String shortNames(final Object type) { + return type.toString().replace(TypeAnnotationTest.class.getName() + ".", "") + .replace(TypeAnnotationTest.class.getName() + "$", "") + .replace(TypeAnnotationTest.class.getPackage().getName() + ".", "").replace("java.lang.", "") + .replace("java.util.", ""); + } + + /** Test type annotations. */ + @Test + void fieldsWithTypeAnnotations() { + try (ScanResult scanResult = new ClassGraph() + .acceptPackages(TypeAnnotationTest.class.getPackage().getName()).enableAllInfo().scan()) { + final ClassInfo classInfo = scanResult.getClassInfo(TypeAnnotationTest.class.getName()); + + assertThat(shortNames(classInfo.getFieldInfo("map"))) + .isEqualTo("@A Map<@B ? extends @C String, @D List<@E Object>> map"); + + assertThat(shortNames(classInfo.getFieldInfo("arr"))).isEqualTo("@I String @F [] @G [] @H [] arr"); + + assertThat(shortNames(classInfo.getFieldInfo("comparable"))) + .isEqualTo("@A List<@B Comparable<@F Object @C [] @D [] @E []>> comparable"); + + assertThat(shortNames(classInfo.getFieldInfo("inner1"))) + .isEqualTo("@A Outer.@B Middle.@C Inner1 inner1"); + + assertThat(shortNames(classInfo.getFieldInfo("inner2"))) + .isEqualTo("Outer.@A MiddleStatic.@B Inner2 inner2"); + + assertThat(shortNames(classInfo.getFieldInfo("inner3"))) + .isEqualTo("Outer.MiddleStatic.@A InnerStatic inner3"); + + assertThat(shortNames(classInfo.getFieldInfo("inner4"))) + .isEqualTo("Outer.MiddleGeneric<@A Foo.@B Bar>.InnerGeneric<@D String @C []> inner4"); + + assertThat(shortNames(classInfo.getFieldInfo("xyz"))).isEqualTo("List<@A X.@B Y.@C Z> xyz"); + + 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("xyz4"))).isEqualTo("List xyz4"); + } + } +} diff --git a/src/test/java/io/github/classgraph/issues/issue402/X.java b/src/test/java/io/github/classgraph/issues/issue402/X.java new file mode 100644 index 000000000..e15126639 --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue402/X.java @@ -0,0 +1,8 @@ +package io.github.classgraph.issues.issue402; + +class X { + class Y { + class Z { + } + } +} \ No newline at end of file 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 0b9ebc690..34cebc169 100644 --- a/src/test/java/io/github/classgraph/test/methodinfo/MethodInfoTest.java +++ b/src/test/java/io/github/classgraph/test/methodinfo/MethodInfoTest.java @@ -129,7 +129,7 @@ public boolean accept(final MethodInfo methodInfo) { "@" + ExternalAnnotation.class.getName() // + " public final int publicMethodWithArgs" + "(java.lang.String, char, long, float[], byte[][], " - + "java.util.List, " + X.class.getName() + + "java.util.List, " + X.class.getName().replace('$', '.') + "[][][], java.lang.String[]...)", "@" + Test.class.getName() + " public void methodInfoNotEnabled()", "@" + Test.class.getName() + " public void testGetMethodInfo()", @@ -170,7 +170,7 @@ public boolean accept(final MethodInfo methodInfo) { "@" + ExternalAnnotation.class.getName() // + " public final int publicMethodWithArgs" + "(java.lang.String, char, long, float[], byte[][], " - + "java.util.List, " + X.class.getName() + + "java.util.List, " + X.class.getName().replace('$', '.') + "[][][], java.lang.String[]...)", "private static java.lang.String[] privateMethod()", "@" + Test.class.getName() + " public void methodInfoNotEnabled()", @@ -204,9 +204,8 @@ public void testMethodInfoLoadMethodForArrayArg() { arrayClassInfoList.add(((ArrayTypeSignature) paramTypeSig).getArrayClassInfo()); } } - assertThat(arrayClassInfoList.toString()).isEqualTo("[class float[], class byte[][], " - + "class io.github.classgraph.test.methodinfo.MethodInfoTest$X[][][], " - + "class java.lang.String[][]]"); + assertThat(arrayClassInfoList.toString()).isEqualTo("[class float[], class byte[][], " + "class " + + X.class.getName().replace('$', '.') + "[][][], " + "class java.lang.String[][]]"); final ArrayClassInfo p1 = arrayClassInfoList.get(1); assertThat(p1.loadElementClass()).isEqualTo(byte.class); assertThat(p1.loadClass()).isEqualTo(byte[][].class); From 9457851ab5577c5c66004a19c566fc633b49a1b2 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 17 Dec 2020 17:31:55 -0700 Subject: [PATCH 0959/1778] Make API consistent (#402) --- src/main/java/io/github/classgraph/ClassRefTypeSignature.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/ClassRefTypeSignature.java b/src/main/java/io/github/classgraph/ClassRefTypeSignature.java index d20a6732e..85c51442e 100644 --- a/src/main/java/io/github/classgraph/ClassRefTypeSignature.java +++ b/src/main/java/io/github/classgraph/ClassRefTypeSignature.java @@ -152,7 +152,7 @@ public List> getSuffixTypeArguments() { * @return The list of lists of type annotations for the suffixes (nested inner classes), one list per suffix, * or null if none. */ - public List getSuffixTypeAnnotations() { + public List getSuffixTypeAnnotationInfo() { return suffixTypeAnnotations; } From 1b336cc0e3387ab189da2f928bf64309bb436c57 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 17 Dec 2020 18:06:58 -0700 Subject: [PATCH 0960/1778] Improve Javadoc --- .../java/io/github/classgraph/ClassGraph.java | 99 +++++++++---------- .../java/io/github/classgraph/ScanResult.java | 3 +- 2 files changed, 51 insertions(+), 51 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index 40b145d0e..48ebe8818 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -666,12 +666,12 @@ public ClassGraph acceptPackages(final String... packageNames) { } /** - * @deprecated Use {@link #acceptPackages(String...)} instead. + * Use {@link #acceptPackages(String...)} instead. * - * @param packageNames - * The fully-qualified names of packages to scan (using '.' as a separator). May include a glob + * @param packageNames The fully-qualified names of packages to scan (using '.' as a separator). May include a glob * wildcard ({@code '*'}). * @return this (for method chaining). + * @deprecated Use {@link #acceptPackages(String...)} instead. */ @Deprecated public ClassGraph whitelistPackages(final String... packageNames) { @@ -711,12 +711,12 @@ public ClassGraph acceptPaths(final String... paths) { } /** - * @deprecated Use {@link #acceptPaths(String...)} instead. + * Use {@link #acceptPaths(String...)} instead. * - * @param paths - * The paths to scan, relative to the package root of the classpath element (with '/' as a + * @param paths The paths to scan, relative to the package root of the classpath element (with '/' as a * separator). May include a glob wildcard ({@code '*'}). * @return this (for method chaining). + * @deprecated Use {@link #acceptPaths(String...)} instead. */ @Deprecated public ClassGraph whitelistPaths(final String... paths) { @@ -759,13 +759,12 @@ public ClassGraph acceptPackagesNonRecursive(final String... packageNames) { } /** - * @deprecated Use {@link #acceptPackagesNonRecursive(String...)} instead. + * Use {@link #acceptPackagesNonRecursive(String...)} instead. * - * @param packageNames - * The fully-qualified names of packages to scan (with '.' as a separator). May not include a glob + * @param packageNames The fully-qualified names of packages to scan (with '.' as a separator). May not include a glob * wildcard ({@code '*'}). - * * @return this (for method chaining). + * @deprecated Use {@link #acceptPackagesNonRecursive(String...)} instead. */ @Deprecated public ClassGraph whitelistPackagesNonRecursive(final String... packageNames) { @@ -802,12 +801,12 @@ public ClassGraph acceptPathsNonRecursive(final String... paths) { } /** - * @deprecated Use {@link #acceptPathsNonRecursive(String...)} instead. + * Use {@link #acceptPathsNonRecursive(String...)} instead. * - * @param paths - * The paths to scan, relative to the package root of the classpath element (with '/' as a + * @param paths The paths to scan, relative to the package root of the classpath element (with '/' as a * separator). May not include a glob wildcard ({@code '*'}). * @return this (for method chaining). + * @deprecated Use {@link #acceptPathsNonRecursive(String...)} instead. */ @Deprecated public ClassGraph whitelistPathsNonRecursive(final String... paths) { @@ -848,12 +847,12 @@ public ClassGraph rejectPackages(final String... packageNames) { } /** - * @deprecated Use {@link #rejectPackages(String...)} instead. + * Use {@link #rejectPackages(String...)} instead. * - * @param packageNames - * The fully-qualified names of packages to reject (with '.' as a separator). May include a glob + * @param packageNames The fully-qualified names of packages to reject (with '.' as a separator). May include a glob * wildcard ({@code '*'}). * @return this (for method chaining). + * @deprecated Use {@link #rejectPackages(String...)} instead. */ @Deprecated public ClassGraph blacklistPackages(final String... packageNames) { @@ -888,11 +887,11 @@ public ClassGraph rejectPaths(final String... paths) { } /** - * @deprecated Use {@link #rejectPaths(String...)} instead. + * Use {@link #rejectPaths(String...)} instead. * - * @param paths - * The paths to reject (with '/' as a separator). May include a glob wildcard ({@code '*'}). + * @param paths The paths to reject (with '/' as a separator). May include a glob wildcard ({@code '*'}). * @return this (for method chaining). + * @deprecated Use {@link #rejectPaths(String...)} instead. */ @Deprecated public ClassGraph blacklistPaths(final String... paths) { @@ -933,12 +932,12 @@ public ClassGraph acceptClasses(final String... classNames) { } /** - * @deprecated Use {@link #acceptClasses(String...)} instead. - * - * @param classNames - * The fully-qualified names of classes to scan (using '.' as a separator). May not include a glob + * 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 '*'}). * @return this (for method chaining). + * @deprecated Use {@link #acceptClasses(String...)} instead. */ @Deprecated public ClassGraph whitelistClasses(final String... classNames) { @@ -972,12 +971,12 @@ public ClassGraph rejectClasses(final String... classNames) { } /** - * @deprecated Use {@link #rejectClasses(String...)} instead. + * Use {@link #rejectClasses(String...)} instead. * - * @param classNames - * The fully-qualified names of classes to reject (using '.' as a separator). May not include a glob + * @param classNames The fully-qualified names of classes to reject (using '.' as a separator). May not include a glob * wildcard ({@code '*'}). * @return this (for method chaining). + * @deprecated Use {@link #rejectClasses(String...)} instead. */ @Deprecated public ClassGraph blacklistClasses(final String... classNames) { @@ -1004,12 +1003,12 @@ public ClassGraph acceptJars(final String... jarLeafNames) { } /** - * @deprecated Use {@link #acceptJars(String...)} instead. + * Use {@link #acceptJars(String...)} instead. * - * @param jarLeafNames - * The leafnames of the jars that should be scanned (e.g. {@code "mylib.jar"}). May contain a + * @param jarLeafNames The leafnames of the jars that should be scanned (e.g. {@code "mylib.jar"}). May contain a * wildcard glob ({@code "mylib-*.jar"}). * @return this (for method chaining). + * @deprecated Use {@link #acceptJars(String...)} instead. */ @Deprecated public ClassGraph whitelistJars(final String... jarLeafNames) { @@ -1036,12 +1035,12 @@ public ClassGraph rejectJars(final String... jarLeafNames) { } /** - * @deprecated Use {@link #rejectJars(String...)} instead. + * Use {@link #rejectJars(String...)} instead. * - * @param jarLeafNames - * The leafnames of the jars that should be scanned (e.g. {@code "badlib.jar"}). May contain a + * @param jarLeafNames The leafnames of the jars that should be scanned (e.g. {@code "badlib.jar"}). May contain a * wildcard glob ({@code "badlib-*.jar"}). * @return this (for method chaining). + * @deprecated Use {@link #rejectJars(String...)} instead. */ @Deprecated public ClassGraph blacklistJars(final String... jarLeafNames) { @@ -1129,13 +1128,13 @@ public ClassGraph acceptLibOrExtJars(final String... jarLeafNames) { } /** - * @deprecated Use {@link #acceptLibOrExtJars(String...)} instead. + * Use {@link #acceptLibOrExtJars(String...)} instead. * - * @param jarLeafNames - * The leafnames of the lib/ext jar(s) that should be scanned (e.g. {@code "mylib.jar"}). May contain + * @param jarLeafNames The leafnames of the lib/ext jar(s) that should be scanned (e.g. {@code "mylib.jar"}). May contain * a wildcard glob ({@code '*'}). Note that if you call this method with no parameters, all JRE/JDK * "lib/" or "ext/" jars will be accepted. * @return this (for method chaining). + * @deprecated Use {@link #acceptLibOrExtJars(String...)} instead. */ @Deprecated public ClassGraph whitelistLibOrExtJars(final String... jarLeafNames) { @@ -1157,13 +1156,13 @@ public ClassGraph rejectLibOrExtJars(final String... jarLeafNames) { } /** - * @deprecated Use {@link #rejectLibOrExtJars(String...)} instead. + * Use {@link #rejectLibOrExtJars(String...)} instead. * - * @param jarLeafNames - * The leafnames of the lib/ext jar(s) that should not be scanned (e.g. + * @param jarLeafNames The leafnames of the lib/ext jar(s) that should not be scanned (e.g. * {@code "jre/lib/badlib.jar"}). May contain a wildcard glob ({@code '*'}). If you call this method * with no parameters, all JRE/JDK {@code "lib/"} or {@code "ext/"} jars will be rejected. * @return this (for method chaining). + * @deprecated Use {@link #rejectLibOrExtJars(String...)} instead. */ @Deprecated public ClassGraph blacklistLibOrExtJars(final String... jarLeafNames) { @@ -1185,11 +1184,11 @@ public ClassGraph acceptModules(final String... moduleNames) { } /** - * @deprecated Use {@link #acceptModules(String...)} instead. + * Use {@link #acceptModules(String...)} instead. * - * @param moduleNames - * The names of the modules that should be scanned. May contain a wildcard glob ({@code '*'}). + * @param moduleNames The names of the modules that should be scanned. May contain a wildcard glob ({@code '*'}). * @return this (for method chaining). + * @deprecated Use {@link #acceptModules(String...)} instead. */ @Deprecated public ClassGraph whitelistModules(final String... moduleNames) { @@ -1211,11 +1210,11 @@ public ClassGraph rejectModules(final String... moduleNames) { } /** - * @deprecated Use {@link #rejectModules(String...)} instead. + * Use {@link #rejectModules(String...)} instead. * - * @param moduleNames - * The names of the modules that should not be scanned. May contain a wildcard glob ({@code '*'}). + * @param moduleNames The names of the modules that should not be scanned. May contain a wildcard glob ({@code '*'}). * @return this (for method chaining). + * @deprecated Use {@link #rejectModules(String...)} instead. */ @Deprecated public ClassGraph blacklistModules(final String... moduleNames) { @@ -1240,12 +1239,12 @@ public ClassGraph acceptClasspathElementsContainingResourcePath(final String... } /** - * @deprecated Use {@link #acceptClasspathElementsContainingResourcePath(String...)} instead. + * Use {@link #acceptClasspathElementsContainingResourcePath(String...)} instead. * - * @param resourcePaths - * The resource paths, any of which must be present in a classpath element for the classpath element + * @param resourcePaths The resource paths, any of which must be present in a classpath element for the classpath element * to be scanned. May contain a wildcard glob ({@code '*'}). * @return this (for method chaining). + * @deprecated Use {@link #acceptClasspathElementsContainingResourcePath(String...)} instead. */ @Deprecated public ClassGraph whitelistClasspathElementsContainingResourcePath(final String... resourcePaths) { @@ -1270,12 +1269,12 @@ public ClassGraph rejectClasspathElementsContainingResourcePath(final String... } /** - * @deprecated Use {@link #rejectClasspathElementsContainingResourcePath(String...)} instead. + * Use {@link #rejectClasspathElementsContainingResourcePath(String...)} instead. * - * @param resourcePaths - * The resource paths which cause a classpath not to be scanned if any are present in a classpath + * @param resourcePaths The resource paths which cause a classpath not to be scanned if any are present in a classpath * element for the classpath element. May contain a wildcard glob ({@code '*'}). * @return this (for method chaining). + * @deprecated Use {@link #rejectClasspathElementsContainingResourcePath(String...)} instead. */ @Deprecated public ClassGraph blacklistClasspathElementsContainingResourcePath(final String... resourcePaths) { diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index e4bd9abdc..ac8a6435a 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -554,13 +554,14 @@ public ResourceList getResourcesWithPathIgnoringAccept(final String resourcePath } /** - * @deprecated Use {@link #getResourcesWithPathIgnoringAccept(String)} instead. + * 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. + * @deprecated Use {@link #getResourcesWithPathIgnoringAccept(String)} instead. */ @Deprecated public ResourceList getResourcesWithPathIgnoringWhitelist(final String resourcePath) { From 0f61d2217db56b9d7f394b604fc532db089bc9b7 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 17 Dec 2020 18:10:07 -0700 Subject: [PATCH 0961/1778] Add missing Javadoc with JAutoDoc --- .../io/github/classgraph/AnnotationInfo.java | 4 +- .../java/io/github/classgraph/ClassGraph.java | 48 ++++++++++++------- .../java/io/github/classgraph/MethodInfo.java | 4 +- .../classgraph/classpath/ModuleFinder.java | 6 ++- .../fastzipfilereader/FastZipEntry.java | 2 +- .../fileslice/reader/ClassfileReader.java | 12 ++++- .../issues/issue255/Issue255Test.java | 4 ++ .../classgraph/issues/issue350/Issue350.java | 28 ++++++++--- .../classgraph/issues/issue352/Issue352.java | 8 +++- .../classgraph/issues/issue355/Issue355.java | 35 +++++++++++--- .../issues/issue368/Issue368Test.java | 3 +- .../issue370/annotations/ApiOperation.java | 2 + .../issues/issue431/Issue431Test.java | 4 +- .../issues/issue468/Issue468Test.java | 14 +++++- .../test/methodinfo/MethodInfoTest.java | 2 + .../classgraph/json/JSONParserTest.java | 15 ++++-- 16 files changed, 146 insertions(+), 45 deletions(-) diff --git a/src/main/java/io/github/classgraph/AnnotationInfo.java b/src/main/java/io/github/classgraph/AnnotationInfo.java index 01bcb1bda..5be610b89 100644 --- a/src/main/java/io/github/classgraph/AnnotationInfo.java +++ b/src/main/java/io/github/classgraph/AnnotationInfo.java @@ -477,8 +477,8 @@ public int compareTo(final AnnotationInfo o) { } else if (o.annotationParamValues == null) { return 1; } else { - for (int i = 0, max = Math.max(annotationParamValues.size(), - o.annotationParamValues.size()); i < max; i++) { + for (int i = 0, + max = Math.max(annotationParamValues.size(), o.annotationParamValues.size()); i < max; i++) { if (i >= annotationParamValues.size()) { return -1; } else if (i >= o.annotationParamValues.size()) { diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index 48ebe8818..ac5f296db 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -668,7 +668,8 @@ public ClassGraph acceptPackages(final String... packageNames) { /** * Use {@link #acceptPackages(String...)} instead. * - * @param packageNames The fully-qualified names of packages to scan (using '.' as a separator). May include a glob + * @param packageNames + * The fully-qualified names of packages to scan (using '.' as a separator). May include a glob * wildcard ({@code '*'}). * @return this (for method chaining). * @deprecated Use {@link #acceptPackages(String...)} instead. @@ -713,7 +714,8 @@ public ClassGraph acceptPaths(final String... paths) { /** * Use {@link #acceptPaths(String...)} instead. * - * @param paths The paths to scan, relative to the package root of the classpath element (with '/' as a + * @param paths + * The paths to scan, relative to the package root of the classpath element (with '/' as a * separator). May include a glob wildcard ({@code '*'}). * @return this (for method chaining). * @deprecated Use {@link #acceptPaths(String...)} instead. @@ -761,7 +763,8 @@ public ClassGraph acceptPackagesNonRecursive(final String... packageNames) { /** * Use {@link #acceptPackagesNonRecursive(String...)} instead. * - * @param packageNames The fully-qualified names of packages to scan (with '.' as a separator). May not include a glob + * @param packageNames + * The fully-qualified names of packages to scan (with '.' as a separator). May not include a glob * wildcard ({@code '*'}). * @return this (for method chaining). * @deprecated Use {@link #acceptPackagesNonRecursive(String...)} instead. @@ -803,7 +806,8 @@ public ClassGraph acceptPathsNonRecursive(final String... paths) { /** * Use {@link #acceptPathsNonRecursive(String...)} instead. * - * @param paths The paths to scan, relative to the package root of the classpath element (with '/' as a + * @param paths + * The paths to scan, relative to the package root of the classpath element (with '/' as a * separator). May not include a glob wildcard ({@code '*'}). * @return this (for method chaining). * @deprecated Use {@link #acceptPathsNonRecursive(String...)} instead. @@ -849,7 +853,8 @@ public ClassGraph rejectPackages(final String... packageNames) { /** * Use {@link #rejectPackages(String...)} instead. * - * @param packageNames The fully-qualified names of packages to reject (with '.' as a separator). May include a glob + * @param packageNames + * The fully-qualified names of packages to reject (with '.' as a separator). May include a glob * wildcard ({@code '*'}). * @return this (for method chaining). * @deprecated Use {@link #rejectPackages(String...)} instead. @@ -889,7 +894,8 @@ public ClassGraph rejectPaths(final String... paths) { /** * Use {@link #rejectPaths(String...)} instead. * - * @param paths The paths to reject (with '/' as a separator). May include a glob wildcard ({@code '*'}). + * @param paths + * The paths to reject (with '/' as a separator). May include a glob wildcard ({@code '*'}). * @return this (for method chaining). * @deprecated Use {@link #rejectPaths(String...)} instead. */ @@ -934,7 +940,8 @@ 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 + * @param classNames + * The fully-qualified names of classes to scan (using '.' as a separator). May not include a glob * wildcard ({@code '*'}). * @return this (for method chaining). * @deprecated Use {@link #acceptClasses(String...)} instead. @@ -973,7 +980,8 @@ 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 + * @param classNames + * The fully-qualified names of classes to reject (using '.' as a separator). May not include a glob * wildcard ({@code '*'}). * @return this (for method chaining). * @deprecated Use {@link #rejectClasses(String...)} instead. @@ -1005,7 +1013,8 @@ public ClassGraph acceptJars(final String... jarLeafNames) { /** * Use {@link #acceptJars(String...)} instead. * - * @param jarLeafNames The leafnames of the jars that should be scanned (e.g. {@code "mylib.jar"}). May contain a + * @param jarLeafNames + * The leafnames of the jars that should be scanned (e.g. {@code "mylib.jar"}). May contain a * wildcard glob ({@code "mylib-*.jar"}). * @return this (for method chaining). * @deprecated Use {@link #acceptJars(String...)} instead. @@ -1037,7 +1046,8 @@ public ClassGraph rejectJars(final String... jarLeafNames) { /** * Use {@link #rejectJars(String...)} instead. * - * @param jarLeafNames The leafnames of the jars that should be scanned (e.g. {@code "badlib.jar"}). May contain a + * @param jarLeafNames + * The leafnames of the jars that should be scanned (e.g. {@code "badlib.jar"}). May contain a * wildcard glob ({@code "badlib-*.jar"}). * @return this (for method chaining). * @deprecated Use {@link #rejectJars(String...)} instead. @@ -1130,7 +1140,8 @@ public ClassGraph acceptLibOrExtJars(final String... jarLeafNames) { /** * Use {@link #acceptLibOrExtJars(String...)} instead. * - * @param jarLeafNames The leafnames of the lib/ext jar(s) that should be scanned (e.g. {@code "mylib.jar"}). May contain + * @param jarLeafNames + * The leafnames of the lib/ext jar(s) that should be scanned (e.g. {@code "mylib.jar"}). May contain * a wildcard glob ({@code '*'}). Note that if you call this method with no parameters, all JRE/JDK * "lib/" or "ext/" jars will be accepted. * @return this (for method chaining). @@ -1158,7 +1169,8 @@ public ClassGraph rejectLibOrExtJars(final String... jarLeafNames) { /** * Use {@link #rejectLibOrExtJars(String...)} instead. * - * @param jarLeafNames The leafnames of the lib/ext jar(s) that should not be scanned (e.g. + * @param jarLeafNames + * The leafnames of the lib/ext jar(s) that should not be scanned (e.g. * {@code "jre/lib/badlib.jar"}). May contain a wildcard glob ({@code '*'}). If you call this method * with no parameters, all JRE/JDK {@code "lib/"} or {@code "ext/"} jars will be rejected. * @return this (for method chaining). @@ -1186,7 +1198,8 @@ public ClassGraph acceptModules(final String... moduleNames) { /** * Use {@link #acceptModules(String...)} instead. * - * @param moduleNames The names of the modules that should be scanned. May contain a wildcard glob ({@code '*'}). + * @param moduleNames + * The names of the modules that should be scanned. May contain a wildcard glob ({@code '*'}). * @return this (for method chaining). * @deprecated Use {@link #acceptModules(String...)} instead. */ @@ -1212,7 +1225,8 @@ public ClassGraph rejectModules(final String... moduleNames) { /** * Use {@link #rejectModules(String...)} instead. * - * @param moduleNames The names of the modules that should not be scanned. May contain a wildcard glob ({@code '*'}). + * @param moduleNames + * The names of the modules that should not be scanned. May contain a wildcard glob ({@code '*'}). * @return this (for method chaining). * @deprecated Use {@link #rejectModules(String...)} instead. */ @@ -1241,7 +1255,8 @@ public ClassGraph acceptClasspathElementsContainingResourcePath(final String... /** * Use {@link #acceptClasspathElementsContainingResourcePath(String...)} instead. * - * @param resourcePaths The resource paths, any of which must be present in a classpath element for the classpath element + * @param resourcePaths + * The resource paths, any of which must be present in a classpath element for the classpath element * to be scanned. May contain a wildcard glob ({@code '*'}). * @return this (for method chaining). * @deprecated Use {@link #acceptClasspathElementsContainingResourcePath(String...)} instead. @@ -1271,7 +1286,8 @@ public ClassGraph rejectClasspathElementsContainingResourcePath(final String... /** * Use {@link #rejectClasspathElementsContainingResourcePath(String...)} instead. * - * @param resourcePaths The resource paths which cause a classpath not to be scanned if any are present in a classpath + * @param resourcePaths + * The resource paths which cause a classpath not to be scanned if any are present in a classpath * element for the classpath element. May contain a wildcard glob ({@code '*'}). * @return this (for method chaining). * @deprecated Use {@link #rejectClasspathElementsContainingResourcePath(String...)} instead. diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index e02fb5a0d..7169f8d35 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -468,8 +468,8 @@ public MethodParameterInfo[] getParameterInfo() { } else { // Right-align when not the right length paramModifiersAligned = new int[numParams]; - for (int i = 0, lenDiff = numParams - - parameterModifiers.length; i < parameterModifiers.length; i++) { + for (int i = 0, + lenDiff = numParams - parameterModifiers.length; i < parameterModifiers.length; i++) { paramModifiersAligned[lenDiff + i] = parameterModifiers[i]; } } 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 97a8923db..6013e8b53 100644 --- a/src/main/java/nonapi/io/github/classgraph/classpath/ModuleFinder.java +++ b/src/main/java/nonapi/io/github/classgraph/classpath/ModuleFinder.java @@ -76,7 +76,11 @@ public List getNonSystemModuleRefs() { return nonSystemModuleRefs; } - /** @return If true, must forcibly scan {@code java.class.path}, since there was an anonymous module layer. */ + /** + * Force scan java class path. + * + * @return If true, must forcibly scan {@code java.class.path}, since there was an anonymous module layer. + */ public boolean forceScanJavaClassPath() { return forceScanJavaClassPath; } 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 05946aa59..08936bed6 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/FastZipEntry.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/FastZipEntry.java @@ -65,7 +65,7 @@ public class FastZipEntry implements Comparable { /** The last modified date in MSDOS format, if {@link FastZipEntry#lastModifiedTimeMillis} is 0L. */ private final int lastModifiedDateMSDOS; - /** The file attributes for this resource, or 0 if unknown */ + /** The file attributes for this resource, or 0 if unknown. */ public final int fileAttributes; /** The {@link Slice} for the zip entry's raw data (which can be either stored or deflated). */ 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 7c9492853..8326940ec 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 @@ -141,12 +141,20 @@ public ClassfileReader(final InputStream inputStream) throws IOException { arr = new byte[INITIAL_BUF_SIZE]; } - /** @return the current read position. */ + /** + * Curr pos. + * + * @return the current read position. + */ public int currPos() { return currIdx; } - /** @return the buffer. */ + /** + * Buf. + * + * @return the buffer. + */ public byte[] buf() { return arr; } diff --git a/src/test/java/io/github/classgraph/issues/issue255/Issue255Test.java b/src/test/java/io/github/classgraph/issues/issue255/Issue255Test.java index eab0b948f..894970894 100644 --- a/src/test/java/io/github/classgraph/issues/issue255/Issue255Test.java +++ b/src/test/java/io/github/classgraph/issues/issue255/Issue255Test.java @@ -42,8 +42,12 @@ * Issue255Test. */ public class Issue255Test { + /** * Issue 255 test. + * + * @throws IOException + * If an I/O exception occurs. */ @Test public void issue255Test() throws IOException { 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 db21f3aa7..5cbb2729e 100644 --- a/src/test/java/io/github/classgraph/issues/issue350/Issue350.java +++ b/src/test/java/io/github/classgraph/issues/issue350/Issue350.java @@ -14,24 +14,34 @@ * Unit test. */ public class Issue350 { - /** */ + + /** + * The Interface SuperclassAnnotation. + */ @Retention(RetentionPolicy.RUNTIME) public static @interface SuperclassAnnotation { } - /** */ + /** + * The Class Pub. + */ public static class Pub { - /** */ + + /** The annotated public field. */ @SuperclassAnnotation public int annotatedPublicField; - /** */ + /** + * Annotated public method. + */ @SuperclassAnnotation public void annotatedPublicMethod() { } } - /** */ + /** + * The Class Priv. + */ public static class Priv { /** */ @SuperclassAnnotation @@ -43,11 +53,15 @@ private void annotatedPrivateMethod() { } } - /** */ + /** + * The Class PubSub. + */ public static class PubSub extends Pub { } - /** */ + /** + * The Class PrivSub. + */ public static class PrivSub extends Priv { } diff --git a/src/test/java/io/github/classgraph/issues/issue352/Issue352.java b/src/test/java/io/github/classgraph/issues/issue352/Issue352.java index 87dc963ff..0d2a10ce3 100644 --- a/src/test/java/io/github/classgraph/issues/issue352/Issue352.java +++ b/src/test/java/io/github/classgraph/issues/issue352/Issue352.java @@ -16,7 +16,13 @@ * Unit test. */ public class Issue352 { - /** Test **/ + + /** + * Test *. + * + * @throws IOException + * Signals that an I/O exception has occurred. + */ @Test public void test() throws IOException { final File resolvedFile = MavenResolvers.createMavenResolver(null, null).resolve("com.sun.istack", diff --git a/src/test/java/io/github/classgraph/issues/issue355/Issue355.java b/src/test/java/io/github/classgraph/issues/issue355/Issue355.java index 1e6ca012d..1f8042da9 100644 --- a/src/test/java/io/github/classgraph/issues/issue355/Issue355.java +++ b/src/test/java/io/github/classgraph/issues/issue355/Issue355.java @@ -21,26 +21,49 @@ * Unit test. */ public class Issue355 { - /** Annotation parameter class */ + + /** + * Annotation parameter class. + */ public class X { } - /** Annotation with class reference array typed param */ + /** + * Annotation with class reference array typed param. + */ @Retention(RetentionPolicy.RUNTIME) public @interface Ann { - /** Annotation parameter */ + + /** + * Annotation parameter. + * + * @return the class[] + */ public Class[] value(); } - /** Annotated with class reference array */ + /** + * Annotated with class reference array. + */ @Ann({ X.class }) public class Y { - /** method with array-typed param */ + + /** + * method with array-typed param. + * + * @param x + * the x + */ public void y(final X[] x) { } } - /** Test **/ + /** + * Test. + * + * @throws IOException + * Signals that an I/O exception has occurred. + */ @Test public void test() throws IOException { try (ScanResult scanResult = new ClassGraph() diff --git a/src/test/java/io/github/classgraph/issues/issue368/Issue368Test.java b/src/test/java/io/github/classgraph/issues/issue368/Issue368Test.java index 9359ab131..cbe03f932 100644 --- a/src/test/java/io/github/classgraph/issues/issue368/Issue368Test.java +++ b/src/test/java/io/github/classgraph/issues/issue368/Issue368Test.java @@ -40,13 +40,14 @@ /** * Issue368Test. */ +@SuppressWarnings("null") public class Issue368Test { /** * InnerClass. */ public static class InnerClass { - @SuppressWarnings("javadoc") + /** The inner class field. */ public Class innerClassField = Issue368Test.class; } diff --git a/src/test/java/io/github/classgraph/issues/issue370/annotations/ApiOperation.java b/src/test/java/io/github/classgraph/issues/issue370/annotations/ApiOperation.java index 7c51004b4..f57f9f236 100644 --- a/src/test/java/io/github/classgraph/issues/issue370/annotations/ApiOperation.java +++ b/src/test/java/io/github/classgraph/issues/issue370/annotations/ApiOperation.java @@ -19,6 +19,8 @@ String notes(); /** + * Extensions. + * * @return an optional array of extensions */ Extension[] extensions() default @Extension(properties = @ExtensionProperty(name = "", value = "")); diff --git a/src/test/java/io/github/classgraph/issues/issue431/Issue431Test.java b/src/test/java/io/github/classgraph/issues/issue431/Issue431Test.java index 49b405052..07f33c51e 100644 --- a/src/test/java/io/github/classgraph/issues/issue431/Issue431Test.java +++ b/src/test/java/io/github/classgraph/issues/issue431/Issue431Test.java @@ -42,7 +42,9 @@ * Issue431Test. */ public class Issue431Test { - /** Class X */ + /** + * Class X. + */ public static class X { /** a */ static final int a = Integer.MAX_VALUE; diff --git a/src/test/java/io/github/classgraph/issues/issue468/Issue468Test.java b/src/test/java/io/github/classgraph/issues/issue468/Issue468Test.java index 73ae09b47..cfa422a70 100644 --- a/src/test/java/io/github/classgraph/issues/issue468/Issue468Test.java +++ b/src/test/java/io/github/classgraph/issues/issue468/Issue468Test.java @@ -52,7 +52,12 @@ private static void scan(final ClassGraph classGraph) { } } - /** Test '+' signs in URLs. */ + /** + * Test '+' signs in URLs. + * + * @throws Exception + * the exception + */ @Test public void testPlusSigns() throws Exception { final URL url = Issue468Test.class.getClassLoader().getResource("issue468/x+y/z+w.jar"); @@ -62,7 +67,12 @@ public void testPlusSigns() throws Exception { scan(new ClassGraph().acceptPackagesNonRecursive("").overrideClasspath(url)); } - /** Test "file:" URIs as strings, with and without the scheme. */ + /** + * Test "file:" URIs as strings, with and without the scheme. + * + * @throws Exception + * the exception + */ @Test public void testFileURIs() throws Exception { final URL url = Issue468Test.class.getClassLoader().getResource("issue468/x+y/z+w.jar"); 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 34cebc169..2a722c2f3 100644 --- a/src/test/java/io/github/classgraph/test/methodinfo/MethodInfoTest.java +++ b/src/test/java/io/github/classgraph/test/methodinfo/MethodInfoTest.java @@ -77,6 +77,8 @@ public void xMethod() { * the b * @param l * the l + * @param xArray + * the x array * @param varargs * the varargs * @return the int diff --git a/src/test/java/nonapi/io/github/classgraph/json/JSONParserTest.java b/src/test/java/nonapi/io/github/classgraph/json/JSONParserTest.java index bc1f95570..78d7d75d1 100644 --- a/src/test/java/nonapi/io/github/classgraph/json/JSONParserTest.java +++ b/src/test/java/nonapi/io/github/classgraph/json/JSONParserTest.java @@ -8,16 +8,25 @@ * Unit test. */ public class JSONParserTest { - /** Test double value. */ + /** + * Test double value. + * + * @throws ParseException + * the parse exception + */ @Test public void test1() throws ParseException { JSONParser.parseJSON("{\"doubleValue\":-2.147483648}"); } - /** Test double value with exponent. */ + /** + * Test double value with exponent. + * + * @throws ParseException + * the parse exception + */ @Test public void test2() throws ParseException { JSONParser.parseJSON("{\"doubleValue\":-2.147483648E9}"); } - } \ No newline at end of file From 774b1697d67e67f6d5046ed2e16d863fd8b04607 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 17 Dec 2020 19:15:58 -0700 Subject: [PATCH 0962/1778] Reorder methods --- .../github/classgraph/ClassTypeSignature.java | 84 ++++----- .../classgraph/MethodTypeSignature.java | 160 +++++++++--------- 2 files changed, 122 insertions(+), 122 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassTypeSignature.java b/src/main/java/io/github/classgraph/ClassTypeSignature.java index 0190660f7..87585485e 100644 --- a/src/main/java/io/github/classgraph/ClassTypeSignature.java +++ b/src/main/java/io/github/classgraph/ClassTypeSignature.java @@ -119,48 +119,6 @@ protected void addTypeAnnotation(final List typePath, final Annota // ------------------------------------------------------------------------------------------------------------- - /** - * Parse a class type signature or class type descriptor. - * - * @param typeDescriptor - * The class type signature or class type descriptor to parse. - * @param classInfo - * the class info - * @return The parsed class type signature or class type descriptor. - * @throws ParseException - * If the class type signature could not be parsed. - */ - static ClassTypeSignature parse(final String typeDescriptor, final ClassInfo classInfo) throws ParseException { - final Parser parser = new Parser(typeDescriptor); - // The defining class name is used to resolve type variables using the defining class' type descriptor. - // But here we are parsing the defining class' type descriptor, so it can't contain variables that - // point to itself => just use null as the defining class name. - final String definingClassNameNull = null; - final List typeParameters = TypeParameter.parseList(parser, definingClassNameNull); - final ClassRefTypeSignature superclassSignature = ClassRefTypeSignature.parse(parser, - definingClassNameNull); - List superinterfaceSignatures; - if (parser.hasMore()) { - superinterfaceSignatures = new ArrayList<>(); - while (parser.hasMore()) { - final ClassRefTypeSignature superinterfaceSignature = ClassRefTypeSignature.parse(parser, - definingClassNameNull); - if (superinterfaceSignature == null) { - throw new ParseException(parser, "Could not parse superinterface signature"); - } - superinterfaceSignatures.add(superinterfaceSignature); - } - } else { - superinterfaceSignatures = Collections.emptyList(); - } - if (parser.hasMore()) { - throw new ParseException(parser, "Extra characters at end of type descriptor"); - } - return new ClassTypeSignature(classInfo, typeParameters, superclassSignature, superinterfaceSignatures); - } - - // ------------------------------------------------------------------------------------------------------------- - /* (non-Javadoc) * @see io.github.classgraph.ScanResultObject#getClassName() */ @@ -338,4 +296,46 @@ protected void toStringInternal(final boolean useSimpleNames, final AnnotationIn buf.append(toString(classInfo.getName(), useSimpleNames, /* typeNameOnly = */ false, classInfo.getModifiers(), classInfo.isAnnotation(), classInfo.isInterface(), annotationsToExclude)); } + + // ------------------------------------------------------------------------------------------------------------- + + /** + * Parse a class type signature or class type descriptor. + * + * @param typeDescriptor + * The class type signature or class type descriptor to parse. + * @param classInfo + * the class info + * @return The parsed class type signature or class type descriptor. + * @throws ParseException + * If the class type signature could not be parsed. + */ + static ClassTypeSignature parse(final String typeDescriptor, final ClassInfo classInfo) throws ParseException { + final Parser parser = new Parser(typeDescriptor); + // The defining class name is used to resolve type variables using the defining class' type descriptor. + // But here we are parsing the defining class' type descriptor, so it can't contain variables that + // point to itself => just use null as the defining class name. + final String definingClassNameNull = null; + final List typeParameters = TypeParameter.parseList(parser, definingClassNameNull); + final ClassRefTypeSignature superclassSignature = ClassRefTypeSignature.parse(parser, + definingClassNameNull); + List superinterfaceSignatures; + if (parser.hasMore()) { + superinterfaceSignatures = new ArrayList<>(); + while (parser.hasMore()) { + final ClassRefTypeSignature superinterfaceSignature = ClassRefTypeSignature.parse(parser, + definingClassNameNull); + if (superinterfaceSignature == null) { + throw new ParseException(parser, "Could not parse superinterface signature"); + } + superinterfaceSignatures.add(superinterfaceSignature); + } + } else { + superinterfaceSignatures = Collections.emptyList(); + } + if (parser.hasMore()) { + throw new ParseException(parser, "Extra characters at end of type descriptor"); + } + return new ClassTypeSignature(classInfo, typeParameters, superclassSignature, superinterfaceSignatures); + } } \ No newline at end of file diff --git a/src/main/java/io/github/classgraph/MethodTypeSignature.java b/src/main/java/io/github/classgraph/MethodTypeSignature.java index 7b2904d7b..8ff14fa06 100644 --- a/src/main/java/io/github/classgraph/MethodTypeSignature.java +++ b/src/main/java/io/github/classgraph/MethodTypeSignature.java @@ -126,86 +126,6 @@ protected void addTypeAnnotation(final List typePath, final Annota // ------------------------------------------------------------------------------------------------------------- - /** - * Parse a method signature. - * - * @param typeDescriptor - * The type descriptor of the method. - * @param definingClassName - * The name of the defining class (for resolving type variables). - * @return The parsed method type signature. - * @throws ParseException - * If method type signature could not be parsed. - */ - static MethodTypeSignature parse(final String typeDescriptor, final String definingClassName) - throws ParseException { - if (typeDescriptor.equals("")) { - // Special case for instance initialization method signatures in a CONSTANT_NameAndType_info structure: - // https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-4.html#jvms-4.4.2 - return new MethodTypeSignature(Collections. emptyList(), - Collections. emptyList(), /* void */ new BaseTypeSignature('V'), - Collections. emptyList()); - } - final Parser parser = new Parser(typeDescriptor); - final List typeParameters = TypeParameter.parseList(parser, definingClassName); - parser.expect('('); - final List paramTypes = new ArrayList<>(); - while (parser.peek() != ')') { - if (!parser.hasMore()) { - throw new ParseException(parser, "Ran out of input while parsing method signature"); - } - final TypeSignature paramType = TypeSignature.parse(parser, definingClassName); - if (paramType == null) { - throw new ParseException(parser, "Missing method parameter type signature"); - } - paramTypes.add(paramType); - } - parser.expect(')'); - final TypeSignature resultType = TypeSignature.parse(parser, definingClassName); - if (resultType == null) { - throw new ParseException(parser, "Missing method result type signature"); - } - List throwsSignatures; - if (parser.peek() == '^') { - throwsSignatures = new ArrayList<>(); - while (parser.peek() == '^') { - parser.expect('^'); - final ClassRefTypeSignature classTypeSignature = ClassRefTypeSignature.parse(parser, - definingClassName); - if (classTypeSignature != null) { - throwsSignatures.add(classTypeSignature); - } else { - final TypeVariableSignature typeVariableSignature = TypeVariableSignature.parse(parser, - definingClassName); - if (typeVariableSignature != null) { - throwsSignatures.add(typeVariableSignature); - } else { - throw new ParseException(parser, "Missing type variable signature"); - } - } - } - } else { - throwsSignatures = Collections.emptyList(); - } - if (parser.hasMore()) { - throw new ParseException(parser, "Extra characters at end of type descriptor"); - } - final MethodTypeSignature methodSignature = new MethodTypeSignature(typeParameters, paramTypes, resultType, - throwsSignatures); - // Add back-links from type variable signature to the method signature it is part of, - // and to the enclosing class' type signature - @SuppressWarnings("unchecked") - final List typeVariableSignatures = (List) parser.getState(); - if (typeVariableSignatures != null) { - for (final TypeVariableSignature typeVariableSignature : typeVariableSignatures) { - typeVariableSignature.containingMethodSignature = methodSignature; - } - } - return methodSignature; - } - - // ------------------------------------------------------------------------------------------------------------- - /* (non-Javadoc) * @see io.github.classgraph.ScanResultObject#getClassName() */ @@ -360,4 +280,84 @@ protected void toStringInternal(final boolean useSimpleNames, final AnnotationIn } } } + + // ------------------------------------------------------------------------------------------------------------- + + /** + * Parse a method signature. + * + * @param typeDescriptor + * The type descriptor of the method. + * @param definingClassName + * The name of the defining class (for resolving type variables). + * @return The parsed method type signature. + * @throws ParseException + * If method type signature could not be parsed. + */ + static MethodTypeSignature parse(final String typeDescriptor, final String definingClassName) + throws ParseException { + if (typeDescriptor.equals("")) { + // Special case for instance initialization method signatures in a CONSTANT_NameAndType_info structure: + // https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-4.html#jvms-4.4.2 + return new MethodTypeSignature(Collections. emptyList(), + Collections. emptyList(), /* void */ new BaseTypeSignature('V'), + Collections. emptyList()); + } + final Parser parser = new Parser(typeDescriptor); + final List typeParameters = TypeParameter.parseList(parser, definingClassName); + parser.expect('('); + final List paramTypes = new ArrayList<>(); + while (parser.peek() != ')') { + if (!parser.hasMore()) { + throw new ParseException(parser, "Ran out of input while parsing method signature"); + } + final TypeSignature paramType = TypeSignature.parse(parser, definingClassName); + if (paramType == null) { + throw new ParseException(parser, "Missing method parameter type signature"); + } + paramTypes.add(paramType); + } + parser.expect(')'); + final TypeSignature resultType = TypeSignature.parse(parser, definingClassName); + if (resultType == null) { + throw new ParseException(parser, "Missing method result type signature"); + } + List throwsSignatures; + if (parser.peek() == '^') { + throwsSignatures = new ArrayList<>(); + while (parser.peek() == '^') { + parser.expect('^'); + final ClassRefTypeSignature classTypeSignature = ClassRefTypeSignature.parse(parser, + definingClassName); + if (classTypeSignature != null) { + throwsSignatures.add(classTypeSignature); + } else { + final TypeVariableSignature typeVariableSignature = TypeVariableSignature.parse(parser, + definingClassName); + if (typeVariableSignature != null) { + throwsSignatures.add(typeVariableSignature); + } else { + throw new ParseException(parser, "Missing type variable signature"); + } + } + } + } else { + throwsSignatures = Collections.emptyList(); + } + if (parser.hasMore()) { + throw new ParseException(parser, "Extra characters at end of type descriptor"); + } + final MethodTypeSignature methodSignature = new MethodTypeSignature(typeParameters, paramTypes, resultType, + throwsSignatures); + // Add back-links from type variable signature to the method signature it is part of, + // and to the enclosing class' type signature + @SuppressWarnings("unchecked") + final List typeVariableSignatures = (List) parser.getState(); + if (typeVariableSignatures != null) { + for (final TypeVariableSignature typeVariableSignature : typeVariableSignatures) { + typeVariableSignature.containingMethodSignature = methodSignature; + } + } + return methodSignature; + } } \ No newline at end of file From d4b5e62856557af161e7f4ddc94106eeb088c18c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 17 Dec 2020 20:27:49 -0700 Subject: [PATCH 0963/1778] Type annotation bugfixes (#402) --- .../java/io/github/classgraph/ClassInfo.java | 3 +- .../java/io/github/classgraph/Classfile.java | 89 ++++++++++++------- .../java/io/github/classgraph/MethodInfo.java | 19 +++- .../issues/issue402/TypeAnnotationTest.java | 30 +++++++ 4 files changed, 104 insertions(+), 37 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index d36dae002..f50845623 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -2036,8 +2036,7 @@ private MethodInfoList getMethodInfo(final String methodName, final boolean getN 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 - if (nameAndTypeDescriptorSet - .add(new SimpleEntry<>(mi.getName(), mi.getTypeDescriptor().toString()))) { + if (nameAndTypeDescriptorSet.add(new SimpleEntry<>(mi.getName(), mi.getTypeDescriptorStr()))) { // Add method/constructor to output order methodInfoList.add(mi); } diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index dbf548f57..3016532e4 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -1581,20 +1581,35 @@ private void readMethods() throws IOException, ClassfileFormatException { public void decorate(final MethodTypeSignature methodTypeSignature) { if (targetType == 0x01) { // Type parameter declaration of generic method or constructor - methodTypeSignature.getTypeParameters().get(typeParameterIndex) - .addTypeAnnotation(typePath, annotationInfo); - } else if (targetType == 0x12) { - // Type in bound of type parameter declaration of generic method or constructor - final TypeParameter typeParameter = methodTypeSignature - .getTypeParameters().get(typeParameterIndex); - // TODO: documentation does not specify if bound index should be treated this way ******************* - if (boundIndex == 0) { - typeParameter.getClassBound().addTypeAnnotation(typePath, + final List typeParameters = methodTypeSignature + .getTypeParameters(); + if (typeParameters != null + && typeParameterIndex < typeParameters.size()) { + typeParameters.get(typeParameterIndex).addTypeAnnotation(typePath, annotationInfo); - } else { - typeParameter.getInterfaceBounds().get(boundIndex - 1) - .addTypeAnnotation(typePath, annotationInfo); } + // else this is a method type descriptor, not a method type signature, + // so there are no type parameters + } else if (targetType == 0x12) { + // Type in bound of type parameter declaration of generic method or + // constructor + final List typeParameters = methodTypeSignature + .getTypeParameters(); + if (typeParameters != null + && typeParameterIndex < typeParameters.size()) { + final TypeParameter typeParameter = typeParameters + .get(typeParameterIndex); + // boundIndex == 0 => class bound; boundIndex > 0 => interface bound + if (boundIndex == 0) { + typeParameter.getClassBound().addTypeAnnotation(typePath, + annotationInfo); + } else { + typeParameter.getInterfaceBounds().get(boundIndex - 1) + .addTypeAnnotation(typePath, annotationInfo); + } + } + // else this is a method type descriptor, not a method type signature, + // so there are no type parameters } else if (targetType == 0x14) { // Return type of method, or type of newly constructed object methodTypeSignature.getResultType().addTypeAnnotation(typePath, @@ -1603,14 +1618,13 @@ public void decorate(final MethodTypeSignature methodTypeSignature) { // Receiver type of method or constructor (explicit receiver parameter) final List paramTypeSignatures = methodTypeSignature .getParameterTypeSignatures(); - if (paramTypeSignatures.size() == 0) { - throw new IllegalArgumentException( - "No explicit receiver parameter"); + if (paramTypeSignatures != null && paramTypeSignatures.size() > 0) { + // TODO: is this the right way to apply a receiver type annotation? ************************** + final TypeSignature receiverParamTypeSignature = paramTypeSignatures + .get(0); + receiverParamTypeSignature.addTypeAnnotation(typePath, + annotationInfo); } - // TODO: is this the right way to apply a receiver type annotation? - final TypeSignature receiverParamTypeSignature = paramTypeSignatures - .get(0); - receiverParamTypeSignature.addTypeAnnotation(typePath, annotationInfo); } else if (targetType == 0x16) { // Type in formal parameter declaration of method, constructor, // or lambda expression @@ -1624,8 +1638,13 @@ public void decorate(final MethodTypeSignature methodTypeSignature) { } } else if (targetType == 0x17) { // Type in throws clause of method or constructor - methodTypeSignature.getThrowsSignatures().get(throwsTypeIndex) - .addTypeAnnotation(typePath, annotationInfo); + final List throwsSignatures = // + methodTypeSignature.getThrowsSignatures(); + if (throwsSignatures != null + && throwsTypeIndex < throwsSignatures.size()) { + throwsSignatures.get(throwsTypeIndex).addTypeAnnotation(typePath, + annotationInfo); + } } } }); @@ -1741,8 +1760,12 @@ private void readClassAttributes() throws IOException, ClassfileFormatException public void decorate(final ClassTypeSignature classTypeSignature) { if (targetType == 0x00) { // Type parameter declaration of generic class or interface - classTypeSignature.getTypeParameters().get(typeParameterIndex) - .addTypeAnnotation(typePath, annotationInfo); + final List typeParameters = classTypeSignature + .getTypeParameters(); + if (typeParameters != null && typeParameterIndex < typeParameters.size()) { + typeParameters.get(typeParameterIndex).addTypeAnnotation(typePath, + annotationInfo); + } } else if (targetType == 0x10) { if (supertypeIndex == 65535) { // Type in extends clause of class declaration @@ -1755,14 +1778,18 @@ public void decorate(final ClassTypeSignature classTypeSignature) { } } else if (targetType == 0x11) { // Type in bound of type parameter declaration of generic class or interface - final TypeParameter typeParameter = classTypeSignature.getTypeParameters() - .get(typeParameterIndex); - // TODO: documentation does not specify if bound index should be treated this way ******************* - if (boundIndex == 0) { - typeParameter.getClassBound().addTypeAnnotation(typePath, annotationInfo); - } else { - typeParameter.getInterfaceBounds().get(boundIndex - 1) - .addTypeAnnotation(typePath, annotationInfo); + final List typeParameters = classTypeSignature + .getTypeParameters(); + if (typeParameters != null && typeParameterIndex < typeParameters.size()) { + final TypeParameter typeParameter = typeParameters.get(typeParameterIndex); + // boundIndex == 0 => class bound; boundIndex > 0 => interface bound + if (boundIndex == 0) { + typeParameter.getClassBound().addTypeAnnotation(typePath, + annotationInfo); + } else { + typeParameter.getInterfaceBounds().get(boundIndex - 1) + .addTypeAnnotation(typePath, annotationInfo); + } } } } diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index 7169f8d35..20fecb174 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -933,14 +933,14 @@ public String toString() { MethodParameterInfo.modifiersToString(paramInfo.getModifiers(), buf); - final TypeSignature paramType = paramInfo.getTypeSignatureOrTypeDescriptor(); + final TypeSignature paramTypeSignature = paramInfo.getTypeSignatureOrTypeDescriptor(); if (i == varArgsParamIndex) { // Show varargs params correctly -- replace last "[]" with "..." - if (!(paramType instanceof ArrayTypeSignature)) { + if (!(paramTypeSignature instanceof ArrayTypeSignature)) { throw new IllegalArgumentException( "Got non-array type for last parameter of varargs method " + name); } - final ArrayTypeSignature arrayType = (ArrayTypeSignature) paramType; + 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); @@ -951,7 +951,18 @@ public String toString() { } buf.append("..."); } else { - buf.append(paramType.toString()); + // 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); + for (int j = 0; j < paramInfo.annotationInfo.length; j++) { + annotationsToExclude.add(paramInfo.annotationInfo[j]); + } + } + paramTypeSignature.toStringInternal(/* useSimpleNames = */ false, annotationsToExclude, buf); } if (hasParamNames) { 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 f6d5317c9..558747dbb 100644 --- a/src/test/java/io/github/classgraph/issues/issue402/TypeAnnotationTest.java +++ b/src/test/java/io/github/classgraph/issues/issue402/TypeAnnotationTest.java @@ -11,6 +11,7 @@ import io.github.classgraph.ClassGraph; import io.github.classgraph.ClassInfo; +import io.github.classgraph.MethodParameterInfo; import io.github.classgraph.ScanResult; /** @@ -109,6 +110,28 @@ class Z4 { List xyz4; + static class U { + } + + interface V { + } + + <@A T extends @B U> @D U t(@E T t) { + return null; + } + + static class P<@A T extends @B U & @C V> { + public void explicitReceiver(@F P this) { + } + } + + /** + * Convert class names to short names. + * + * @param type + * the type + * @return the class names as short names + */ private static String shortNames(final Object type) { return type.toString().replace(TypeAnnotationTest.class.getName() + ".", "") .replace(TypeAnnotationTest.class.getName() + "$", "") @@ -150,6 +173,13 @@ void fieldsWithTypeAnnotations() { assertThat(shortNames(classInfo.getFieldInfo("xyz3"))).isEqualTo("List xyz3"); assertThat(shortNames(classInfo.getFieldInfo("xyz4"))).isEqualTo("List xyz4"); + + assertThat(shortNames(classInfo.getMethodInfo("t").get(0))) + .isEqualTo("<@A T extends @B U> @D U t(@E T)"); + + final ClassInfo pClassInfo = scanResult.getClassInfo(P.class.getName()); + + assertThat(shortNames(pClassInfo)).isEqualTo("static class P<@A T extends @B U & @C V>"); } } } From 34ceb396f8b9bd0b8dad941e1f1c284beb415b5f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 17 Dec 2020 20:53:50 -0700 Subject: [PATCH 0964/1778] Bugfixes for type annotations (#402) --- .../java/io/github/classgraph/Classfile.java | 10 +------ .../classgraph/MethodTypeSignature.java | 25 ++++++++++++++++ .../issues/issue402/TypeAnnotationTest.java | 30 ++++++++++++++++--- 3 files changed, 52 insertions(+), 13 deletions(-) diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index 3016532e4..f20545a5a 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -1616,15 +1616,7 @@ public void decorate(final MethodTypeSignature methodTypeSignature) { annotationInfo); } else if (targetType == 0x15) { // Receiver type of method or constructor (explicit receiver parameter) - final List paramTypeSignatures = methodTypeSignature - .getParameterTypeSignatures(); - if (paramTypeSignatures != null && paramTypeSignatures.size() > 0) { - // TODO: is this the right way to apply a receiver type annotation? ************************** - final TypeSignature receiverParamTypeSignature = paramTypeSignatures - .get(0); - receiverParamTypeSignature.addTypeAnnotation(typePath, - annotationInfo); - } + methodTypeSignature.addRecieverTypeAnnotation(annotationInfo); } else if (targetType == 0x16) { // Type in formal parameter declaration of method, constructor, // or lambda expression diff --git a/src/main/java/io/github/classgraph/MethodTypeSignature.java b/src/main/java/io/github/classgraph/MethodTypeSignature.java index 8ff14fa06..022e98614 100644 --- a/src/main/java/io/github/classgraph/MethodTypeSignature.java +++ b/src/main/java/io/github/classgraph/MethodTypeSignature.java @@ -53,6 +53,9 @@ public final class MethodTypeSignature extends HierarchicalTypeSignature { /** The throws type signatures. */ private final List throwsSignatures; + /** Any type annotation(s) on an explicit receiver parameter. */ + private AnnotationInfoList receiverTypeAnnotationInfo; + // ------------------------------------------------------------------------------------------------------------- /** @@ -124,6 +127,28 @@ protected void addTypeAnnotation(final List typePath, final Annota "Cannot call this method on " + MethodTypeSignature.class.getSimpleName()); } + /** + * Add a type annotation for an explicit receiver parameter. + * + * @param annotationInfo + * the receiver type annotation + */ + void addRecieverTypeAnnotation(final AnnotationInfo annotationInfo) { + if (receiverTypeAnnotationInfo == null) { + receiverTypeAnnotationInfo = new AnnotationInfoList(1); + } + receiverTypeAnnotationInfo.add(annotationInfo); + } + + /** + * Get type annotations on the explicit receiver parameter, or null if none. + * + * @return type annotations on the explicit receiver parameter, or null if none. + */ + public AnnotationInfoList getReceiverTypeAnnotationInfo() { + return receiverTypeAnnotationInfo; + } + // ------------------------------------------------------------------------------------------------------------- /* (non-Javadoc) 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 558747dbb..35686e05d 100644 --- a/src/test/java/io/github/classgraph/issues/issue402/TypeAnnotationTest.java +++ b/src/test/java/io/github/classgraph/issues/issue402/TypeAnnotationTest.java @@ -9,9 +9,10 @@ import org.junit.jupiter.api.Test; +import io.github.classgraph.AnnotationInfo; import io.github.classgraph.ClassGraph; import io.github.classgraph.ClassInfo; -import io.github.classgraph.MethodParameterInfo; +import io.github.classgraph.MethodInfo; import io.github.classgraph.ScanResult; /** @@ -116,7 +117,7 @@ static class U { interface V { } - <@A T extends @B U> @D U t(@E T t) { + <@A T extends @B U> @D U t(@E final T t) { return null; } @@ -139,9 +140,9 @@ private static String shortNames(final Object type) { .replace("java.util.", ""); } - /** Test type annotations. */ + /** Test field type annotations. */ @Test - void fieldsWithTypeAnnotations() { + void fieldTypeAnnotations() { try (ScanResult scanResult = new ClassGraph() .acceptPackages(TypeAnnotationTest.class.getPackage().getName()).enableAllInfo().scan()) { final ClassInfo classInfo = scanResult.getClassInfo(TypeAnnotationTest.class.getName()); @@ -180,6 +181,27 @@ void fieldsWithTypeAnnotations() { final ClassInfo pClassInfo = scanResult.getClassInfo(P.class.getName()); assertThat(shortNames(pClassInfo)).isEqualTo("static class P<@A T extends @B U & @C V>"); + + final MethodInfo methodInfo = pClassInfo.getMethodInfo("explicitReceiver").get(0); + final AnnotationInfo receiverTypeAnnotationInfo = methodInfo.getTypeSignatureOrTypeDescriptor() + .getReceiverTypeAnnotationInfo().get(0); + assertThat(shortNames(receiverTypeAnnotationInfo)).isEqualTo("@F"); + } + } + + /** Test class and method type annotations. */ + @Test + void classAndMethodTypeAnnotations() { + try (ScanResult scanResult = new ClassGraph() + .acceptPackages(TypeAnnotationTest.class.getPackage().getName()).enableAllInfo().scan()) { + final ClassInfo classInfo = scanResult.getClassInfo(P.class.getName()); + + assertThat(shortNames(classInfo)).isEqualTo("static class P<@A T extends @B U & @C V>"); + + final MethodInfo methodInfo = classInfo.getMethodInfo("explicitReceiver").get(0); + final AnnotationInfo receiverTypeAnnotationInfo = methodInfo.getTypeSignatureOrTypeDescriptor() + .getReceiverTypeAnnotationInfo().get(0); + assertThat(shortNames(receiverTypeAnnotationInfo)).isEqualTo("@F"); } } } From ce37d96d3a626132fb39f3d8543bb2fc0863a1fe Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 17 Dec 2020 20:56:38 -0700 Subject: [PATCH 0965/1778] Remove old comments --- src/main/java/io/github/classgraph/ClassTypeSignature.java | 1 - src/main/java/io/github/classgraph/TypeParameter.java | 1 - src/main/java/io/github/classgraph/TypeVariableSignature.java | 1 - 3 files changed, 3 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassTypeSignature.java b/src/main/java/io/github/classgraph/ClassTypeSignature.java index 87585485e..b7623c4bb 100644 --- a/src/main/java/io/github/classgraph/ClassTypeSignature.java +++ b/src/main/java/io/github/classgraph/ClassTypeSignature.java @@ -289,7 +289,6 @@ String toString(final String className, final boolean useSimpleNames, final bool return buf.toString(); } - // TODO: useSimpleNames is being ignored @Override protected void toStringInternal(final boolean useSimpleNames, final AnnotationInfoList annotationsToExclude, final StringBuilder buf) { diff --git a/src/main/java/io/github/classgraph/TypeParameter.java b/src/main/java/io/github/classgraph/TypeParameter.java index 1b693bbd4..63a4486fc 100644 --- a/src/main/java/io/github/classgraph/TypeParameter.java +++ b/src/main/java/io/github/classgraph/TypeParameter.java @@ -102,7 +102,6 @@ protected void addTypeAnnotation(final List typePath, final Annota if (typePath.isEmpty()) { addTypeAnnotation(annotationInfo); } else { - // TODO is this right? throw new IllegalArgumentException("Type parameter should have empty typePath"); } } diff --git a/src/main/java/io/github/classgraph/TypeVariableSignature.java b/src/main/java/io/github/classgraph/TypeVariableSignature.java index 06628d947..95f57f4e2 100644 --- a/src/main/java/io/github/classgraph/TypeVariableSignature.java +++ b/src/main/java/io/github/classgraph/TypeVariableSignature.java @@ -121,7 +121,6 @@ protected void addTypeAnnotation(final List typePath, final Annota if (typePath.isEmpty()) { addTypeAnnotation(annotationInfo); } else { - // TODO is this right? throw new IllegalArgumentException("Type variable should have empty typePath"); } } From a83d36446d0908044bc239346a26771f8d448678 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 17 Dec 2020 21:11:56 -0700 Subject: [PATCH 0966/1778] Robustness fixes --- .../java/io/github/classgraph/Classfile.java | 31 ++++++++++++++----- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index f20545a5a..73e1770c8 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -1601,11 +1601,19 @@ public void decorate(final MethodTypeSignature methodTypeSignature) { .get(typeParameterIndex); // boundIndex == 0 => class bound; boundIndex > 0 => interface bound if (boundIndex == 0) { - typeParameter.getClassBound().addTypeAnnotation(typePath, - annotationInfo); + final ReferenceTypeSignature classBound = typeParameter + .getClassBound(); + if (classBound != null) { + classBound.addTypeAnnotation(typePath, annotationInfo); + } } else { - typeParameter.getInterfaceBounds().get(boundIndex - 1) - .addTypeAnnotation(typePath, annotationInfo); + final List interfaceBounds = // + typeParameter.getInterfaceBounds(); + if (interfaceBounds != null + && boundIndex - 1 < interfaceBounds.size()) { + interfaceBounds.get(boundIndex - 1) + .addTypeAnnotation(typePath, annotationInfo); + } } } // else this is a method type descriptor, not a method type signature, @@ -1776,11 +1784,18 @@ public void decorate(final ClassTypeSignature classTypeSignature) { final TypeParameter typeParameter = typeParameters.get(typeParameterIndex); // boundIndex == 0 => class bound; boundIndex > 0 => interface bound if (boundIndex == 0) { - typeParameter.getClassBound().addTypeAnnotation(typePath, - annotationInfo); + final ReferenceTypeSignature classBound = typeParameter.getClassBound(); + if (classBound != null) { + classBound.addTypeAnnotation(typePath, annotationInfo); + } } else { - typeParameter.getInterfaceBounds().get(boundIndex - 1) - .addTypeAnnotation(typePath, annotationInfo); + final List interfaceBounds = typeParameter + .getInterfaceBounds(); + if (interfaceBounds != null + && boundIndex - 1 < interfaceBounds.size()) { + typeParameter.getInterfaceBounds().get(boundIndex - 1) + .addTypeAnnotation(typePath, annotationInfo); + } } } } From 3bd30636f31b294240c21dbf0bca4ea9b6721c2d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 17 Dec 2020 22:54:23 -0700 Subject: [PATCH 0967/1778] Fix static analysis warning --- src/main/java/io/github/classgraph/ClassRefTypeSignature.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassRefTypeSignature.java b/src/main/java/io/github/classgraph/ClassRefTypeSignature.java index 85c51442e..6b83624e3 100644 --- a/src/main/java/io/github/classgraph/ClassRefTypeSignature.java +++ b/src/main/java/io/github/classgraph/ClassRefTypeSignature.java @@ -197,9 +197,9 @@ protected void addTypeAnnotation(final List typePath, final Annota skipSuffix = false; } else { // For suffix path X.Y, classes are not nested if Y is static - final ClassInfo outerClassInfo = scanResult.getClassInfo(typePrefix.toString()); + final ClassInfo outerClassInfo = scanResult.getClassInfo(typePrefix); typePrefix = typePrefix + '$' + suffixes.get(suffixIdx + 1); - final ClassInfo innerClassInfo = scanResult.getClassInfo(typePrefix.toString()); + final ClassInfo innerClassInfo = scanResult.getClassInfo(typePrefix); skipSuffix = outerClassInfo == null || innerClassInfo == null || outerClassInfo.isInterfaceOrAnnotation() // || innerClassInfo.isInterfaceOrAnnotation() // From 26520b3106b2839fe6420b2daa6ce5398daf8cd3 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 18 Dec 2020 01:40:43 -0700 Subject: [PATCH 0968/1778] Add unit test for example in #402 --- .../issues/issue402/TypeAnnotationTest.java | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) 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 35686e05d..8094cbdc5 100644 --- a/src/test/java/io/github/classgraph/issues/issue402/TypeAnnotationTest.java +++ b/src/test/java/io/github/classgraph/issues/issue402/TypeAnnotationTest.java @@ -12,6 +12,8 @@ import io.github.classgraph.AnnotationInfo; import io.github.classgraph.ClassGraph; import io.github.classgraph.ClassInfo; +import io.github.classgraph.ClassRefTypeSignature; +import io.github.classgraph.FieldInfo; import io.github.classgraph.MethodInfo; import io.github.classgraph.ScanResult; @@ -19,47 +21,38 @@ * TypeAnnotationTest. */ class TypeAnnotationTest { - /** Annotation A. */ @Retention(RetentionPolicy.RUNTIME) private static @interface A { } - /** Annotation B. */ @Retention(RetentionPolicy.RUNTIME) private static @interface B { } - /** Annotation C. */ @Retention(RetentionPolicy.RUNTIME) private static @interface C { } - /** Annotation D. */ @Retention(RetentionPolicy.RUNTIME) private static @interface D { } - /** Annotation E. */ @Retention(RetentionPolicy.RUNTIME) private static @interface E { } - /** Annotation F. */ @Retention(RetentionPolicy.RUNTIME) private static @interface F { } - /** Annotation G. */ @Retention(RetentionPolicy.RUNTIME) private static @interface G { } - /** Annotation H. */ @Retention(RetentionPolicy.RUNTIME) private static @interface H { } - /** Annotation I. */ @Retention(RetentionPolicy.RUNTIME) private static @interface I { } @@ -126,6 +119,15 @@ public void explicitReceiver(@F P this) { } } + @Retention(RetentionPolicy.RUNTIME) + private static @interface Size { + int max(); + } + + class Person { + List<@Size(max = 50) String> emails; + } + /** * Convert class names to short names. * @@ -178,14 +180,14 @@ void fieldTypeAnnotations() { assertThat(shortNames(classInfo.getMethodInfo("t").get(0))) .isEqualTo("<@A T extends @B U> @D U t(@E T)"); - final ClassInfo pClassInfo = scanResult.getClassInfo(P.class.getName()); + final FieldInfo emailsFieldInfo = scanResult.getClassInfo(Person.class.getName()) + .getFieldInfo("emails"); - assertThat(shortNames(pClassInfo)).isEqualTo("static class P<@A T extends @B U & @C V>"); + assertThat(shortNames(emailsFieldInfo)).isEqualTo("List<@Size(max=50) String> emails"); - final MethodInfo methodInfo = pClassInfo.getMethodInfo("explicitReceiver").get(0); - final AnnotationInfo receiverTypeAnnotationInfo = methodInfo.getTypeSignatureOrTypeDescriptor() - .getReceiverTypeAnnotationInfo().get(0); - assertThat(shortNames(receiverTypeAnnotationInfo)).isEqualTo("@F"); + assertThat(shortNames(((ClassRefTypeSignature) emailsFieldInfo.getTypeSignatureOrTypeDescriptor()) + .getTypeArguments().get(0).getTypeSignature().getTypeAnnotationInfo().get(0))) + .isEqualTo("@Size(max=50)"); } } From 005b2d2666ec335fb60737ebd49816aa1cab0d2a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 18 Dec 2020 03:19:58 -0700 Subject: [PATCH 0969/1778] Added toStringWithShortNames() method to all ScanResultObject subclasses --- .../github/classgraph/AnnotationClassRef.java | 25 ++-- .../classgraph/AnnotationEnumValue.java | 9 +- .../io/github/classgraph/AnnotationInfo.java | 31 ++--- .../classgraph/AnnotationParameterValue.java | 129 +++++++++--------- .../github/classgraph/ArrayTypeSignature.java | 6 +- .../github/classgraph/BaseTypeSignature.java | 4 +- .../java/io/github/classgraph/ClassInfo.java | 90 ++++++------ .../classgraph/ClassRefTypeSignature.java | 12 +- .../github/classgraph/ClassTypeSignature.java | 81 ++++++----- .../java/io/github/classgraph/FieldInfo.java | 17 +-- .../classgraph/HierarchicalTypeSignature.java | 37 ++--- .../java/io/github/classgraph/MethodInfo.java | 29 ++-- .../classgraph/MethodParameterInfo.java | 41 ++++-- .../classgraph/MethodTypeSignature.java | 9 +- .../classgraph/ObjectTypedValueWrapper.java | 52 +++++++ .../github/classgraph/ScanResultObject.java | 55 +++++++- .../io/github/classgraph/TypeArgument.java | 9 +- .../io/github/classgraph/TypeParameter.java | 8 +- .../classgraph/TypeVariableSignature.java | 2 +- .../issues/issue402/TypeAnnotationTest.java | 60 ++++---- 20 files changed, 416 insertions(+), 290 deletions(-) diff --git a/src/main/java/io/github/classgraph/AnnotationClassRef.java b/src/main/java/io/github/classgraph/AnnotationClassRef.java index 676c0df18..d3b3392e4 100644 --- a/src/main/java/io/github/classgraph/AnnotationClassRef.java +++ b/src/main/java/io/github/classgraph/AnnotationClassRef.java @@ -199,22 +199,21 @@ public boolean equals(final Object obj) { return getTypeSignature().equals(((AnnotationClassRef) obj).getTypeSignature()); } - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ @Override - public String toString() { - // String prefix = "class "; - if (scanResult != null) { - final ClassInfo ci = getClassInfo(); - // The JDK uses "interface" for both interfaces and annotations in Annotation::toString - if (ci != null && ci.isInterfaceOrAnnotation()) { - // prefix = "interface "; - } - } + protected void toString(final boolean useSimpleNames, final StringBuilder buf) { // More recent versions of Annotation::toString() have dropped the "class"/"interface" prefix, // and added ".class" to the end of the class reference (which does not actually match the // annotation source syntax...) - return /* prefix + */ getTypeSignature().toString() + ".class"; + + // String prefix = "class "; + // if (scanResult != null) { + // final ClassInfo ci = getClassInfo(); + // // The JDK uses "interface" for both interfaces and annotations in Annotation::toString + // if (ci != null && ci.isInterfaceOrAnnotation()) { + // prefix = "interface "; + // } + // } + + buf.append(/* prefix + */ getTypeSignature().toString(useSimpleNames) + ".class"); } } \ No newline at end of file diff --git a/src/main/java/io/github/classgraph/AnnotationEnumValue.java b/src/main/java/io/github/classgraph/AnnotationEnumValue.java index b68f23792..104526630 100644 --- a/src/main/java/io/github/classgraph/AnnotationEnumValue.java +++ b/src/main/java/io/github/classgraph/AnnotationEnumValue.java @@ -174,11 +174,10 @@ public int hashCode() { return className.hashCode() * 11 + valueName.hashCode(); } - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ @Override - public String toString() { - return className + "." + valueName; + protected void toString(final boolean useSimpleNames, final StringBuilder buf) { + buf.append(useSimpleNames ? ClassInfo.getSimpleName(className) : className); + buf.append('.'); + buf.append(valueName); } } \ No newline at end of file diff --git a/src/main/java/io/github/classgraph/AnnotationInfo.java b/src/main/java/io/github/classgraph/AnnotationInfo.java index 5be610b89..e40627c89 100644 --- a/src/main/java/io/github/classgraph/AnnotationInfo.java +++ b/src/main/java/io/github/classgraph/AnnotationInfo.java @@ -308,7 +308,7 @@ private static class AnnotationInvocationHandler implements InvocationHandler { if (instantiatedValue == null) { // Annotations cannot contain null values throw new IllegalArgumentException("Got null value for annotation parameter " + apv.getName() - + " of annotation " + annotationInfo.getName()); + + " of annotation " + annotationInfo.name); } this.annotationParameterValuesInstantiated.put(apv.getName(), instantiatedValue); } @@ -466,7 +466,7 @@ void convertWrapperArraysToPrimitiveArrays() { */ @Override public int compareTo(final AnnotationInfo o) { - final int diff = getName().compareTo(o.getName()); + final int diff = this.name.compareTo(o.name); if (diff != 0) { return diff; } @@ -513,7 +513,7 @@ public boolean equals(final Object obj) { */ @Override public int hashCode() { - int h = getName().hashCode(); + int h = name.hashCode(); if (annotationParamValues != null) { for (final AnnotationParameterValue e : annotationParamValues) { h = h * 7 + e.getName().hashCode() * 3 + e.getValue().hashCode(); @@ -522,14 +522,9 @@ public int hashCode() { return h; } - /** - * Render as a string, into a StringBuilder buffer. - * - * @param buf - * The buffer. - */ - void toString(final StringBuilder buf) { - buf.append('@').append(getName()); + @Override + protected void toString(final boolean useSimpleNames, final StringBuilder buf) { + buf.append('@').append(useSimpleNames ? ClassInfo.getSimpleName(name) : name); final AnnotationParameterValueList paramVals = getParameterValues(); if (!paramVals.isEmpty()) { buf.append('('); @@ -539,22 +534,12 @@ void toString(final StringBuilder buf) { } final AnnotationParameterValue paramVal = paramVals.get(i); if (paramVals.size() > 1 || !"value".equals(paramVal.getName())) { - paramVal.toString(buf); + paramVal.toString(useSimpleNames, buf); } else { - paramVal.toStringParamValueOnly(buf); + paramVal.toStringParamValueOnly(useSimpleNames, buf); } } buf.append(')'); } } - - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - final StringBuilder buf = new StringBuilder(); - toString(buf); - return buf.toString(); - } } diff --git a/src/main/java/io/github/classgraph/AnnotationParameterValue.java b/src/main/java/io/github/classgraph/AnnotationParameterValue.java index 66e3dff95..930a4d30d 100644 --- a/src/main/java/io/github/classgraph/AnnotationParameterValue.java +++ b/src/main/java/io/github/classgraph/AnnotationParameterValue.java @@ -180,25 +180,78 @@ Object instantiate(final ClassInfo annotationClassInfo) { // ------------------------------------------------------------------------------------------------------------- /* (non-Javadoc) - * @see java.lang.Object#toString() + * @see java.lang.Comparable#compareTo(java.lang.Object) */ @Override - public String toString() { - final StringBuilder buf = new StringBuilder(); - toString(buf); - return buf.toString(); + public int compareTo(final AnnotationParameterValue other) { + if (other == this) { + return 0; + } + final int diff = name.compareTo(other.getName()); + if (diff != 0) { + return diff; + } + if (value.equals(other.value)) { + return 0; + } + // Use toString() order (which can be slow) as a last-ditch effort -- only happens + // if the annotation has multiple parameters of the same name but different value. + final Object p0 = getValue(); + final Object p1 = other.getValue(); + return p0 == null || p1 == null ? (p0 == null ? 0 : 1) - (p1 == null ? 0 : 1) + : toStringParamValueOnly().compareTo(other.toStringParamValueOnly()); + } + + /* (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 AnnotationParameterValue)) { + return false; + } + final AnnotationParameterValue other = (AnnotationParameterValue) obj; + return this.name.equals(other.name) && (value == null) == (other.value == null) + && (value == null || value.equals(other.value)); + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return Objects.hash(name, value); + } + + // ------------------------------------------------------------------------------------------------------------- + + @Override + protected void toString(final boolean useSimpleNames, final StringBuilder buf) { + buf.append(name); + buf.append("="); + toStringParamValueOnly(useSimpleNames, buf); } /** - * To string. + * Write an annotation parameter value's string representation to the buffer. * + * @param val + * the value + * @param useSimpleNames + * the use simple names * @param buf - * the buf + * the buffer */ - void toString(final StringBuilder buf) { - buf.append(name); - buf.append("="); - toStringParamValueOnly(buf); + private static void toString(final Object val, final boolean useSimpleNames, final StringBuilder buf) { + if (val == null) { + buf.append("null"); + } else if (val instanceof ScanResultObject) { + ((ScanResultObject) val).toString(useSimpleNames, buf); + } else { + buf.append(val.toString()); + } } /** @@ -207,7 +260,7 @@ void toString(final StringBuilder buf) { * @param buf * the buf */ - void toStringParamValueOnly(final StringBuilder buf) { + void toStringParamValueOnly(final boolean useSimpleNames, final StringBuilder buf) { if (value == null) { buf.append("null"); } else { @@ -220,7 +273,7 @@ void toStringParamValueOnly(final StringBuilder buf) { buf.append(", "); } final Object elt = Array.get(paramVal, j); - buf.append(elt == null ? "null" : elt.toString()); + toString(elt, useSimpleNames, buf); } buf.append('}'); } else if (paramVal instanceof String) { @@ -232,7 +285,7 @@ void toStringParamValueOnly(final StringBuilder buf) { buf.append(paramVal.toString().replace("'", "\\'").replace("\n", "\\n").replace("\r", "\\r")); buf.append('\''); } else { - buf.append(paramVal.toString()); + toString(paramVal, useSimpleNames, buf); } } } @@ -244,53 +297,7 @@ void toStringParamValueOnly(final StringBuilder buf) { */ private String toStringParamValueOnly() { final StringBuilder buf = new StringBuilder(); - toStringParamValueOnly(buf); + toStringParamValueOnly(false, buf); return buf.toString(); } - - /* (non-Javadoc) - * @see java.lang.Comparable#compareTo(java.lang.Object) - */ - @Override - public int compareTo(final AnnotationParameterValue other) { - if (other == this) { - return 0; - } - final int diff = name.compareTo(other.getName()); - if (diff != 0) { - return diff; - } - if (value.equals(other.value)) { - return 0; - } - // Use toString() order (which can be slow) as a last-ditch effort -- only happens - // if the annotation has multiple parameters of the same name but different value. - final Object p0 = getValue(); - final Object p1 = other.getValue(); - return p0 == null || p1 == null ? (p0 == null ? 0 : 1) - (p1 == null ? 0 : 1) - : toStringParamValueOnly().compareTo(other.toStringParamValueOnly()); - } - - /* (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 AnnotationParameterValue)) { - return false; - } - final AnnotationParameterValue other = (AnnotationParameterValue) obj; - return this.name.equals(other.name) && (value == null) == (other.value == null) - && (value == null || value.equals(other.value)); - } - - /* (non-Javadoc) - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() { - return Objects.hash(name, value); - } } diff --git a/src/main/java/io/github/classgraph/ArrayTypeSignature.java b/src/main/java/io/github/classgraph/ArrayTypeSignature.java index 8cc82da4e..6bb9df570 100644 --- a/src/main/java/io/github/classgraph/ArrayTypeSignature.java +++ b/src/main/java/io/github/classgraph/ArrayTypeSignature.java @@ -360,6 +360,8 @@ public boolean equalsIgnoringTypeParams(final TypeSignature other) { return this.nestedType.equalsIgnoringTypeParams(o.nestedType); } + // ------------------------------------------------------------------------------------------------------------- + @Override protected void toStringInternal(final boolean useSimpleNames, final AnnotationInfoList annotationsToExclude, final StringBuilder buf) { @@ -373,7 +375,7 @@ protected void toStringInternal(final boolean useSimpleNames, final AnnotationIn if (buf.length() == 0 || buf.charAt(buf.length() - 1) != ' ') { buf.append(' '); } - buf.append(annotationInfo); + annotationInfo.toString(useSimpleNames, buf); } buf.append(' '); } @@ -388,6 +390,8 @@ protected void toStringInternal(final boolean useSimpleNames, final AnnotationIn } } + // ------------------------------------------------------------------------------------------------------------- + /** * Parses the array type signature. * diff --git a/src/main/java/io/github/classgraph/BaseTypeSignature.java b/src/main/java/io/github/classgraph/BaseTypeSignature.java index 6c1eb5488..db42bbb88 100644 --- a/src/main/java/io/github/classgraph/BaseTypeSignature.java +++ b/src/main/java/io/github/classgraph/BaseTypeSignature.java @@ -346,13 +346,15 @@ public boolean equalsIgnoringTypeParams(final TypeSignature other) { return typeSignatureChar == ((BaseTypeSignature) other).typeSignatureChar; } + // ------------------------------------------------------------------------------------------------------------- + @Override protected void toStringInternal(final boolean useSimpleNames, final AnnotationInfoList annotationsToExclude, final StringBuilder buf) { if (typeAnnotationInfo != null) { for (final AnnotationInfo annotationInfo : typeAnnotationInfo) { if (annotationsToExclude == null || !annotationsToExclude.contains(annotationInfo)) { - buf.append(annotationInfo); + annotationInfo.toString(useSimpleNames, buf); buf.append(' '); } } diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index f50845623..5f83c499d 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -3010,63 +3010,63 @@ public int hashCode() { return name == null ? 0 : name.hashCode(); } + // ------------------------------------------------------------------------------------------------------------- + /** * To string. * - * @param typeNameOnly - * if true, convert type name to string only. - * @return the string + * @param useSimpleNames + * use simple names + * @param buf + * the buf */ - protected String toString(final boolean typeNameOnly) { + @Override + protected void toString(final boolean useSimpleNames, final StringBuilder buf) { + if (annotationInfo != null) { + for (final AnnotationInfo annotation : annotationInfo) { + if (buf.length() > 0) { + buf.append(' '); + } + annotation.toString(useSimpleNames, buf); + } + } final ClassTypeSignature typeSig = getTypeSignature(); if (typeSig != null) { // Generic classes - return typeSig.toString(name, /* useSimpleNames = */ false, typeNameOnly, modifiers, isAnnotation(), - isInterface(), /* annotationsToExclude = */ null); + typeSig.toStringInternal(useSimpleNames ? ClassInfo.getSimpleName(name) : name, + /* useSimpleNames = */ false, modifiers, isAnnotation(), isInterface(), + /* annotationsToExclude = */ annotationInfo, buf); } else { // Non-generic classes - final StringBuilder buf = new StringBuilder(); - if (typeNameOnly) { - buf.append(name); - } else { - TypeUtils.modifiersToString(modifiers, ModifierType.CLASS, /* ignored */ false, buf); - if (buf.length() > 0) { - buf.append(' '); - } - buf.append(isRecord() ? "record " // - : isEnum() ? "enum " // - : isAnnotation() ? "@interface " // - : isInterface() ? "interface " // - : "class "); - buf.append(name); - final ClassInfo superclass = getSuperclass(); - if (superclass != null && !superclass.getName().equals("java.lang.Object")) { - buf.append(" extends ").append(superclass.toString(/* typeNameOnly = */ true)); - } - final Set interfaces = this.filterClassInfo(RelType.IMPLEMENTED_INTERFACES, - /* strictAccept = */ false).directlyRelatedClasses; - if (!interfaces.isEmpty()) { - buf.append(isInterface() ? " extends " : " implements "); - boolean first = true; - for (final ClassInfo iface : interfaces) { - if (first) { - first = false; - } else { - buf.append(", "); - } - buf.append(iface.toString(/* typeNameOnly = */ true)); + TypeUtils.modifiersToString(modifiers, ModifierType.CLASS, /* ignored */ false, buf); + if (buf.length() > 0) { + buf.append(' '); + } + buf.append(isRecord() ? "record " // + : isEnum() ? "enum " // + : isAnnotation() ? "@interface " // + : isInterface() ? "interface " // + : "class "); + buf.append(useSimpleNames ? ClassInfo.getSimpleName(name) : name); + final ClassInfo superclass = getSuperclass(); + if (superclass != null && !superclass.getName().equals("java.lang.Object")) { + buf.append(" extends "); + superclass.toString(useSimpleNames, buf); + } + final Set interfaces = this.filterClassInfo(RelType.IMPLEMENTED_INTERFACES, + /* strictAccept = */ false).directlyRelatedClasses; + if (!interfaces.isEmpty()) { + buf.append(isInterface() ? " extends " : " implements "); + boolean first = true; + for (final ClassInfo iface : interfaces) { + if (first) { + first = false; + } else { + buf.append(", "); } + iface.toString(useSimpleNames, buf); } } - return buf.toString(); } } - - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return toString(false); - } } diff --git a/src/main/java/io/github/classgraph/ClassRefTypeSignature.java b/src/main/java/io/github/classgraph/ClassRefTypeSignature.java index 6b83624e3..cbfb8a3e3 100644 --- a/src/main/java/io/github/classgraph/ClassRefTypeSignature.java +++ b/src/main/java/io/github/classgraph/ClassRefTypeSignature.java @@ -370,6 +370,8 @@ public boolean equalsIgnoringTypeParams(final TypeSignature other) { && suffixesMatch(o, this); } + // ------------------------------------------------------------------------------------------------------------- + @Override protected void toStringInternal(final boolean useSimpleNames, final AnnotationInfoList annotationsToExclude, final StringBuilder buf) { @@ -379,7 +381,7 @@ protected void toStringInternal(final boolean useSimpleNames, final AnnotationIn if (typeAnnotationInfo != null) { for (final AnnotationInfo annotationInfo : typeAnnotationInfo) { if (annotationsToExclude == null || !annotationsToExclude.contains(annotationInfo)) { - buf.append(annotationInfo); + annotationInfo.toString(useSimpleNames, buf); buf.append(' '); } } @@ -393,7 +395,7 @@ protected void toStringInternal(final boolean useSimpleNames, final AnnotationIn if (i > 0) { buf.append(", "); } - buf.append(typeArguments.get(i).toString(useSimpleNames)); + typeArguments.get(i).toString(useSimpleNames, buf); } buf.append('>'); } @@ -417,7 +419,7 @@ protected void toStringInternal(final boolean useSimpleNames, final AnnotationIn } if (typeAnnotations != null && !typeAnnotations.isEmpty()) { for (final AnnotationInfo annotationInfo : typeAnnotations) { - buf.append(annotationInfo); + annotationInfo.toString(useSimpleNames, buf); buf.append(' '); } } @@ -431,7 +433,7 @@ protected void toStringInternal(final boolean useSimpleNames, final AnnotationIn if (j > 0) { buf.append(", "); } - buf.append(suffixTypeArgumentsList.get(j).toString(useSimpleNames)); + suffixTypeArgumentsList.get(j).toString(useSimpleNames, buf); } buf.append('>'); } @@ -439,6 +441,8 @@ protected void toStringInternal(final boolean useSimpleNames, final AnnotationIn } } + // ------------------------------------------------------------------------------------------------------------- + /** * Parse a class type signature. * diff --git a/src/main/java/io/github/classgraph/ClassTypeSignature.java b/src/main/java/io/github/classgraph/ClassTypeSignature.java index b7623c4bb..a02812aaf 100644 --- a/src/main/java/io/github/classgraph/ClassTypeSignature.java +++ b/src/main/java/io/github/classgraph/ClassTypeSignature.java @@ -225,36 +225,36 @@ public boolean equals(final Object obj) { /** * Render into String form. - * + * * @param className * The class name - * @param typeNameOnly - * If true, only return the type name (and generic type parameters). + * @param useSimpleNames + * the use simple names * @param modifiers * The class modifiers. * @param isAnnotation * True if the class is an annotation. * @param isInterface * True if the class is an interface. - * @return The String representation. + * @param annotationsToExclude + * the annotations to exclude + * @param buf + * the buf */ - String toString(final String className, final boolean useSimpleNames, final boolean typeNameOnly, - final int modifiers, final boolean isAnnotation, final boolean isInterface, - final AnnotationInfoList annotationsToExclude) { - final StringBuilder buf = new StringBuilder(); - if (!typeNameOnly) { - if (modifiers != 0) { - TypeUtils.modifiersToString(modifiers, ModifierType.CLASS, /* ignored */ false, buf); - if (buf.length() > 0) { - buf.append(' '); - } + void toStringInternal(final String className, final boolean useSimpleNames, final int modifiers, + final boolean isAnnotation, final boolean isInterface, final AnnotationInfoList annotationsToExclude, + final StringBuilder buf) { + if (modifiers != 0) { + TypeUtils.modifiersToString(modifiers, ModifierType.CLASS, /* ignored */ false, buf); + if (buf.length() > 0) { + buf.append(' '); } - buf.append(isAnnotation ? "@interface" - : isInterface ? "interface" : (modifiers & 0x4000) != 0 ? "enum" : "class"); - buf.append(' '); } + buf.append(isAnnotation ? "@interface" + : isInterface ? "interface" : (modifiers & 0x4000) != 0 ? "enum" : "class"); + buf.append(' '); if (className != null) { - buf.append(className); + buf.append(useSimpleNames ? ClassInfo.getSimpleName(className) : className); } if (!typeParameters.isEmpty()) { buf.append('<'); @@ -266,34 +266,41 @@ String toString(final String className, final boolean useSimpleNames, final bool } buf.append('>'); } - if (!typeNameOnly) { - if (superclassSignature != null) { - final String superSig = superclassSignature.toString(useSimpleNames); - // superSig could have a class type annotation even if the superclass is Object - if (!superSig.equals("java.lang.Object") && !(superSig.equals("Object") - && superclassSignature.className.equals("java.lang.Object"))) { - buf.append(" extends "); - buf.append(superSig); - } + if (superclassSignature != null) { + final String superSig = superclassSignature.toString(useSimpleNames); + // superSig could have a class type annotation even if the superclass is Object + if (!superSig.equals("java.lang.Object") + && !(superSig.equals("Object") && superclassSignature.className.equals("java.lang.Object"))) { + buf.append(" extends "); + buf.append(superSig); } - if (!superinterfaceSignatures.isEmpty()) { - buf.append(isInterface ? " extends " : " implements "); - for (int i = 0; i < superinterfaceSignatures.size(); i++) { - if (i > 0) { - buf.append(", "); - } - superinterfaceSignatures.get(i).toStringInternal(useSimpleNames, null, buf); + } + if (!superinterfaceSignatures.isEmpty()) { + buf.append(isInterface ? " extends " : " implements "); + for (int i = 0; i < superinterfaceSignatures.size(); i++) { + if (i > 0) { + buf.append(", "); } + superinterfaceSignatures.get(i).toStringInternal(useSimpleNames, null, buf); } } - return buf.toString(); } + /** + * To string internal. + * + * @param useSimpleNames + * the use simple names + * @param annotationsToExclude + * the annotations to exclude + * @param buf + * the buf + */ @Override protected void toStringInternal(final boolean useSimpleNames, final AnnotationInfoList annotationsToExclude, final StringBuilder buf) { - buf.append(toString(classInfo.getName(), useSimpleNames, /* typeNameOnly = */ false, - classInfo.getModifiers(), classInfo.isAnnotation(), classInfo.isInterface(), annotationsToExclude)); + toStringInternal(classInfo.getName(), useSimpleNames, classInfo.getModifiers(), classInfo.isAnnotation(), + classInfo.isInterface(), annotationsToExclude, buf); } // ------------------------------------------------------------------------------------------------------------- diff --git a/src/main/java/io/github/classgraph/FieldInfo.java b/src/main/java/io/github/classgraph/FieldInfo.java index 086d4b5b5..ed6329805 100644 --- a/src/main/java/io/github/classgraph/FieldInfo.java +++ b/src/main/java/io/github/classgraph/FieldInfo.java @@ -521,19 +521,16 @@ public int compareTo(final FieldInfo other) { return name.compareTo(other.name); } - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - final StringBuilder buf = new StringBuilder(); + // ------------------------------------------------------------------------------------------------------------- + @Override + protected void toString(final boolean useSimpleNames, final StringBuilder buf) { if (annotationInfo != null) { for (final AnnotationInfo annotation : annotationInfo) { if (buf.length() > 0) { buf.append(' '); } - buf.append(annotation.toString()); + annotation.toString(useSimpleNames, buf); } } @@ -548,7 +545,7 @@ public String toString() { buf.append(' '); } final TypeSignature typeSig = getTypeSignatureOrTypeDescriptor(); - typeSig.toStringInternal(/* useSimpleNames = */ false, /* annotationsToExclude = */ annotationInfo, buf); + typeSig.toStringInternal(useSimpleNames, /* annotationsToExclude = */ annotationInfo, buf); buf.append(' '); buf.append(name); @@ -562,10 +559,8 @@ public String toString() { buf.append('\'').append(((Character) val).toString().replace("\\", "\\\\").replaceAll("'", "\\'")) .append('\''); } else { - buf.append(val.toString()); + buf.append(val == null ? "null" : val.toString()); } } - - return buf.toString(); } } diff --git a/src/main/java/io/github/classgraph/HierarchicalTypeSignature.java b/src/main/java/io/github/classgraph/HierarchicalTypeSignature.java index 712c2bb25..0f67ed77e 100644 --- a/src/main/java/io/github/classgraph/HierarchicalTypeSignature.java +++ b/src/main/java/io/github/classgraph/HierarchicalTypeSignature.java @@ -81,7 +81,7 @@ void setScanResult(final ScanResult scanResult) { protected abstract void addTypeAnnotation(List typePath, AnnotationInfo annotationInfo); /** - * {@link #toString()} internal method. + * Render type signature to string. * * @param useSimpleNames * whether to use simple names for classes. @@ -91,38 +91,19 @@ void setScanResult(final ScanResult scanResult) { * @param buf * the {@link StringBuilder} to write to. */ - abstract void toStringInternal(final boolean useSimpleNames, AnnotationInfoList annotationsToExclude, + protected abstract void toStringInternal(final boolean useSimpleNames, AnnotationInfoList annotationsToExclude, StringBuilder buf); /** - * {@link #toString()} that renders only simple names for classes. - * - * @return the string representation of the type - */ - public String toStringWithSimpleNames() { - final StringBuilder buf = new StringBuilder(); - toStringInternal(true, null, buf); - return buf.toString(); - } - - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - final StringBuilder buf = new StringBuilder(); - toStringInternal(false, null, buf); - return buf.toString(); - } - - /** - * {@link #toString()} that renders only simple names for classes if requested. + * Render type signature to string. * * @param useSimpleNames - * if true, use just the simple name of each class. - * @return the string representation of the type + * whether to use simple names for classes. + * @param buf + * the {@link StringBuilder} to write to. */ - public String toString(final boolean useSimpleNames) { - return useSimpleNames ? toStringWithSimpleNames() : toString(); + @Override + protected void toString(final boolean useSimpleNames, final StringBuilder buf) { + toStringInternal(useSimpleNames, /* annotationsToExclude = */ null, buf); } } \ No newline at end of file diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index 20fecb174..d3e275764 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -836,20 +836,21 @@ public int compareTo(final MethodInfo other) { * Get a string representation of the method. Note that constructors are named {@code ""}, and private * static class initializer blocks are named {@code ""}. * - * @return the string representation of the method. + * @param useSimpleNames + * the use simple names + * @param buf + * the buf */ @Override - public String toString() { + protected void toString(final boolean useSimpleNames, final StringBuilder buf) { final MethodTypeSignature methodType = getTypeSignatureOrTypeDescriptor(); - final StringBuilder buf = new StringBuilder(); - if (annotationInfo != null) { for (final AnnotationInfo annotation : annotationInfo) { if (buf.length() > 0) { buf.append(' '); } - annotation.toString(buf); + annotation.toString(useSimpleNames, buf); } } @@ -870,8 +871,7 @@ public String toString() { if (i > 0) { buf.append(", "); } - final String typeParamStr = typeParameters.get(i).toString(); - buf.append(typeParamStr); + typeParameters.get(i).toString(useSimpleNames, buf); } buf.append('>'); } @@ -880,13 +880,13 @@ public String toString() { if (buf.length() > 0) { buf.append(' '); } - methodType.getResultType().toStringInternal(/* useSimpleNames = */ false, - /* annotationsToExclude = */ annotationInfo, buf); + methodType.getResultType().toStringInternal(useSimpleNames, /* annotationsToExclude = */ annotationInfo, + buf); } buf.append(' '); if (name != null) { - buf.append(name); + buf.append(useSimpleNames ? ClassInfo.getSimpleName(name) : name); } // If at least one param is named, then use placeholder names for unnamed params, @@ -926,7 +926,7 @@ public String toString() { if (paramInfo.annotationInfo != null) { for (final AnnotationInfo ai : paramInfo.annotationInfo) { - ai.toString(buf); + ai.toString(useSimpleNames, buf); buf.append(' '); } } @@ -945,7 +945,7 @@ public String toString() { throw new IllegalArgumentException( "Got a zero-dimension array type for last parameter of varargs method " + name); } - buf.append(arrayType.getElementTypeSignature().toString()); + arrayType.getElementTypeSignature().toString(useSimpleNames, buf); for (int j = 0; j < arrayType.getNumDimensions() - 1; j++) { buf.append("[]"); } @@ -962,7 +962,7 @@ public String toString() { annotationsToExclude.add(paramInfo.annotationInfo[j]); } } - paramTypeSignature.toStringInternal(/* useSimpleNames = */ false, annotationsToExclude, buf); + paramTypeSignature.toStringInternal(useSimpleNames, annotationsToExclude, buf); } if (hasParamNames) { @@ -981,9 +981,8 @@ public String toString() { if (i > 0) { buf.append(", "); } - buf.append(methodType.getThrowsSignatures().get(i).toString()); + methodType.getThrowsSignatures().get(i).toString(useSimpleNames, buf); } } - return buf.toString(); } } diff --git a/src/main/java/io/github/classgraph/MethodParameterInfo.java b/src/main/java/io/github/classgraph/MethodParameterInfo.java index 81271d979..abb48f037 100644 --- a/src/main/java/io/github/classgraph/MethodParameterInfo.java +++ b/src/main/java/io/github/classgraph/MethodParameterInfo.java @@ -314,27 +314,52 @@ static void modifiersToString(final int modifiers, final StringBuilder buf) { } } - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - final StringBuilder buf = new StringBuilder(); + // ------------------------------------------------------------------------------------------------------------- + /** + * Render to string. + * + * @param useSimpleNames + * if true, use just the simple name of each class. + * @param buf + * the buf + */ + protected void toString(final boolean useSimpleNames, final StringBuilder buf) { if (annotationInfo != null) { for (final AnnotationInfo anAnnotationInfo : annotationInfo) { - anAnnotationInfo.toString(buf); + anAnnotationInfo.toString(useSimpleNames, buf); buf.append(' '); } } modifiersToString(modifiers, buf); - buf.append(getTypeSignatureOrTypeDescriptor().toString()); + getTypeSignatureOrTypeDescriptor().toString(useSimpleNames, buf); buf.append(' '); buf.append(name == null ? "_unnamed_param" : name); + } + + /** + * Render to string with simple names for classes. + * + * @return the string representation. + */ + public String toStringWithSimpleNames() { + final StringBuilder buf = new StringBuilder(); + toString(/* useSimpleNames = */ true, buf); + return buf.toString(); + } + /** + * Render to string. + * + * @return the string representation. + */ + @Override + public String toString() { + final StringBuilder buf = new StringBuilder(); + toString(/* useSimpleNames = */ false, buf); return buf.toString(); } } diff --git a/src/main/java/io/github/classgraph/MethodTypeSignature.java b/src/main/java/io/github/classgraph/MethodTypeSignature.java index 022e98614..979b9e091 100644 --- a/src/main/java/io/github/classgraph/MethodTypeSignature.java +++ b/src/main/java/io/github/classgraph/MethodTypeSignature.java @@ -266,6 +266,8 @@ public boolean equals(final Object obj) { && o.resultType.equals(this.resultType) && o.throwsSignatures.equals(this.throwsSignatures); } + // ------------------------------------------------------------------------------------------------------------- + @Override protected void toStringInternal(final boolean useSimpleNames, final AnnotationInfoList annotationsToExclude, final StringBuilder buf) { @@ -275,8 +277,7 @@ protected void toStringInternal(final boolean useSimpleNames, final AnnotationIn if (i > 0) { buf.append(", "); } - final String typeParamStr = typeParameters.get(i).toString(useSimpleNames); - buf.append(typeParamStr); + typeParameters.get(i).toString(useSimpleNames, buf); } buf.append('>'); } @@ -291,7 +292,7 @@ protected void toStringInternal(final boolean useSimpleNames, final AnnotationIn if (i > 0) { buf.append(", "); } - buf.append(parameterTypeSignatures.get(i).toString(useSimpleNames)); + parameterTypeSignatures.get(i).toString(useSimpleNames, buf); } buf.append(')'); @@ -301,7 +302,7 @@ protected void toStringInternal(final boolean useSimpleNames, final AnnotationIn if (i > 0) { buf.append(", "); } - buf.append(throwsSignatures.get(i).toString(useSimpleNames)); + throwsSignatures.get(i).toString(useSimpleNames, buf); } } } diff --git a/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java b/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java index 815745a9c..3ad0ec083 100644 --- a/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java +++ b/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java @@ -613,4 +613,56 @@ public boolean equals(final Object other) { && Arrays.equals(byteArrayValue, o.byteArrayValue) && Arrays.deepEquals(objectArrayValue, o.objectArrayValue); } + + // ------------------------------------------------------------------------------------------------------------- + + @Override + protected void toString(final boolean useSimpleNames, final StringBuilder buf) { + if (annotationEnumValue != null) { + annotationEnumValue.toString(useSimpleNames, buf); + } else if (annotationClassRef != null) { + annotationClassRef.toString(useSimpleNames, buf); + } else if (annotationInfo != null) { + annotationInfo.toString(useSimpleNames, buf); + } else if (stringValue != null) { + buf.append(stringValue); + } else if (integerValue != null) { + buf.append(Integer.toString(integerValue)); + } else if (longValue != null) { + buf.append(Long.toString(longValue)); + } else if (shortValue != null) { + buf.append(Short.toString(shortValue)); + } else if (booleanValue != null) { + buf.append(Boolean.toString(booleanValue)); + } else if (characterValue != null) { + buf.append(Character.toString(characterValue)); + } else if (floatValue != null) { + buf.append(Float.toString(floatValue)); + } else if (doubleValue != null) { + buf.append(Double.toString(doubleValue)); + } else if (byteValue != null) { + buf.append(Byte.toString(byteValue)); + } else if (stringArrayValue != null) { + buf.append(Arrays.toString(stringArrayValue)); + } else if (intArrayValue != null) { + buf.append(Arrays.toString(intArrayValue)); + } else if (longArrayValue != null) { + buf.append(Arrays.toString(longArrayValue)); + } else if (shortArrayValue != null) { + buf.append(Arrays.toString(shortArrayValue)); + } else if (booleanArrayValue != null) { + buf.append(Arrays.toString(booleanArrayValue)); + } else if (charArrayValue != null) { + buf.append(Arrays.toString(charArrayValue)); + } else if (floatArrayValue != null) { + buf.append(Arrays.toString(floatArrayValue)); + } else if (doubleArrayValue != null) { + buf.append(Arrays.toString(doubleArrayValue)); + } else if (byteArrayValue != null) { + buf.append(Arrays.toString(byteArrayValue)); + } else if (objectArrayValue != null) { + // TODO this doesn't handle nested arrays, but this toString() method is only used for debugging + buf.append(Arrays.toString(objectArrayValue)); + } + } } \ No newline at end of file diff --git a/src/main/java/io/github/classgraph/ScanResultObject.java b/src/main/java/io/github/classgraph/ScanResultObject.java index fd7de4725..eb0b98e87 100644 --- a/src/main/java/io/github/classgraph/ScanResultObject.java +++ b/src/main/java/io/github/classgraph/ScanResultObject.java @@ -36,7 +36,6 @@ * A superclass of objects accessible from a {@link ScanResult} that are associated with a {@link ClassInfo} object. */ abstract class ScanResultObject { - /** The scan result. */ transient protected ScanResult scanResult; @@ -46,6 +45,8 @@ abstract class ScanResultObject { /** The class ref, once the class is loaded. */ protected transient Class classRef; + // ------------------------------------------------------------------------------------------------------------- + /** * Set ScanResult backreferences in info objects after scan has completed. * @@ -85,6 +86,8 @@ protected void findReferencedClassInfo(final Map classNameToC } } + // ------------------------------------------------------------------------------------------------------------- + /** * The name of the class (used by {@link #getClassInfo()} to fetch the {@link ClassInfo} object for the class). * @@ -142,6 +145,8 @@ private String getClassInfoNameOrClassName() { return className; } + // ------------------------------------------------------------------------------------------------------------- + /** * Load the class named returned by {@link #getClassInfo()}, or if that returns null, the class named by * {@link #getClassName()}. Returns a {@code Class} reference for the class, cast to the requested superclass @@ -238,4 +243,52 @@ Class loadClass(final boolean ignoreExceptions) { Class loadClass() { return loadClass(/* ignoreExceptions = */ false); } + + // ------------------------------------------------------------------------------------------------------------- + + /** + * Render to string. + * + * @param useSimpleNames + * if true, use just the simple name of each class. + * @param buf + * the buf + */ + protected abstract void toString(final boolean useSimpleNames, StringBuilder buf); + + /** + * Render to string, with simple names for classes if useSimpleNames is true. + * + * @param useSimpleNames + * if true, use just the simple name of each class. + * @return the string representation. + */ + String toString(final boolean useSimpleNames) { + final StringBuilder buf = new StringBuilder(); + toString(useSimpleNames, buf); + return buf.toString(); + } + + /** + * Render to string, using only simple names for classes. + * + * @return the string representation, using simple names for classes. + */ + public String toStringWithSimpleNames() { + final StringBuilder buf = new StringBuilder(); + toString(/* useSimpleNames = */ true, buf); + return buf.toString(); + } + + /** + * Render to string. + * + * @return the string representation. + */ + @Override + public String toString() { + final StringBuilder buf = new StringBuilder(); + toString(/* useSimpleNames = */ false, buf); + return buf.toString(); + } } \ No newline at end of file diff --git a/src/main/java/io/github/classgraph/TypeArgument.java b/src/main/java/io/github/classgraph/TypeArgument.java index 30a3920d3..a2b678426 100644 --- a/src/main/java/io/github/classgraph/TypeArgument.java +++ b/src/main/java/io/github/classgraph/TypeArgument.java @@ -250,13 +250,15 @@ public boolean equals(final Object obj) { && (other.typeSignature.equals(this.typeSignature) && other.wildcard.equals(this.wildcard)); } + // ------------------------------------------------------------------------------------------------------------- + @Override protected void toStringInternal(final boolean useSimpleNames, final AnnotationInfoList annotationsToExclude, final StringBuilder buf) { if (typeAnnotationInfo != null) { for (final AnnotationInfo annotationInfo : typeAnnotationInfo) { if (annotationsToExclude == null || !annotationsToExclude.contains(annotationInfo)) { - buf.append(annotationInfo); + annotationInfo.toString(useSimpleNames, buf); buf.append(' '); } } @@ -270,10 +272,11 @@ protected void toStringInternal(final boolean useSimpleNames, final AnnotationIn buf.append(typeSigStr.equals("java.lang.Object") ? "?" : "? extends " + typeSigStr); break; case SUPER: - buf.append("? super " + typeSignature.toString(useSimpleNames)); + buf.append("? super "); + typeSignature.toString(useSimpleNames, buf); break; case NONE: - buf.append(typeSignature.toString(useSimpleNames)); + typeSignature.toString(useSimpleNames, buf); break; default: // Should not happen diff --git a/src/main/java/io/github/classgraph/TypeParameter.java b/src/main/java/io/github/classgraph/TypeParameter.java index 63a4486fc..53c1a5bb8 100644 --- a/src/main/java/io/github/classgraph/TypeParameter.java +++ b/src/main/java/io/github/classgraph/TypeParameter.java @@ -236,18 +236,20 @@ public boolean equals(final Object obj) { && other.interfaceBounds.equals(this.interfaceBounds); } + // ------------------------------------------------------------------------------------------------------------- + @Override protected void toStringInternal(final boolean useSimpleNames, final AnnotationInfoList annotationsToExclude, final StringBuilder buf) { if (typeAnnotationInfo != null) { for (final AnnotationInfo annotationInfo : typeAnnotationInfo) { if (annotationsToExclude == null || !annotationsToExclude.contains(annotationInfo)) { - buf.append(annotationInfo); + annotationInfo.toString(useSimpleNames, buf); buf.append(' '); } } } - buf.append(name); + buf.append(useSimpleNames ? ClassInfo.getSimpleName(name) : name); String classBoundStr; if (classBound == null) { classBoundStr = null; @@ -271,7 +273,7 @@ protected void toStringInternal(final boolean useSimpleNames, final AnnotationIn buf.append(" &"); } buf.append(' '); - buf.append(interfaceBounds.get(i).toString(useSimpleNames)); + interfaceBounds.get(i).toString(useSimpleNames, buf); } } } \ No newline at end of file diff --git a/src/main/java/io/github/classgraph/TypeVariableSignature.java b/src/main/java/io/github/classgraph/TypeVariableSignature.java index 95f57f4e2..995481c5b 100644 --- a/src/main/java/io/github/classgraph/TypeVariableSignature.java +++ b/src/main/java/io/github/classgraph/TypeVariableSignature.java @@ -301,7 +301,7 @@ protected void toStringInternal(final boolean useSimpleNames, final AnnotationIn if (typeAnnotationInfo != null) { for (final AnnotationInfo annotationInfo : typeAnnotationInfo) { if (annotationsToExclude == null || !annotationsToExclude.contains(annotationInfo)) { - buf.append(annotationInfo); + annotationInfo.toString(useSimpleNames, buf); buf.append(' '); } } 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 8094cbdc5..3a30c04fa 100644 --- a/src/test/java/io/github/classgraph/issues/issue402/TypeAnnotationTest.java +++ b/src/test/java/io/github/classgraph/issues/issue402/TypeAnnotationTest.java @@ -9,12 +9,10 @@ import org.junit.jupiter.api.Test; -import io.github.classgraph.AnnotationInfo; import io.github.classgraph.ClassGraph; import io.github.classgraph.ClassInfo; import io.github.classgraph.ClassRefTypeSignature; import io.github.classgraph.FieldInfo; -import io.github.classgraph.MethodInfo; import io.github.classgraph.ScanResult; /** @@ -124,6 +122,7 @@ public void explicitReceiver(@F P this) { int max(); } + @A class Person { List<@Size(max = 50) String> emails; } @@ -144,21 +143,29 @@ private static String shortNames(final Object type) { /** Test field type annotations. */ @Test - void fieldTypeAnnotations() { + void typeAnnotations() { try (ScanResult scanResult = new ClassGraph() .acceptPackages(TypeAnnotationTest.class.getPackage().getName()).enableAllInfo().scan()) { final ClassInfo classInfo = scanResult.getClassInfo(TypeAnnotationTest.class.getName()); - assertThat(shortNames(classInfo.getFieldInfo("map"))) + final FieldInfo mapField = classInfo.getFieldInfo("map"); + assertThat(shortNames(mapField)).isEqualTo("@A Map<@B ? extends @C String, @D List<@E Object>> map"); + assertThat(mapField.toStringWithSimpleNames()) .isEqualTo("@A Map<@B ? extends @C String, @D List<@E Object>> map"); - assertThat(shortNames(classInfo.getFieldInfo("arr"))).isEqualTo("@I String @F [] @G [] @H [] arr"); + final FieldInfo arrField = classInfo.getFieldInfo("arr"); + assertThat(shortNames(arrField)).isEqualTo("@I String @F [] @G [] @H [] arr"); + assertThat(arrField.toStringWithSimpleNames()).isEqualTo("@I String @F [] @G [] @H [] arr"); - assertThat(shortNames(classInfo.getFieldInfo("comparable"))) + final FieldInfo comparableField = classInfo.getFieldInfo("comparable"); + assertThat(shortNames(comparableField)) + .isEqualTo("@A List<@B Comparable<@F Object @C [] @D [] @E []>> comparable"); + assertThat(comparableField.toStringWithSimpleNames()) .isEqualTo("@A List<@B Comparable<@F Object @C [] @D [] @E []>> comparable"); - assertThat(shortNames(classInfo.getFieldInfo("inner1"))) - .isEqualTo("@A Outer.@B Middle.@C Inner1 inner1"); + final FieldInfo inner1Field = classInfo.getFieldInfo("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"); @@ -169,7 +176,9 @@ void fieldTypeAnnotations() { assertThat(shortNames(classInfo.getFieldInfo("inner4"))) .isEqualTo("Outer.MiddleGeneric<@A Foo.@B Bar>.InnerGeneric<@D String @C []> inner4"); - assertThat(shortNames(classInfo.getFieldInfo("xyz"))).isEqualTo("List<@A X.@B Y.@C Z> xyz"); + final FieldInfo xyzField = classInfo.getFieldInfo("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"); @@ -180,30 +189,29 @@ void fieldTypeAnnotations() { assertThat(shortNames(classInfo.getMethodInfo("t").get(0))) .isEqualTo("<@A T extends @B U> @D U t(@E T)"); - final FieldInfo emailsFieldInfo = scanResult.getClassInfo(Person.class.getName()) - .getFieldInfo("emails"); + assertThat(classInfo.getMethodInfo("t").get(0).toStringWithSimpleNames()) + .isEqualTo("<@A T extends @B U> @D U t(@E T)"); + + final ClassInfo personClassInfo = scanResult.getClassInfo(Person.class.getName()); - assertThat(shortNames(emailsFieldInfo)).isEqualTo("List<@Size(max=50) String> emails"); + final FieldInfo emailsField = personClassInfo.getFieldInfo("emails"); + assertThat(shortNames(emailsField)).isEqualTo("List<@Size(max=50) String> emails"); + assertThat(emailsField.toStringWithSimpleNames()).isEqualTo("List<@Size(max=50) String> emails"); - assertThat(shortNames(((ClassRefTypeSignature) emailsFieldInfo.getTypeSignatureOrTypeDescriptor()) + assertThat(shortNames(((ClassRefTypeSignature) emailsField.getTypeSignatureOrTypeDescriptor()) .getTypeArguments().get(0).getTypeSignature().getTypeAnnotationInfo().get(0))) .isEqualTo("@Size(max=50)"); - } - } - /** Test class and method type annotations. */ - @Test - void classAndMethodTypeAnnotations() { - try (ScanResult scanResult = new ClassGraph() - .acceptPackages(TypeAnnotationTest.class.getPackage().getName()).enableAllInfo().scan()) { - final ClassInfo classInfo = scanResult.getClassInfo(P.class.getName()); + assertThat(shortNames(personClassInfo)).isEqualTo("@A class Person"); + assertThat(personClassInfo.toStringWithSimpleNames()).isEqualTo("@A class Person"); + + final ClassInfo pClassInfo = scanResult.getClassInfo(P.class.getName()); + + assertThat(shortNames(pClassInfo)).isEqualTo("static class P<@A T extends @B U & @C V>"); - assertThat(shortNames(classInfo)).isEqualTo("static class P<@A T extends @B U & @C V>"); + assertThat(shortNames(pClassInfo.getMethodInfo("explicitReceiver").get(0) + .getTypeSignatureOrTypeDescriptor().getReceiverTypeAnnotationInfo().get(0))).isEqualTo("@F"); - final MethodInfo methodInfo = classInfo.getMethodInfo("explicitReceiver").get(0); - final AnnotationInfo receiverTypeAnnotationInfo = methodInfo.getTypeSignatureOrTypeDescriptor() - .getReceiverTypeAnnotationInfo().get(0); - assertThat(shortNames(receiverTypeAnnotationInfo)).isEqualTo("@F"); } } } From d7474ac8ed0c2322783ed235478d4dca2088fa22 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 18 Dec 2020 03:24:18 -0700 Subject: [PATCH 0970/1778] Fix compilation warnings --- src/main/java/io/github/classgraph/Classfile.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index 73e1770c8..5ad3ecc36 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -1532,10 +1532,10 @@ private void readMethods() throws IOException, ClassfileFormatException { methodTypeAnnotationDecorators = new ArrayList<>(annotationCount); for (int m = 0; m < annotationCount; m++) { final int targetType = reader.readUnsignedByte(); - int typeParameterIndex; - int boundIndex; - int formalParameterIndex; - int throwsTypeIndex; + final int typeParameterIndex; + final int boundIndex; + final int formalParameterIndex; + final int throwsTypeIndex; if (targetType == 0x01) { typeParameterIndex = reader.readUnsignedByte(); boundIndex = -1; @@ -1731,9 +1731,9 @@ private void readClassAttributes() throws IOException, ClassfileFormatException classTypeAnnotationDecorators = new ArrayList<>(annotationCount); for (int m = 0; m < annotationCount; m++) { final int targetType = reader.readUnsignedByte(); - int typeParameterIndex; - int supertypeIndex; - int boundIndex; + final int typeParameterIndex; + final int supertypeIndex; + final int boundIndex; if (targetType == 0x00) { typeParameterIndex = reader.readUnsignedByte(); supertypeIndex = -1; From 9262d8db060bbf0391c3be369d0fa963d9497789 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 18 Dec 2020 04:01:48 -0700 Subject: [PATCH 0971/1778] Make tests pass --- .../issues/issue402/TypeAnnotationTest.java | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) 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 3a30c04fa..a895adb16 100644 --- a/src/test/java/io/github/classgraph/issues/issue402/TypeAnnotationTest.java +++ b/src/test/java/io/github/classgraph/issues/issue402/TypeAnnotationTest.java @@ -2,8 +2,8 @@ import static org.assertj.core.api.Assertions.assertThat; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; import java.util.List; import java.util.Map; @@ -19,39 +19,39 @@ * TypeAnnotationTest. */ class TypeAnnotationTest { - @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.FIELD, ElementType.TYPE_USE, ElementType.TYPE_PARAMETER }) private static @interface A { } - @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE_USE, ElementType.TYPE_PARAMETER }) private static @interface B { } - @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE_USE, ElementType.TYPE_PARAMETER }) private static @interface C { } - @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE_USE, ElementType.TYPE_PARAMETER }) private static @interface D { } - @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE_USE, ElementType.TYPE_PARAMETER }) private static @interface E { } - @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE_USE, ElementType.TYPE_PARAMETER }) private static @interface F { } - @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE_USE, ElementType.TYPE_PARAMETER }) private static @interface G { } - @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE_USE, ElementType.TYPE_PARAMETER }) private static @interface H { } - @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE_USE, ElementType.TYPE_PARAMETER }) private static @interface I { } @@ -117,7 +117,7 @@ public void explicitReceiver(@F P this) { } } - @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE_USE, ElementType.TYPE_PARAMETER }) private static @interface Size { int max(); } From 1901f173374b56cfe38aa27066451606cd9b89e0 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 18 Dec 2020 04:03:42 -0700 Subject: [PATCH 0972/1778] [maven-release-plugin] prepare release classgraph-4.8.96 --- pom.xml | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index 2222dab55..eae37be17 100644 --- a/pom.xml +++ b/pom.xml @@ -1,9 +1,12 @@ - + 4.0.0 io.github.classgraph classgraph - 4.8.96-SNAPSHOT + 4.8.96 ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -31,8 +34,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.95 - + classgraph-4.8.96 + https://github.com/classgraph/classgraph/issues @@ -268,10 +271,10 @@ - + org.codehaus.mojo.signature - java17 1.0 @@ -348,8 +351,13 @@ - - + + @@ -376,6 +384,10 @@ 7 7 + + 8 + 8 + false From 25cdfbf906300fcd7492e7c4479c7e0c8b136c15 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 18 Dec 2020 04:03:49 -0700 Subject: [PATCH 0973/1778] [maven-release-plugin] prepare for next development iteration --- pom.xml | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/pom.xml b/pom.xml index eae37be17..fdbf694c9 100644 --- a/pom.xml +++ b/pom.xml @@ -1,12 +1,9 @@ - + 4.0.0 io.github.classgraph classgraph - 4.8.96 + 4.8.97-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. @@ -271,8 +268,7 @@ - + org.codehaus.mojo.signature java17 @@ -351,13 +347,8 @@ - - + + From 40aec439aed5c7a44326d26b57cceb3547021f2a Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 18 Dec 2020 04:28:56 -0700 Subject: [PATCH 0974/1778] Throw IllegalArgumentException for methods that always return null: #402 --- src/main/java/io/github/classgraph/ClassTypeSignature.java | 7 +++++++ .../java/io/github/classgraph/MethodTypeSignature.java | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/src/main/java/io/github/classgraph/ClassTypeSignature.java b/src/main/java/io/github/classgraph/ClassTypeSignature.java index a02812aaf..daf373070 100644 --- a/src/main/java/io/github/classgraph/ClassTypeSignature.java +++ b/src/main/java/io/github/classgraph/ClassTypeSignature.java @@ -117,6 +117,13 @@ protected void addTypeAnnotation(final List typePath, final Annota "Cannot call this method on " + ClassTypeSignature.class.getSimpleName()); } + @Override + public AnnotationInfoList getTypeAnnotationInfo() { + throw new IllegalArgumentException("Cannot call this method on " + ClassTypeSignature.class.getSimpleName() + + " -- type annotations are accessible by calling getTypeParameters(), getSuperclassSignature(), " + + "and getSuperinterfaceSignature()."); + } + // ------------------------------------------------------------------------------------------------------------- /* (non-Javadoc) diff --git a/src/main/java/io/github/classgraph/MethodTypeSignature.java b/src/main/java/io/github/classgraph/MethodTypeSignature.java index 979b9e091..8ee0702ee 100644 --- a/src/main/java/io/github/classgraph/MethodTypeSignature.java +++ b/src/main/java/io/github/classgraph/MethodTypeSignature.java @@ -127,6 +127,13 @@ protected void addTypeAnnotation(final List typePath, final Annota "Cannot call this method on " + MethodTypeSignature.class.getSimpleName()); } + @Override + public AnnotationInfoList getTypeAnnotationInfo() { + throw new IllegalArgumentException("Cannot call this method on " + MethodTypeSignature.class.getSimpleName() + + " -- type annotations are accessible by calling getTypeParameters(), getResultType(), " + + "and getThrowsSignatures()."); + } + /** * Add a type annotation for an explicit receiver parameter. * From daa3d229df81680561a46591b19f1ed5009a2439 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 18 Dec 2020 04:36:52 -0700 Subject: [PATCH 0975/1778] Remove invalid methods from ClassTypeSignature and MethodTypeSignature --- .../java/io/github/classgraph/ArrayTypeSignature.java | 3 +-- .../java/io/github/classgraph/ClassTypeSignature.java | 7 ------- .../io/github/classgraph/HierarchicalTypeSignature.java | 9 --------- .../java/io/github/classgraph/MethodTypeSignature.java | 7 ------- src/main/java/io/github/classgraph/TypeSignature.java | 9 +++++++++ 5 files changed, 10 insertions(+), 25 deletions(-) diff --git a/src/main/java/io/github/classgraph/ArrayTypeSignature.java b/src/main/java/io/github/classgraph/ArrayTypeSignature.java index 6bb9df570..9266f561d 100644 --- a/src/main/java/io/github/classgraph/ArrayTypeSignature.java +++ b/src/main/java/io/github/classgraph/ArrayTypeSignature.java @@ -150,9 +150,8 @@ protected void addTypeAnnotation(final List typePath, final Annota * @return a list of {@link AnnotationInfo} objects for the type annotations of on this array type, or null if * none. */ - @Override public AnnotationInfoList getTypeAnnotationInfo() { - return super.getTypeAnnotationInfo(); + return typeAnnotationInfo; } // ------------------------------------------------------------------------------------------------------------- diff --git a/src/main/java/io/github/classgraph/ClassTypeSignature.java b/src/main/java/io/github/classgraph/ClassTypeSignature.java index daf373070..a02812aaf 100644 --- a/src/main/java/io/github/classgraph/ClassTypeSignature.java +++ b/src/main/java/io/github/classgraph/ClassTypeSignature.java @@ -117,13 +117,6 @@ protected void addTypeAnnotation(final List typePath, final Annota "Cannot call this method on " + ClassTypeSignature.class.getSimpleName()); } - @Override - public AnnotationInfoList getTypeAnnotationInfo() { - throw new IllegalArgumentException("Cannot call this method on " + ClassTypeSignature.class.getSimpleName() - + " -- type annotations are accessible by calling getTypeParameters(), getSuperclassSignature(), " - + "and getSuperinterfaceSignature()."); - } - // ------------------------------------------------------------------------------------------------------------- /* (non-Javadoc) diff --git a/src/main/java/io/github/classgraph/HierarchicalTypeSignature.java b/src/main/java/io/github/classgraph/HierarchicalTypeSignature.java index 0f67ed77e..f0e9aea10 100644 --- a/src/main/java/io/github/classgraph/HierarchicalTypeSignature.java +++ b/src/main/java/io/github/classgraph/HierarchicalTypeSignature.java @@ -38,15 +38,6 @@ public abstract class HierarchicalTypeSignature extends ScanResultObject { protected AnnotationInfoList typeAnnotationInfo; - /** - * 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/MethodTypeSignature.java b/src/main/java/io/github/classgraph/MethodTypeSignature.java index 8ee0702ee..979b9e091 100644 --- a/src/main/java/io/github/classgraph/MethodTypeSignature.java +++ b/src/main/java/io/github/classgraph/MethodTypeSignature.java @@ -127,13 +127,6 @@ protected void addTypeAnnotation(final List typePath, final Annota "Cannot call this method on " + MethodTypeSignature.class.getSimpleName()); } - @Override - public AnnotationInfoList getTypeAnnotationInfo() { - throw new IllegalArgumentException("Cannot call this method on " + MethodTypeSignature.class.getSimpleName() - + " -- type annotations are accessible by calling getTypeParameters(), getResultType(), " - + "and getThrowsSignatures()."); - } - /** * Add a type annotation for an explicit receiver parameter. * diff --git a/src/main/java/io/github/classgraph/TypeSignature.java b/src/main/java/io/github/classgraph/TypeSignature.java index c95e45617..d143ee8e4 100644 --- a/src/main/java/io/github/classgraph/TypeSignature.java +++ b/src/main/java/io/github/classgraph/TypeSignature.java @@ -81,6 +81,15 @@ final protected void findReferencedClassInfo(final Map classN } } + /** + * 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; + } + /** * Compare base types, ignoring generic type parameters. * From fac0846cf3e45a97bf225852db46016712558edf Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 18 Dec 2020 04:58:55 -0700 Subject: [PATCH 0976/1778] Update feature list --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 8dd24a644..fe041a7f8 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,7 @@ ClassGraph provides a number of important capabilities to the JVM ecosystem: * ClassGraph scans the classpath or module path using [carefully optimized multithreaded code](https://github.com/classgraph/classgraph/wiki/How-fast-is-ClassGraph) for the shortest possible scan times, and it runs as close as possible to I/O bandwidth limits, even on a fast SSD. * ClassGraph handles more [classpath specification mechanisms](https://github.com/classgraph/classgraph/wiki/Classpath-specification-mechanisms) found in the wild than any other classpath scanner, making code that depends upon ClassGraph maximally portable. * ClassGraph can scan the classpath and module path either at runtime or [at build time](https://github.com/classgraph/classgraph/wiki/Build-Time-Scanning) (e.g. to implement annotation processing for Android). +* ClassGraph parses most of the useful metadata in a classfile, including methods, fields, annotations, outer/inner class containment relationships, and even [type annotations](https://docs.oracle.com/javase/tutorial/java/annotations/type_annotations.html). * ClassGraph can [find classes that are duplicated or defined more than once in the classpath or module path](https://github.com/classgraph/classgraph/wiki/Code-examples#find-all-duplicate-class-definitions-in-the-classpath-or-module-path), which can help find the cause of strange class resolution behaviors. * ClassGraph can [create GraphViz visualizations of the class graph structure](https://github.com/classgraph/classgraph/wiki/ClassInfo-API#generating-a-graphviz-dot-file-for-class-graph-visualization), which can help with code understanding: (click to enlarge | [see graph legend here](https://github.com/classgraph/classgraph/blob/master/src/test/java/com/xyz/classgraph-fig-legend.png)) From 7888d1b849f43f9fc80f30cb5ba2aeb215f3604f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 18 Dec 2020 16:28:23 -0700 Subject: [PATCH 0977/1778] Update README.md --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index fe041a7f8..7dfb8f03f 100644 --- a/README.md +++ b/README.md @@ -74,18 +74,17 @@ See the [code examples](https://github.com/classgraph/classgraph/wiki/Code-examp ClassGraph provides a number of important capabilities to the JVM ecosystem: -* ClassGraph has the ability to build a model in memory of the entire relatedness graph of all classes, annotations, interfaces, methods and fields that are visible to the JVM. This graph can be [queried in a wide range of ways](https://github.com/classgraph/classgraph/wiki/Code-examples), enabling some degree of *metaprogramming* in JVM languages -- the ability to write code that analyzes or responds to the properties of other code. +* ClassGraph has the ability to build a model in memory of the entire relatedness graph of all classes, annotations, interfaces, methods and fields that are visible to the JVM, and can even read [type annotations](https://docs.oracle.com/javase/tutorial/java/annotations/type_annotations.html). This graph of class metadata can be [queried in a wide range of ways](https://github.com/classgraph/classgraph/wiki/Code-examples), enabling some degree of *metaprogramming* in JVM languages -- the ability to write code that analyzes or responds to the properties of other code. * ClassGraph reads the classfile bytecode format directly, so it can read all information about classes without loading or initializing them. * ClassGraph is fully compatible with the new JPMS module system (Project Jigsaw / JDK 9+), i.e. it can scan both the traditional classpath and the module path. However, the code is also fully backwards compatible with JDK 7 and JDK 8 (i.e. the code is compiled in Java 7 compatibility mode, and all interaction with the module system is implemented via reflection for backwards compatibility). * ClassGraph scans the classpath or module path using [carefully optimized multithreaded code](https://github.com/classgraph/classgraph/wiki/How-fast-is-ClassGraph) for the shortest possible scan times, and it runs as close as possible to I/O bandwidth limits, even on a fast SSD. * ClassGraph handles more [classpath specification mechanisms](https://github.com/classgraph/classgraph/wiki/Classpath-specification-mechanisms) found in the wild than any other classpath scanner, making code that depends upon ClassGraph maximally portable. * ClassGraph can scan the classpath and module path either at runtime or [at build time](https://github.com/classgraph/classgraph/wiki/Build-Time-Scanning) (e.g. to implement annotation processing for Android). -* ClassGraph parses most of the useful metadata in a classfile, including methods, fields, annotations, outer/inner class containment relationships, and even [type annotations](https://docs.oracle.com/javase/tutorial/java/annotations/type_annotations.html). * ClassGraph can [find classes that are duplicated or defined more than once in the classpath or module path](https://github.com/classgraph/classgraph/wiki/Code-examples#find-all-duplicate-class-definitions-in-the-classpath-or-module-path), which can help find the cause of strange class resolution behaviors. * ClassGraph can [create GraphViz visualizations of the class graph structure](https://github.com/classgraph/classgraph/wiki/ClassInfo-API#generating-a-graphviz-dot-file-for-class-graph-visualization), which can help with code understanding: (click to enlarge | [see graph legend here](https://github.com/classgraph/classgraph/blob/master/src/test/java/com/xyz/classgraph-fig-legend.png))

- Class graph visualization + Class graph visualization

## Downloading From 4bc50c427741785493b6d5a45e74ea9739944609 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 18 Dec 2020 16:29:06 -0700 Subject: [PATCH 0978/1778] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7dfb8f03f..52220441e 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ ClassGraph provides a number of important capabilities to the JVM ecosystem: * ClassGraph handles more [classpath specification mechanisms](https://github.com/classgraph/classgraph/wiki/Classpath-specification-mechanisms) found in the wild than any other classpath scanner, making code that depends upon ClassGraph maximally portable. * ClassGraph can scan the classpath and module path either at runtime or [at build time](https://github.com/classgraph/classgraph/wiki/Build-Time-Scanning) (e.g. to implement annotation processing for Android). * ClassGraph can [find classes that are duplicated or defined more than once in the classpath or module path](https://github.com/classgraph/classgraph/wiki/Code-examples#find-all-duplicate-class-definitions-in-the-classpath-or-module-path), which can help find the cause of strange class resolution behaviors. -* ClassGraph can [create GraphViz visualizations of the class graph structure](https://github.com/classgraph/classgraph/wiki/ClassInfo-API#generating-a-graphviz-dot-file-for-class-graph-visualization), which can help with code understanding: (click to enlarge | [see graph legend here](https://github.com/classgraph/classgraph/blob/master/src/test/java/com/xyz/classgraph-fig-legend.png)) +* ClassGraph can [create GraphViz visualizations of the class graph structure](https://github.com/classgraph/classgraph/wiki/ClassInfo-API#generating-a-graphviz-dot-file-for-class-graph-visualization), which can help with code understanding: (click to enlarge; [see graph legend here](https://github.com/classgraph/classgraph/blob/master/src/test/java/com/xyz/classgraph-fig-legend.png))

Class graph visualization From 97a5008ae13b90864a8dc803c3523ab6b51ef892 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 18 Dec 2020 16:32:24 -0700 Subject: [PATCH 0979/1778] Update README.md --- README.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 52220441e..8c946fdea 100644 --- a/README.md +++ b/README.md @@ -44,10 +44,10 @@ String pkg = "com.xyz"; String routeAnnotation = pkg + ".Route"; try (ScanResult scanResult = new ClassGraph() - .verbose() // Log to stderr - .enableAllInfo() // Scan classes, methods, fields, annotations - .acceptPackages(pkg) // Scan com.xyz and subpackages (omit to scan all packages) - .scan()) { // Start the scan + .verbose() // Log to stderr + .enableAllInfo() // Scan classes, methods, fields, annotations + .acceptPackages(pkg) // Scan com.xyz and subpackages (omit to scan all packages) + .scan()) { // Start the scan for (ClassInfo routeClassInfo : scanResult.getClassesWithAnnotation(routeAnnotation)) { AnnotationInfo routeAnnotationInfo = routeClassInfo.getAnnotationInfo(routeAnnotation); List routeParamVals = routeAnnotationInfo.getParameterValues(); @@ -62,9 +62,10 @@ The following code finds all JSON files in `META-INF/config` in all ClassLoaders ```java try (ScanResult scanResult = new ClassGraph().acceptPathsNonRecursive("META-INF/config").scan()) { - scanResult.getResourcesWithExtension("json").forEachByteArray((Resource res, byte[] content) -> { - readJson(res.getPath(), new String(content, StandardCharsets.UTF_8)); - }); + scanResult.getResourcesWithExtension("json") + .forEachByteArray((Resource res, byte[] content) -> { + readJson(res.getPath(), new String(content, StandardCharsets.UTF_8)); + }); } ``` From d5fbec17c2bdf2bd5a387f78ebcf04fc47ef4c64 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 18 Dec 2020 16:35:06 -0700 Subject: [PATCH 0980/1778] [maven-release-plugin] prepare release classgraph-4.8.97 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index fdbf694c9..a25d6c647 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.97-SNAPSHOT + 4.8.97 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.96 + classgraph-4.8.97 From 4d4e96a45c26d5b052fcc5d3d966edf4946a287e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 18 Dec 2020 16:35:37 -0700 Subject: [PATCH 0981/1778] [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 a25d6c647..e4b15af14 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.97 + 4.8.98-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 4a97779a6fa84cb880aa03f1b1b77e27c8587d3c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 19 Dec 2020 04:48:32 -0700 Subject: [PATCH 0982/1778] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8c946fdea..f2293fffb 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ClassGraph Logo       Duke Award Logo -ClassGraph is an uber-fast, ultra-lightweight, parallelized classpath scanner and module scanner for Java, Scala, Kotlin and other JVM languages. +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. | |-----------------------------| From 4b155faabb9c35f155a21bf819e1604997f871ce Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 19 Dec 2020 19:03:09 -0700 Subject: [PATCH 0983/1778] Add `getAsStringsWithSimpleNames()` method. --- .../java/io/github/classgraph/InfoList.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/main/java/io/github/classgraph/InfoList.java b/src/main/java/io/github/classgraph/InfoList.java index 9896d527d..83294417b 100644 --- a/src/main/java/io/github/classgraph/InfoList.java +++ b/src/main/java/io/github/classgraph/InfoList.java @@ -121,4 +121,26 @@ public List getAsStrings() { return toStringVals; } } + + /** + * Get the String representations of all items in this list, using only the simple name of any named class, by + * calling {@code ScanResultObject#toStringWithSimpleNames()} if the object is a {@code ScanResultObject} (e.g. + * {@link ClassInfo}, {@link MethodInfo} or {@link FieldInfo} object), otherwise calling {@code toString()}, for + * each item in the list. + * + * @return The String representations of all items in this list, using only the simple name of any named class. + */ + public List getAsStringsWithSimpleNames() { + if (this.isEmpty()) { + return Collections.emptyList(); + } else { + final List toStringVals = new ArrayList<>(this.size()); + for (final T i : this) { + toStringVals.add(i == null ? "null" + : i instanceof ScanResultObject ? ((ScanResultObject) i).toStringWithSimpleNames() + : i.toString()); + } + return toStringVals; + } + } } From dc76e9bb5aef3fdcb597644dc636a55e361fae5f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 19 Dec 2020 19:08:40 -0700 Subject: [PATCH 0984/1778] Update Javadoc --- src/main/java/io/github/classgraph/InfoList.java | 13 ++++++++----- .../java/io/github/classgraph/ScanResultObject.java | 4 +++- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/github/classgraph/InfoList.java b/src/main/java/io/github/classgraph/InfoList.java index 83294417b..444520a2f 100644 --- a/src/main/java/io/github/classgraph/InfoList.java +++ b/src/main/java/io/github/classgraph/InfoList.java @@ -123,12 +123,15 @@ public List getAsStrings() { } /** - * Get the String representations of all items in this list, using only the simple name of any named class, by - * calling {@code ScanResultObject#toStringWithSimpleNames()} if the object is a {@code ScanResultObject} (e.g. - * {@link ClassInfo}, {@link MethodInfo} or {@link FieldInfo} object), otherwise calling {@code toString()}, for - * each item in the list. + * Get the String representations of all items in this list, using only simple + * names of any named classes, by calling {@code ScanResultObject#toStringWithSimpleNames()} if the object + * is a subclass of {@code ScanResultObject} (e.g. {@link ClassInfo}, {@link MethodInfo} or {@link FieldInfo} + * object), otherwise calling {@code toString()}, for each item in the list. * - * @return The String representations of all items in this list, using only the simple name of any named class. + * @return The String representations of all items in this list, using only the + * simple names of any named classes. */ public List getAsStringsWithSimpleNames() { if (this.isEmpty()) { diff --git a/src/main/java/io/github/classgraph/ScanResultObject.java b/src/main/java/io/github/classgraph/ScanResultObject.java index eb0b98e87..8200ed91f 100644 --- a/src/main/java/io/github/classgraph/ScanResultObject.java +++ b/src/main/java/io/github/classgraph/ScanResultObject.java @@ -270,7 +270,9 @@ String toString(final boolean useSimpleNames) { } /** - * Render to string, using only simple names for classes. + * Render to string, using only simple + * names for classes. * * @return the string representation, using simple names for classes. */ From 0000d104c5daf21d5da83f86c13e3f1a5f0dbcdb Mon Sep 17 00:00:00 2001 From: Vladimir Tagakov <10127655+Tagakov@users.noreply.github.com> Date: Tue, 22 Dec 2020 18:00:48 -0800 Subject: [PATCH 0985/1778] Fix NPE in TypeArgument's equals and hashCode Since `typeSignature` in `TypeArgument` could be null it should be honored in equals and hashcode methods --- src/main/java/io/github/classgraph/TypeArgument.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/classgraph/TypeArgument.java b/src/main/java/io/github/classgraph/TypeArgument.java index a2b678426..0cca0411e 100644 --- a/src/main/java/io/github/classgraph/TypeArgument.java +++ b/src/main/java/io/github/classgraph/TypeArgument.java @@ -232,7 +232,7 @@ public void findReferencedClassNames(final Set refdClassNames) { */ @Override public int hashCode() { - return typeSignature.hashCode() + 7 * wildcard.hashCode(); + return (typeSignature != null ? typeSignature.hashCode() : 0) + 7 * wildcard.hashCode(); } /* (non-Javadoc) @@ -247,7 +247,7 @@ public boolean equals(final Object obj) { } final TypeArgument other = (TypeArgument) obj; return Objects.equals(this.typeAnnotationInfo, other.typeAnnotationInfo) - && (other.typeSignature.equals(this.typeSignature) && other.wildcard.equals(this.wildcard)); + && (Objects.equals(this.typeSignature, other.typeSignature) && other.wildcard.equals(this.wildcard)); } // ------------------------------------------------------------------------------------------------------------- From 4684dcb26cbc8a4ccf243d8724df009393a65ddf Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 23 Dec 2020 15:22:31 -0700 Subject: [PATCH 0986/1778] [maven-release-plugin] prepare release classgraph-4.8.98 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e4b15af14..05505c196 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.98-SNAPSHOT + 4.8.98 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.97 + classgraph-4.8.98 From cc41e7f92f8844da4db501367f8b250f48ab5100 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 23 Dec 2020 15:22:38 -0700 Subject: [PATCH 0987/1778] [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 05505c196..b731d9b07 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.98 + 4.8.99-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From c3047688d2dffc5b2c0fc70941f8c0cd064e8dcf Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 23 Dec 2020 15:30:34 -0700 Subject: [PATCH 0988/1778] Source > Cleanup --- src/main/java/io/github/classgraph/TypeArgument.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/TypeArgument.java b/src/main/java/io/github/classgraph/TypeArgument.java index 0cca0411e..02aed28dd 100644 --- a/src/main/java/io/github/classgraph/TypeArgument.java +++ b/src/main/java/io/github/classgraph/TypeArgument.java @@ -247,7 +247,8 @@ public boolean equals(final Object obj) { } final TypeArgument other = (TypeArgument) obj; return Objects.equals(this.typeAnnotationInfo, other.typeAnnotationInfo) - && (Objects.equals(this.typeSignature, other.typeSignature) && other.wildcard.equals(this.wildcard)); + && (Objects.equals(this.typeSignature, other.typeSignature) + && other.wildcard.equals(this.wildcard)); } // ------------------------------------------------------------------------------------------------------------- From 9b5794616057352dbfc296aa2b40492496babb3d Mon Sep 17 00:00:00 2001 From: "Sean C. Sullivan" Date: Wed, 23 Dec 2020 18:24:56 -0800 Subject: [PATCH 0989/1778] ci: add JDK 15 to build matrix --- .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 bdfd73250..6c990001e 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' ] + java: [ '8', '11', '12', '13', '14', '15' ] steps: - uses: actions/checkout@v2 - name: Set up JDK From cdcda980a932423a6c88aa692ccf2274054a8117 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Jan 2021 02:01:44 +0000 Subject: [PATCH 0990/1778] Bump jimfs from 1.1 to 1.2 Bumps [jimfs](https://github.com/google/jimfs) from 1.1 to 1.2. - [Release notes](https://github.com/google/jimfs/releases) - [Commits](https://github.com/google/jimfs/compare/v1.1...v1.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 b731d9b07..5d015459b 100644 --- a/pom.xml +++ b/pom.xml @@ -123,7 +123,7 @@ com.google.jimfs jimfs - 1.1 + 1.2 test From f2db35129d290e43a160a41e098bfaf9cba2965b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 21 Jan 2021 11:14:02 -0700 Subject: [PATCH 0991/1778] Fix for Scala type parameters with `$` in the name (#495) --- src/main/java/io/github/classgraph/TypeParameter.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/TypeParameter.java b/src/main/java/io/github/classgraph/TypeParameter.java index 53c1a5bb8..c51f8420c 100644 --- a/src/main/java/io/github/classgraph/TypeParameter.java +++ b/src/main/java/io/github/classgraph/TypeParameter.java @@ -130,7 +130,8 @@ static List parseList(final Parser parser, final String definingC if (!parser.hasMore()) { throw new ParseException(parser, "Missing '>'"); } - if (!TypeUtils.getIdentifierToken(parser)) { + // Scala sometimes uses '$' in the names of internal/implicit type parameters (#495) + if (!TypeUtils.getIdentifierToken(parser, /* separator = */ '$', /* separatorReplace = */ '$')) { throw new ParseException(parser, "Could not parse identifier token"); } final String identifier = parser.currToken(); From 626eb779b17fb7de580a2668d97163e7bf89aebb Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Thu, 21 Jan 2021 11:36:50 -0700 Subject: [PATCH 0992/1778] Initial working test for #494 --- .../github/classgraph/issues/issue494/A.java | 40 ++++++++++++++ .../issues/issue494/Issue494Test.java | 54 +++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 src/test/java/io/github/classgraph/issues/issue494/A.java create mode 100644 src/test/java/io/github/classgraph/issues/issue494/Issue494Test.java diff --git a/src/test/java/io/github/classgraph/issues/issue494/A.java b/src/test/java/io/github/classgraph/issues/issue494/A.java new file mode 100644 index 000000000..67ec22bdd --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue494/A.java @@ -0,0 +1,40 @@ +/* + * 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.issues.issue494; + +/** + * The Class A. + */ +public class A { + /** + * The Class B. + */ + public static class B { + } +} diff --git a/src/test/java/io/github/classgraph/issues/issue494/Issue494Test.java b/src/test/java/io/github/classgraph/issues/issue494/Issue494Test.java new file mode 100644 index 000000000..ab6de71bc --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue494/Issue494Test.java @@ -0,0 +1,54 @@ +/* + * 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.issues.issue494; + +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; + +/** + * Test. + */ +public class Issue494Test { + /** Test for bug #494 */ + @Test + public void testStaticInnerClass() { + final String pkgName = Issue494Test.class.getPackage().getName(); + try (ScanResult scanResult = new ClassGraph().enableClassInfo().acceptPackages(pkgName).scan()) { + final ClassInfo bClassInfo = scanResult.getClassInfo(pkgName + ".A$B"); + assertThat(bClassInfo).isNotNull(); + final Class bClass = bClassInfo.loadClass(); + assertThat(bClass).isNotNull(); + } + } +} From 163c4cd526c20f43495c5e669172277a512901ec Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 22 Jan 2021 03:02:47 -0700 Subject: [PATCH 0993/1778] Also allow `$` in type variable names (#495) --- src/main/java/io/github/classgraph/TypeParameter.java | 2 +- src/main/java/io/github/classgraph/TypeVariableSignature.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/classgraph/TypeParameter.java b/src/main/java/io/github/classgraph/TypeParameter.java index c51f8420c..4324860bb 100644 --- a/src/main/java/io/github/classgraph/TypeParameter.java +++ b/src/main/java/io/github/classgraph/TypeParameter.java @@ -130,7 +130,7 @@ static List parseList(final Parser parser, final String definingC if (!parser.hasMore()) { throw new ParseException(parser, "Missing '>'"); } - // Scala sometimes uses '$' in the names of internal/implicit type parameters (#495) + // Scala can contain '$' in type parameter names (#495) if (!TypeUtils.getIdentifierToken(parser, /* separator = */ '$', /* separatorReplace = */ '$')) { throw new ParseException(parser, "Could not parse identifier token"); } diff --git a/src/main/java/io/github/classgraph/TypeVariableSignature.java b/src/main/java/io/github/classgraph/TypeVariableSignature.java index 995481c5b..81e431ba7 100644 --- a/src/main/java/io/github/classgraph/TypeVariableSignature.java +++ b/src/main/java/io/github/classgraph/TypeVariableSignature.java @@ -142,7 +142,8 @@ static TypeVariableSignature parse(final Parser parser, final String definingCla final char peek = parser.peek(); if (peek == 'T') { parser.next(); - if (!TypeUtils.getIdentifierToken(parser)) { + // Scala can contain '$' in type variable names (#495) + if (!TypeUtils.getIdentifierToken(parser, /* separator = */ '$', /* separatorReplace = */ '$')) { throw new ParseException(parser, "Could not parse type variable signature"); } parser.expect(';'); From 7f2e5eedfea4f290bc8ce9dc0203276a6955f2e6 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 22 Jan 2021 03:11:58 -0700 Subject: [PATCH 0994/1778] Make type annotation parsing more robust --- .../java/io/github/classgraph/ClassRefTypeSignature.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassRefTypeSignature.java b/src/main/java/io/github/classgraph/ClassRefTypeSignature.java index cbfb8a3e3..81a37e293 100644 --- a/src/main/java/io/github/classgraph/ClassRefTypeSignature.java +++ b/src/main/java/io/github/classgraph/ClassRefTypeSignature.java @@ -228,8 +228,13 @@ protected void addTypeAnnotation(final List typePath, final Annota } else { final List typeArgumentList = suffixIdx == -1 ? typeArguments : suffixTypeArguments.get(suffixIdx); - typeArgumentList.get(nextTypeArgIdx).addTypeAnnotation( - typePath.subList(numDeeperNestedLevels + 1, typePath.size()), annotationInfo); + // For type descriptors (as opposed to type signatures), typeArguments is the empty list, + // so need to bounds-check nextTypeArgIdx + if (nextTypeArgIdx < typeArgumentList.size()) { + // Add type annotation to type argument + typeArgumentList.get(nextTypeArgIdx).addTypeAnnotation( + typePath.subList(numDeeperNestedLevels + 1, typePath.size()), annotationInfo); + } } } From 5d17ffd36d14d770beb26b30889471d674d2a668 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 22 Jan 2021 03:20:58 -0700 Subject: [PATCH 0995/1778] Simplify and fix parsing of Java identifiers in type signatures (#495) --- .../classgraph/ClassRefTypeSignature.java | 5 ++- .../io/github/classgraph/TypeParameter.java | 2 +- .../classgraph/TypeVariableSignature.java | 2 +- .../io/github/classgraph/types/TypeUtils.java | 31 +++---------------- 4 files changed, 9 insertions(+), 31 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassRefTypeSignature.java b/src/main/java/io/github/classgraph/ClassRefTypeSignature.java index 81a37e293..5c17d9c9d 100644 --- a/src/main/java/io/github/classgraph/ClassRefTypeSignature.java +++ b/src/main/java/io/github/classgraph/ClassRefTypeSignature.java @@ -462,7 +462,7 @@ protected void toStringInternal(final boolean useSimpleNames, final AnnotationIn static ClassRefTypeSignature parse(final Parser parser, final String definingClassName) throws ParseException { if (parser.peek() == 'L') { parser.next(); - if (!TypeUtils.getIdentifierToken(parser, /* separator = */ '/', /* separatorReplace = */ '.')) { + if (!TypeUtils.getIdentifierToken(parser)) { throw new ParseException(parser, "Could not parse identifier token"); } final String className = parser.currToken(); @@ -474,8 +474,7 @@ static ClassRefTypeSignature parse(final Parser parser, final String definingCla suffixTypeArguments = new ArrayList<>(); while (parser.peek() == '.' || parser.peek() == '$') { parser.advance(1); - if (!TypeUtils.getIdentifierToken(parser, /* separator = */ '/', - /* separatorReplace = */ '.')) { + if (!TypeUtils.getIdentifierToken(parser)) { throw new ParseException(parser, "Could not parse identifier token"); } suffixes.add(parser.currToken()); diff --git a/src/main/java/io/github/classgraph/TypeParameter.java b/src/main/java/io/github/classgraph/TypeParameter.java index 4324860bb..be4d9bf05 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, /* separator = */ '$', /* separatorReplace = */ '$')) { + if (!TypeUtils.getIdentifierToken(parser)) { 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 81e431ba7..71d327d5f 100644 --- a/src/main/java/io/github/classgraph/TypeVariableSignature.java +++ b/src/main/java/io/github/classgraph/TypeVariableSignature.java @@ -143,7 +143,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, /* separator = */ '$', /* separatorReplace = */ '$')) { + if (!TypeUtils.getIdentifierToken(parser)) { 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 6ac79593c..fe4de81f3 100644 --- a/src/main/java/nonapi/io/github/classgraph/types/TypeUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/types/TypeUtils.java @@ -45,28 +45,21 @@ private TypeUtils() { } /** - * Parse a Java identifier with the given separator ('.' or '/'). Potentially replaces the separator with a - * different character. Appends the identifier to the token buffer in the parser. + * Parse a Java identifier, replacing '/' with '.'. Appends the identifier to the token buffer in the parser. * * @param parser * The parser. - * @param separator - * The separator character. - * @param separatorReplace - * The character to replace the separator with. * @return true if at least one identifier character was parsed. */ - public static boolean getIdentifierToken(final Parser parser, final char separator, - final char separatorReplace) { + public static boolean getIdentifierToken(final Parser parser) { boolean consumedChar = false; while (parser.hasMore()) { final char c = parser.peek(); - if (c == separator) { - parser.appendToToken(separatorReplace); + if (c == '/') { + parser.appendToToken('.'); parser.next(); consumedChar = true; - } else if (c != ';' && c != '[' && c != '<' && c != '>' && c != ':' && c != '/' && c != '.' - && c != '$') { + } else if (c != ';' && c != '[' && c != '<' && c != '>' && c != ':') { parser.appendToToken(c); parser.next(); consumedChar = true; @@ -77,20 +70,6 @@ public static boolean getIdentifierToken(final Parser parser, final char separat return consumedChar; } - /** - * Parse a Java identifier part (between separators and other non-alphanumeric characters). Appends the - * identifier to the token buffer in the parser. - * - * @param parser - * The parser. - * @return true if at least one identifier character was parsed. - * @throws ParseException - * If the parser ran out of input. - */ - public static boolean getIdentifierToken(final Parser parser) throws ParseException { - return getIdentifierToken(parser, '\0', '\0'); - } - /** The origin of the modifier bits. */ public enum ModifierType { /** The modifier bits apply to a class. */ From d95050e2cc8ab08ec66dc4803dcf27c565ba4d32 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 22 Jan 2021 03:21:27 -0700 Subject: [PATCH 0996/1778] Add missing @Override --- src/main/java/io/github/classgraph/ArrayTypeSignature.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/io/github/classgraph/ArrayTypeSignature.java b/src/main/java/io/github/classgraph/ArrayTypeSignature.java index 9266f561d..1774904f1 100644 --- a/src/main/java/io/github/classgraph/ArrayTypeSignature.java +++ b/src/main/java/io/github/classgraph/ArrayTypeSignature.java @@ -150,6 +150,7 @@ protected void addTypeAnnotation(final List typePath, final Annota * @return a list of {@link AnnotationInfo} objects for the type annotations of on this array type, or null if * none. */ + @Override public AnnotationInfoList getTypeAnnotationInfo() { return typeAnnotationInfo; } From f3d888092df322e115b694f85f54e9059f61fdfb Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 22 Jan 2021 03:38:12 -0700 Subject: [PATCH 0997/1778] Re-enable `$` as a suffix separator in ClassRefTypeSignature (#495) --- .../java/io/github/classgraph/ClassRefTypeSignature.java | 4 ++-- src/main/java/io/github/classgraph/TypeParameter.java | 2 +- .../java/io/github/classgraph/TypeVariableSignature.java | 2 +- .../java/nonapi/io/github/classgraph/types/TypeUtils.java | 7 +++++-- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassRefTypeSignature.java b/src/main/java/io/github/classgraph/ClassRefTypeSignature.java index 5c17d9c9d..008a3ebc8 100644 --- a/src/main/java/io/github/classgraph/ClassRefTypeSignature.java +++ b/src/main/java/io/github/classgraph/ClassRefTypeSignature.java @@ -462,7 +462,7 @@ protected void toStringInternal(final boolean useSimpleNames, final AnnotationIn static ClassRefTypeSignature parse(final Parser parser, final String definingClassName) throws ParseException { if (parser.peek() == 'L') { parser.next(); - if (!TypeUtils.getIdentifierToken(parser)) { + if (!TypeUtils.getIdentifierToken(parser, /* stopAtDollarSign = */ true)) { throw new ParseException(parser, "Could not parse identifier token"); } final String className = parser.currToken(); @@ -474,7 +474,7 @@ static ClassRefTypeSignature parse(final Parser parser, final String definingCla suffixTypeArguments = new ArrayList<>(); while (parser.peek() == '.' || parser.peek() == '$') { parser.advance(1); - if (!TypeUtils.getIdentifierToken(parser)) { + if (!TypeUtils.getIdentifierToken(parser, /* stopAtDollarSign = */ true)) { throw new ParseException(parser, "Could not parse identifier token"); } suffixes.add(parser.currToken()); diff --git a/src/main/java/io/github/classgraph/TypeParameter.java b/src/main/java/io/github/classgraph/TypeParameter.java index be4d9bf05..095bc5559 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)) { + if (!TypeUtils.getIdentifierToken(parser, /* stopAtDollarSign = */ false)) { 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 71d327d5f..0aaa290ce 100644 --- a/src/main/java/io/github/classgraph/TypeVariableSignature.java +++ b/src/main/java/io/github/classgraph/TypeVariableSignature.java @@ -143,7 +143,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)) { + if (!TypeUtils.getIdentifierToken(parser, /* stopAtDollarSign = */ false)) { 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 fe4de81f3..4e6d727a5 100644 --- a/src/main/java/nonapi/io/github/classgraph/types/TypeUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/types/TypeUtils.java @@ -49,9 +49,11 @@ private TypeUtils() { * * @param parser * The parser. + * @param stopAtDollarSign + * 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) { + public static boolean getIdentifierToken(final Parser parser, boolean stopAtDollarSign) { boolean consumedChar = false; while (parser.hasMore()) { final char c = parser.peek(); @@ -59,7 +61,8 @@ public static boolean getIdentifierToken(final Parser parser) { parser.appendToToken('.'); parser.next(); consumedChar = true; - } else if (c != ';' && c != '[' && c != '<' && c != '>' && c != ':') { + } else if (c != ';' && c != '[' && c != '<' && c != '>' && c != ':' + && (!stopAtDollarSign || c != '$')) { parser.appendToToken(c); parser.next(); consumedChar = true; From 8c46d9df316b0296cb029008e86009ddb78c5bf0 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 22 Jan 2021 03:42:16 -0700 Subject: [PATCH 0998/1778] Make test more robust --- .../java/io/github/classgraph/issues/issue348/Issue348.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/test/java/io/github/classgraph/issues/issue348/Issue348.java b/src/test/java/io/github/classgraph/issues/issue348/Issue348.java index 3083885ef..8f7dfd850 100644 --- a/src/test/java/io/github/classgraph/issues/issue348/Issue348.java +++ b/src/test/java/io/github/classgraph/issues/issue348/Issue348.java @@ -23,14 +23,16 @@ public void testWildcard() { try (ScanResult scanResult1 = new ClassGraph().acceptPathsNonRecursive("").scan()) { // Find all resources within classpath elements with ".jar" extension final List jarResourceUris = scanResult1.getResourcesWithExtension("jar").stream() - .map(r -> r.getURI().toString()).collect(Collectors.toList()); + .map(r -> r.getURI().toString().replace(":///", ":/").replace("://", ":/")) + .collect(Collectors.toList()); assertThat(jarResourceUris).isNotEmpty(); try (ScanResult scanResult2 = new ClassGraph().overrideClasspath(jarResourceUris) .acceptJars("issue*.jar").scan()) { // Find all classpath element URIs for non-nested jars final List cpUris = scanResult2.getClasspathURIs().stream().map(URI::toString) - .filter(u -> !u.contains("!")).collect(Collectors.toList()); + .filter(u -> !u.contains("!")).map(u -> u.replace(":///", ":/").replace("://", ":/")) + .collect(Collectors.toList()); assertThat(cpUris).isNotEmpty(); // Check that cpUris is a non-empty subset of jarResourceUris From f1c94f4c7110ba09dbfc55d09498d50c144b920f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 22 Jan 2021 03:43:26 -0700 Subject: [PATCH 0999/1778] [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 5d015459b..eab94ea31 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.99-SNAPSHOT + 4.8.100-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 668a9148f3f6074770a4799b4b1165d2024c2657 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 23 Jan 2021 07:27:39 -0700 Subject: [PATCH 1000/1778] Add inner class 'b' (#494) --- src/test/java/io/github/classgraph/issues/issue494/A.java | 6 ++++++ .../io/github/classgraph/issues/issue494/Issue494Test.java | 7 ++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/test/java/io/github/classgraph/issues/issue494/A.java b/src/test/java/io/github/classgraph/issues/issue494/A.java index 67ec22bdd..98e296cdf 100644 --- a/src/test/java/io/github/classgraph/issues/issue494/A.java +++ b/src/test/java/io/github/classgraph/issues/issue494/A.java @@ -37,4 +37,10 @@ public class A { */ public static class B { } + + /** + * The Class b. + */ + public static class b { + } } diff --git a/src/test/java/io/github/classgraph/issues/issue494/Issue494Test.java b/src/test/java/io/github/classgraph/issues/issue494/Issue494Test.java index ab6de71bc..32f80d23c 100644 --- a/src/test/java/io/github/classgraph/issues/issue494/Issue494Test.java +++ b/src/test/java/io/github/classgraph/issues/issue494/Issue494Test.java @@ -45,7 +45,12 @@ public class Issue494Test { public void testStaticInnerClass() { final String pkgName = Issue494Test.class.getPackage().getName(); try (ScanResult scanResult = new ClassGraph().enableClassInfo().acceptPackages(pkgName).scan()) { - final ClassInfo bClassInfo = scanResult.getClassInfo(pkgName + ".A$B"); + final ClassInfo BClassInfo = scanResult.getClassInfo(pkgName + ".A$B"); + assertThat(BClassInfo).isNotNull(); + final Class BClass = BClassInfo.loadClass(); + assertThat(BClass).isNotNull(); + + final ClassInfo bClassInfo = scanResult.getClassInfo(pkgName + ".A$b"); assertThat(bClassInfo).isNotNull(); final Class bClass = bClassInfo.loadClass(); assertThat(bClass).isNotNull(); From 72ca47b8dc2e3c39f828ecc0d3bb65f34d4d3a1e Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 23 Jan 2021 08:41:00 -0700 Subject: [PATCH 1001/1778] Give warning when can't load class due to case insensitivity (#494) --- .../classgraph/ClassGraphClassLoader.java | 67 +++++++++++++++++-- 1 file changed, 60 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java index ee2a4fc6f..c04dfd98f 100644 --- a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java +++ b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java @@ -42,6 +42,8 @@ import nonapi.io.github.classgraph.scanspec.ScanSpec; import nonapi.io.github.classgraph.utils.JarUtils; +import nonapi.io.github.classgraph.utils.VersionFinder; +import nonapi.io.github.classgraph.utils.VersionFinder.OperatingSystem; /** {@link ClassLoader} for classes found by ClassGraph during scanning. */ public class ClassGraphClassLoader extends ClassLoader { @@ -140,11 +142,14 @@ protected Class findClass(final String className) // First delegate to outer nested ClassGraphClassLoader, if any (#485) final ClassGraphClassLoader delegateClassGraphClassLoader = scanResult.classpathFinder .getDelegateClassGraphClassLoader(); + LinkageError linkageError = null; if (delegateClassGraphClassLoader != null) { try { return Class.forName(className, initializeLoadedClasses, delegateClassGraphClassLoader); - } catch (ClassNotFoundException | LinkageError e) { + } catch (ClassNotFoundException e) { // Ignore + } catch (LinkageError e) { + linkageError = e; } } @@ -153,8 +158,12 @@ protected Class findClass(final String className) for (final ClassLoader overrideClassLoader : overrideClassLoaders) { try { return Class.forName(className, initializeLoadedClasses, overrideClassLoader); - } catch (ClassNotFoundException | LinkageError e) { + } catch (ClassNotFoundException e) { // Ignore + } catch (LinkageError e) { + if (linkageError == null) { + linkageError = e; + } } } } @@ -165,8 +174,12 @@ protected Class findClass(final String className) for (final ClassLoader envClassLoader : environmentClassLoaderDelegationOrder) { try { return Class.forName(className, initializeLoadedClasses, envClassLoader); - } catch (ClassNotFoundException | LinkageError e) { + } catch (ClassNotFoundException e) { // Ignore + } catch (LinkageError e) { + if (linkageError == null) { + linkageError = e; + } } } } @@ -186,8 +199,12 @@ protected Class findClass(final String className) || !environmentClassLoaderDelegationOrder.contains(classInfoClassLoader))) { try { return Class.forName(className, initializeLoadedClasses, classInfoClassLoader); - } catch (ClassNotFoundException | LinkageError e) { + } catch (ClassNotFoundException e) { // Ignore + } catch (LinkageError e) { + if (linkageError == null) { + linkageError = e; + } } } @@ -207,8 +224,12 @@ protected Class findClass(final String className) if (overrideClassLoaders == null && classpathClassLoader != null) { try { return Class.forName(className, initializeLoadedClasses, classpathClassLoader); - } catch (ClassNotFoundException | LinkageError e) { + } catch (ClassNotFoundException e) { // Ignore + } catch (LinkageError e) { + if (linkageError == null) { + linkageError = e; + } } } @@ -218,8 +239,12 @@ protected Class findClass(final String className) if (additionalClassLoader != classInfoClassLoader) { try { return Class.forName(className, initializeLoadedClasses, additionalClassLoader); - } catch (ClassNotFoundException | LinkageError e) { + } catch (ClassNotFoundException e) { // Ignore + } catch (LinkageError e) { + if (linkageError == null) { + linkageError = e; + } } } } @@ -252,12 +277,40 @@ protected Class findClass(final String className) } } catch (final IOException e) { throw new ClassNotFoundException("Could not load classfile for class " + className + " : " + e); + } catch (LinkageError e) { + if (linkageError == null) { + linkageError = e; + } } finally { resource.close(); } } } - throw new ClassNotFoundException("Could not load classfile for class " + className); + + if (linkageError != null) { + if (VersionFinder.OS == OperatingSystem.Windows) { + // LinkageError indicates that a classfile was found, but the class couldn't be loaded. + // Hackily detect the situation where there are two classfiles with the same case insensitive name + // on Windows filesystems (#494). + final String msg = linkageError.getMessage(); + if (msg != null) { + final String wrongName = "(wrong name: "; + final int wrongNameIdx = msg.indexOf(wrongName); + if (wrongNameIdx > -1) { + final String theWrongName = msg.substring(wrongNameIdx + wrongName.length(), + msg.length() - 1); + if (theWrongName.replace('/', '.').equalsIgnoreCase(className)) { + throw new LinkageError("You appear to have two classfiles with the same " + + "case-insensitive name in the same directory on a case-insensitive " + + "filesystem, for class: " + className, linkageError); + } + } + } + } + throw linkageError; + } + + throw new ClassNotFoundException("Could not find or load classfile for class " + className); } /** Get classpath URLs. */ From df371dab1a160177af2b393eb8e8689c25e1ff85 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 23 Jan 2021 08:41:57 -0700 Subject: [PATCH 1002/1778] Update error message (#494) --- src/main/java/io/github/classgraph/ClassGraphClassLoader.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java index c04dfd98f..3cb303c79 100644 --- a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java +++ b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java @@ -302,7 +302,8 @@ protected Class findClass(final String className) if (theWrongName.replace('/', '.').equalsIgnoreCase(className)) { throw new LinkageError("You appear to have two classfiles with the same " + "case-insensitive name in the same directory on a case-insensitive " - + "filesystem, for class: " + className, linkageError); + + "filesystem, for class: " + className + " -- this is not allowed " + + "on Windows, and therefore your code is not portable", linkageError); } } } From 46d68c9c66510f0ee4d0c338c10e999f8afb1853 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 23 Jan 2021 08:44:46 -0700 Subject: [PATCH 1003/1778] Update error message --- src/main/java/io/github/classgraph/ClassGraphClassLoader.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java index 3cb303c79..c2a5463b4 100644 --- a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java +++ b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java @@ -302,8 +302,8 @@ protected Class findClass(final String className) if (theWrongName.replace('/', '.').equalsIgnoreCase(className)) { throw new LinkageError("You appear to have two classfiles with the same " + "case-insensitive name in the same directory on a case-insensitive " - + "filesystem, for class: " + className + " -- this is not allowed " - + "on Windows, and therefore your code is not portable", linkageError); + + "filesystem -- this is not allowed on Windows, and therefore your " + + "code is not portable. Class name: " + className, linkageError); } } } From 3e91c950f3c33531c7cf6901cc3f0802ce17150d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Sat, 23 Jan 2021 08:45:57 -0700 Subject: [PATCH 1004/1778] Delete test since it won't even build on Windows (#494) --- .../github/classgraph/issues/issue494/A.java | 46 --------------- .../issues/issue494/Issue494Test.java | 59 ------------------- 2 files changed, 105 deletions(-) delete mode 100644 src/test/java/io/github/classgraph/issues/issue494/A.java delete mode 100644 src/test/java/io/github/classgraph/issues/issue494/Issue494Test.java diff --git a/src/test/java/io/github/classgraph/issues/issue494/A.java b/src/test/java/io/github/classgraph/issues/issue494/A.java deleted file mode 100644 index 98e296cdf..000000000 --- a/src/test/java/io/github/classgraph/issues/issue494/A.java +++ /dev/null @@ -1,46 +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.issues.issue494; - -/** - * The Class A. - */ -public class A { - /** - * The Class B. - */ - public static class B { - } - - /** - * The Class b. - */ - public static class b { - } -} diff --git a/src/test/java/io/github/classgraph/issues/issue494/Issue494Test.java b/src/test/java/io/github/classgraph/issues/issue494/Issue494Test.java deleted file mode 100644 index 32f80d23c..000000000 --- a/src/test/java/io/github/classgraph/issues/issue494/Issue494Test.java +++ /dev/null @@ -1,59 +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.issues.issue494; - -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; - -/** - * Test. - */ -public class Issue494Test { - /** Test for bug #494 */ - @Test - public void testStaticInnerClass() { - final String pkgName = Issue494Test.class.getPackage().getName(); - try (ScanResult scanResult = new ClassGraph().enableClassInfo().acceptPackages(pkgName).scan()) { - final ClassInfo BClassInfo = scanResult.getClassInfo(pkgName + ".A$B"); - assertThat(BClassInfo).isNotNull(); - final Class BClass = BClassInfo.loadClass(); - assertThat(BClass).isNotNull(); - - final ClassInfo bClassInfo = scanResult.getClassInfo(pkgName + ".A$b"); - assertThat(bClassInfo).isNotNull(); - final Class bClass = bClassInfo.loadClass(); - assertThat(bClass).isNotNull(); - } - } -} From a6727032807c57ace7faee08c05b8f1098e75388 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Jan 2021 02:00:52 +0000 Subject: [PATCH 1005/1778] Bump assertj-core from 3.18.1 to 3.19.0 Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.18.1 to 3.19.0. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.18.1...assertj-core-3.19.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 eab94ea31..5ee93526f 100644 --- a/pom.xml +++ b/pom.xml @@ -87,7 +87,7 @@ org.assertj assertj-core - 3.18.1 + 3.19.0 test From 901fbd054996f02182f93d55ade8af2ae31d8f41 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 25 Jan 2021 03:29:46 -0700 Subject: [PATCH 1006/1778] More fixes for Scala's use of '$' in class names (#495) --- .../classgraph/ClassGraphClassLoader.java | 26 +++++++++---------- .../classgraph/ClassRefTypeSignature.java | 21 ++++++++++++--- .../io/github/classgraph/types/TypeUtils.java | 2 +- 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java index c2a5463b4..73a92678d 100644 --- a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java +++ b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java @@ -146,9 +146,9 @@ protected Class findClass(final String className) if (delegateClassGraphClassLoader != null) { try { return Class.forName(className, initializeLoadedClasses, delegateClassGraphClassLoader); - } catch (ClassNotFoundException e) { + } catch (final ClassNotFoundException e) { // Ignore - } catch (LinkageError e) { + } catch (final LinkageError e) { linkageError = e; } } @@ -158,9 +158,9 @@ protected Class findClass(final String className) for (final ClassLoader overrideClassLoader : overrideClassLoaders) { try { return Class.forName(className, initializeLoadedClasses, overrideClassLoader); - } catch (ClassNotFoundException e) { + } catch (final ClassNotFoundException e) { // Ignore - } catch (LinkageError e) { + } catch (final LinkageError e) { if (linkageError == null) { linkageError = e; } @@ -174,9 +174,9 @@ protected Class findClass(final String className) for (final ClassLoader envClassLoader : environmentClassLoaderDelegationOrder) { try { return Class.forName(className, initializeLoadedClasses, envClassLoader); - } catch (ClassNotFoundException e) { + } catch (final ClassNotFoundException e) { // Ignore - } catch (LinkageError e) { + } catch (final LinkageError e) { if (linkageError == null) { linkageError = e; } @@ -199,9 +199,9 @@ protected Class findClass(final String className) || !environmentClassLoaderDelegationOrder.contains(classInfoClassLoader))) { try { return Class.forName(className, initializeLoadedClasses, classInfoClassLoader); - } catch (ClassNotFoundException e) { + } catch (final ClassNotFoundException e) { // Ignore - } catch (LinkageError e) { + } catch (final LinkageError e) { if (linkageError == null) { linkageError = e; } @@ -224,9 +224,9 @@ protected Class findClass(final String className) if (overrideClassLoaders == null && classpathClassLoader != null) { try { return Class.forName(className, initializeLoadedClasses, classpathClassLoader); - } catch (ClassNotFoundException e) { + } catch (final ClassNotFoundException e) { // Ignore - } catch (LinkageError e) { + } catch (final LinkageError e) { if (linkageError == null) { linkageError = e; } @@ -239,9 +239,9 @@ protected Class findClass(final String className) if (additionalClassLoader != classInfoClassLoader) { try { return Class.forName(className, initializeLoadedClasses, additionalClassLoader); - } catch (ClassNotFoundException e) { + } catch (final ClassNotFoundException e) { // Ignore - } catch (LinkageError e) { + } catch (final LinkageError e) { if (linkageError == null) { linkageError = e; } @@ -277,7 +277,7 @@ protected Class findClass(final String className) } } catch (final IOException e) { throw new ClassNotFoundException("Could not load classfile for class " + className + " : " + e); - } catch (LinkageError e) { + } catch (final LinkageError e) { if (linkageError == null) { linkageError = e; } diff --git a/src/main/java/io/github/classgraph/ClassRefTypeSignature.java b/src/main/java/io/github/classgraph/ClassRefTypeSignature.java index 008a3ebc8..b2b4792ea 100644 --- a/src/main/java/io/github/classgraph/ClassRefTypeSignature.java +++ b/src/main/java/io/github/classgraph/ClassRefTypeSignature.java @@ -462,23 +462,36 @@ protected void toStringInternal(final boolean useSimpleNames, final AnnotationIn static ClassRefTypeSignature parse(final Parser parser, final String definingClassName) throws ParseException { if (parser.peek() == 'L') { parser.next(); + final int startParserPosition = parser.getPosition(); if (!TypeUtils.getIdentifierToken(parser, /* stopAtDollarSign = */ true)) { throw new ParseException(parser, "Could not parse identifier token"); } - final String className = parser.currToken(); + String className = parser.currToken(); final List typeArguments = TypeArgument.parseList(parser, definingClassName); List suffixes; List> suffixTypeArguments; + boolean dropSuffixes = false; if (parser.peek() == '.' || parser.peek() == '$') { suffixes = new ArrayList<>(); suffixTypeArguments = new ArrayList<>(); while (parser.peek() == '.' || parser.peek() == '$') { parser.advance(1); if (!TypeUtils.getIdentifierToken(parser, /* stopAtDollarSign = */ true)) { - throw new ParseException(parser, "Could not parse identifier token"); + // Got the empty string as the next token after '$', i.e. found an empty suffix. + suffixes.add(""); + suffixTypeArguments.add(Collections.emptyList()); + dropSuffixes = true; + } else { + suffixes.add(parser.currToken()); + suffixTypeArguments.add(TypeArgument.parseList(parser, definingClassName)); } - suffixes.add(parser.currToken()); - suffixTypeArguments.add(TypeArgument.parseList(parser, definingClassName)); + } + if (dropSuffixes) { + // Got an empty suffix -- either "$$", or a class name ending in a '$' (which Scala uses). + // In this case, take the whole class reference as a single class name without suffixes. + className = parser.getSubstring(startParserPosition, parser.getPosition()).replace('/', '.'); + suffixes = Collections.emptyList(); + suffixTypeArguments = Collections.emptyList(); } } else { suffixes = Collections.emptyList(); 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 4e6d727a5..afb56c92f 100644 --- a/src/main/java/nonapi/io/github/classgraph/types/TypeUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/types/TypeUtils.java @@ -53,7 +53,7 @@ private TypeUtils() { * 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, boolean stopAtDollarSign) { + public static boolean getIdentifierToken(final Parser parser, final boolean stopAtDollarSign) { boolean consumedChar = false; while (parser.hasMore()) { final char c = parser.peek(); From 5897f9a99fa4552d513c6f4c02a82bbfdc4a3d00 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 25 Jan 2021 03:33:18 -0700 Subject: [PATCH 1007/1778] Refactoring --- .../java/io/github/classgraph/Classfile.java | 8 +- .../io/github/classgraph/ModulePathInfo.java | 6 +- .../fastzipfilereader/LogicalZipFile.java | 5 +- .../io/github/classgraph/utils/Join.java | 111 ------------------ .../github/classgraph/utils/StringUtils.java | 71 +++++++++++ 5 files changed, 81 insertions(+), 120 deletions(-) delete mode 100644 src/main/java/nonapi/io/github/classgraph/utils/Join.java diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index 5ad3ecc36..179037b76 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -47,8 +47,8 @@ import nonapi.io.github.classgraph.types.ParseException; import nonapi.io.github.classgraph.utils.CollectionUtils; import nonapi.io.github.classgraph.utils.JarUtils; -import nonapi.io.github.classgraph.utils.Join; import nonapi.io.github.classgraph.utils.LogNode; +import nonapi.io.github.classgraph.utils.StringUtils; /** * A classfile binary format parser. Implements its own buffering to avoid the overhead of using DataInputStream. @@ -1978,10 +1978,10 @@ public void decorate(final ClassTypeSignature classTypeSignature) { "Super" + (isInterface && !isAnnotation ? "interface" : "class") + ": " + superclassName); } if (implementedInterfaces != null) { - subLog.log("Interfaces: " + Join.join(", ", implementedInterfaces)); + subLog.log("Interfaces: " + StringUtils.join(", ", implementedInterfaces)); } if (classAnnotations != null) { - subLog.log("Class annotations: " + Join.join(", ", classAnnotations.getNames())); + subLog.log("Class annotations: " + StringUtils.join(", ", classAnnotations.getNames())); } if (annotationParamDefaultValues != null) { for (final AnnotationParameterValue apv : annotationParamDefaultValues) { @@ -2004,7 +2004,7 @@ public void decorate(final ClassTypeSignature classTypeSignature) { if (refdClassNames != null) { final List refdClassNamesSorted = new ArrayList<>(refdClassNames); CollectionUtils.sortIfNotEmpty(refdClassNamesSorted); - subLog.log("Referenced class names: " + Join.join(", ", refdClassNamesSorted)); + subLog.log("Referenced class names: " + StringUtils.join(", ", refdClassNamesSorted)); } } diff --git a/src/main/java/io/github/classgraph/ModulePathInfo.java b/src/main/java/io/github/classgraph/ModulePathInfo.java index 101dbc3f0..2cf1c6c2f 100644 --- a/src/main/java/io/github/classgraph/ModulePathInfo.java +++ b/src/main/java/io/github/classgraph/ModulePathInfo.java @@ -35,8 +35,8 @@ import java.util.Set; import nonapi.io.github.classgraph.utils.JarUtils; -import nonapi.io.github.classgraph.utils.Join; import nonapi.io.github.classgraph.utils.ReflectionUtils; +import nonapi.io.github.classgraph.utils.StringUtils; /** * Information on the module path. Note that this will only include module system parameters actually listed in @@ -175,14 +175,14 @@ public String toString() { final StringBuilder buf = new StringBuilder(1024); if (!modulePath.isEmpty()) { buf.append("--module-path="); - buf.append(Join.join(File.pathSeparator, modulePath)); + buf.append(StringUtils.join(File.pathSeparator, modulePath)); } if (!addModules.isEmpty()) { if (buf.length() > 0) { buf.append(' '); } buf.append("--add-modules="); - buf.append(Join.join(",", addModules)); + buf.append(StringUtils.join(",", addModules)); } for (final String patchModulesEntry : patchModules) { if (buf.length() > 0) { 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 304faf8b5..70cc7aa6e 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java @@ -49,8 +49,8 @@ import nonapi.io.github.classgraph.fileslice.reader.RandomAccessReader; import nonapi.io.github.classgraph.utils.CollectionUtils; import nonapi.io.github.classgraph.utils.FileUtils; -import nonapi.io.github.classgraph.utils.Join; import nonapi.io.github.classgraph.utils.LogNode; +import nonapi.io.github.classgraph.utils.StringUtils; import nonapi.io.github.classgraph.utils.VersionFinder; /** @@ -803,7 +803,8 @@ private void readCentralDirectory(final NestedJarHandler nestedJarHandler, final } final List versionsFoundSorted = new ArrayList<>(versionsFound); CollectionUtils.sortIfNotEmpty(versionsFoundSorted); - log.log("This is a multi-release jar, with versions: " + Join.join(", ", versionsFoundSorted)); + log.log("This is a multi-release jar, with versions: " + + StringUtils.join(", ", versionsFoundSorted)); } // Sort in decreasing order of version in preparation for version masking diff --git a/src/main/java/nonapi/io/github/classgraph/utils/Join.java b/src/main/java/nonapi/io/github/classgraph/utils/Join.java deleted file mode 100644 index e1946e223..000000000 --- a/src/main/java/nonapi/io/github/classgraph/utils/Join.java +++ /dev/null @@ -1,111 +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; - -/** A replacement for Java 8's String.join() that will allow compilation on Java 7. */ -public final class Join { - - /** - * Constructor. - */ - private Join() { - // Cannot be constructed - } - - /** - * A replacement for Java 8's String.join(). - * - * @param buf - * The buffer to append to. - * @param addAtBeginning - * The token to add at the beginning of the string. - * @param sep - * The separator string. - * @param addAtEnd - * The token to add at the end of the string. - * @param iterable - * The {@link Iterable} to join. - */ - public static void join(final StringBuilder buf, final String addAtBeginning, final String sep, - final String addAtEnd, final Iterable iterable) { - if (!addAtBeginning.isEmpty()) { - buf.append(addAtBeginning); - } - boolean first = true; - for (final Object item : iterable) { - if (first) { - first = false; - } else { - buf.append(sep); - } - buf.append(item == null ? "null" : item.toString()); - } - if (!addAtEnd.isEmpty()) { - buf.append(addAtEnd); - } - } - - /** - * A replacement for Java 8's String.join(). - * - * @param sep - * The separator string. - * @param iterable - * The {@link Iterable} to join. - * @return The string representation of the joined elements. - */ - public static String join(final String sep, final Iterable iterable) { - final StringBuilder buf = new StringBuilder(); - join(buf, "", sep, "", iterable); - return buf.toString(); - } - - /** - * A replacement for Java 8's String.join(). - * - * @param sep - * The separator string. - * @param items - * The items to join. - * @return The string representation of the joined items. - */ - public static String join(final String sep, final Object... items) { - final StringBuilder buf = new StringBuilder(); - boolean first = true; - for (final Object item : items) { - if (first) { - first = false; - } else { - buf.append(sep); - } - buf.append(item.toString()); - } - return buf.toString(); - } -} diff --git a/src/main/java/nonapi/io/github/classgraph/utils/StringUtils.java b/src/main/java/nonapi/io/github/classgraph/utils/StringUtils.java index 68e28cc02..f601be8fd 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/StringUtils.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/StringUtils.java @@ -134,4 +134,75 @@ public static String readString(final byte[] arr, final int startOffset, final i } } + /** + * A replacement for Java 8's String.join(). + * + * @param buf + * The buffer to append to. + * @param addAtBeginning + * The token to add at the beginning of the string. + * @param sep + * The separator string. + * @param addAtEnd + * The token to add at the end of the string. + * @param iterable + * The {@link Iterable} to join. + */ + public static void join(final StringBuilder buf, final String addAtBeginning, final String sep, + final String addAtEnd, final Iterable iterable) { + if (!addAtBeginning.isEmpty()) { + buf.append(addAtBeginning); + } + boolean first = true; + for (final Object item : iterable) { + if (first) { + first = false; + } else { + buf.append(sep); + } + buf.append(item == null ? "null" : item.toString()); + } + if (!addAtEnd.isEmpty()) { + buf.append(addAtEnd); + } + } + + /** + * A replacement for Java 8's String.join(). + * + * @param sep + * The separator string. + * @param iterable + * The {@link Iterable} to join. + * @return The string representation of the joined elements. + */ + public static String join(final String sep, final Iterable iterable) { + final StringBuilder buf = new StringBuilder(); + join(buf, "", sep, "", iterable); + return buf.toString(); + } + + /** + * A replacement for Java 8's String.join(). + * + * @param sep + * The separator string. + * @param items + * The items to join. + * @return The string representation of the joined items. + */ + public static String join(final String sep, final Object... items) { + final StringBuilder buf = new StringBuilder(); + boolean first = true; + for (final Object item : items) { + if (first) { + first = false; + } else { + buf.append(sep); + } + buf.append(item.toString()); + } + return buf.toString(); + } + } From 1a21e9a6580d0ea40e66ab67d52b8021e4f617d6 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 25 Jan 2021 03:48:38 -0700 Subject: [PATCH 1008/1778] Clean up exception throwing --- .../java/io/github/classgraph/ClassGraph.java | 10 +++--- .../classgraph/ClassGraphException.java | 31 ++----------------- .../java/io/github/classgraph/MethodInfo.java | 8 ++--- .../io/github/classgraph/TypeArgument.java | 4 +-- .../AutoCloseableExecutorService.java | 4 +-- .../fastzipfilereader/LogicalZipFile.java | 3 +- .../classgraph/json/ClassFieldCache.java | 6 ++-- .../classgraph/json/JSONDeserializer.java | 5 ++- .../classgraph/json/JSONSerializer.java | 9 +++--- .../classgraph/json/TypeResolutions.java | 6 ++-- .../github/classgraph/scanspec/ScanSpec.java | 3 +- .../io/github/classgraph/utils/FileUtils.java | 11 +++---- .../io/github/classgraph/utils/JarUtils.java | 4 +-- 13 files changed, 29 insertions(+), 75 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassGraph.java b/src/main/java/io/github/classgraph/ClassGraph.java index ac5f296db..b712ab5eb 100644 --- a/src/main/java/io/github/classgraph/ClassGraph.java +++ b/src/main/java/io/github/classgraph/ClassGraph.java @@ -1565,10 +1565,9 @@ public ScanResult scan(final ExecutorService executorService, final int numParal return scanResult; } catch (final InterruptedException | CancellationException e) { - throw ClassGraphException.newClassGraphException("Scan interrupted", e); + throw new ClassGraphException("Scan interrupted", e); } catch (final ExecutionException e) { - throw ClassGraphException.newClassGraphException("Uncaught exception during scan", - InterruptionChecker.getCause(e)); + throw new ClassGraphException("Uncaught exception during scan", InterruptionChecker.getCause(e)); } } @@ -1625,10 +1624,9 @@ ScanResult getClasspathScanResult(final AutoCloseableExecutorService executorSer return scanResult; } catch (final InterruptedException | CancellationException e) { - throw ClassGraphException.newClassGraphException("Scan interrupted", e); + throw new ClassGraphException("Scan interrupted", e); } catch (final ExecutionException e) { - throw ClassGraphException.newClassGraphException("Uncaught exception during scan", - InterruptionChecker.getCause(e)); + throw new ClassGraphException("Uncaught exception during scan", InterruptionChecker.getCause(e)); } } diff --git a/src/main/java/io/github/classgraph/ClassGraphException.java b/src/main/java/io/github/classgraph/ClassGraphException.java index ef6f76d8e..d88ea9732 100644 --- a/src/main/java/io/github/classgraph/ClassGraphException.java +++ b/src/main/java/io/github/classgraph/ClassGraphException.java @@ -46,7 +46,7 @@ public class ClassGraphException extends IllegalArgumentException { * @param message * the message */ - private ClassGraphException(final String message) { + ClassGraphException(final String message) { super(message); } @@ -58,34 +58,7 @@ private ClassGraphException(final String message) { * @param cause * the cause */ - private ClassGraphException(final String message, final Throwable cause) { + ClassGraphException(final String message, final Throwable cause) { super(message, cause); } - - /** - * Static factory method to stop IDEs from auto-completing ClassGraphException after "new ClassGraph". - * - * @param message - * the message - * @return the ClassGraphException - */ - public static ClassGraphException newClassGraphException(final String message) { - return new ClassGraphException(message); - } - - /** - * Static factory method to stop IDEs from auto-completing ClassGraphException after "new ClassGraph". - * - * @param message - * the message - * @param cause - * the cause - * @return the ClassGraphException - * @throws ClassGraphException - * the class graph exception - */ - public static ClassGraphException newClassGraphException(final String message, final Throwable cause) - throws ClassGraphException { - return new ClassGraphException(message, cause); - } } diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index d3e275764..c6dd283cd 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -417,12 +417,12 @@ public MethodParameterInfo[] getParameterInfo() { final List paramTypeSignatures = getTypeSignature() != null ? getTypeSignature().getParameterTypeSignatures() : null; - + // Figure out the number of params in the alignment (should be num params in type descriptor) final int numParams = paramTypeDescriptors.size(); if (paramTypeSignatures != null && paramTypeSignatures.size() > numParams) { // Should not happen - throw ClassGraphException.newClassGraphException( + throw new ClassGraphException( "typeSignatureParamTypes.size() > typeDescriptorParamTypes.size() for method " + declaringClassName + "." + name); } @@ -433,8 +433,8 @@ public MethodParameterInfo[] getParameterInfo() { parameterAnnotationInfo == null ? 0 : parameterAnnotationInfo.length)); if (otherParamMax > numParams) { // Should not happen - throw ClassGraphException.newClassGraphException("Type descriptor for method " + declaringClassName - + "." + name + " has insufficient parameters"); + throw new ClassGraphException("Type descriptor for method " + declaringClassName + "." + name + + " has insufficient parameters"); } // Kotlin is very inconsistent about the arity of each of the parameter metadata types, see: diff --git a/src/main/java/io/github/classgraph/TypeArgument.java b/src/main/java/io/github/classgraph/TypeArgument.java index 02aed28dd..302d57034 100644 --- a/src/main/java/io/github/classgraph/TypeArgument.java +++ b/src/main/java/io/github/classgraph/TypeArgument.java @@ -277,11 +277,9 @@ protected void toStringInternal(final boolean useSimpleNames, final AnnotationIn typeSignature.toString(useSimpleNames, buf); break; case NONE: + default: typeSignature.toString(useSimpleNames, buf); break; - default: - // Should not happen - throw ClassGraphException.newClassGraphException("Unknown wildcard type " + wildcard); } } } diff --git a/src/main/java/nonapi/io/github/classgraph/concurrency/AutoCloseableExecutorService.java b/src/main/java/nonapi/io/github/classgraph/concurrency/AutoCloseableExecutorService.java index d04119f94..6a6821a56 100644 --- a/src/main/java/nonapi/io/github/classgraph/concurrency/AutoCloseableExecutorService.java +++ b/src/main/java/nonapi/io/github/classgraph/concurrency/AutoCloseableExecutorService.java @@ -35,8 +35,6 @@ import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; -import io.github.classgraph.ClassGraphException; - /** A ThreadPoolExecutor that can be used in a try-with-resources block. */ public class AutoCloseableExecutorService extends ThreadPoolExecutor implements AutoCloseable { /** The {@link InterruptionChecker}. */ @@ -108,7 +106,7 @@ public void close() { // Interrupt all the threads to terminate them, if awaitTermination() timed out shutdownNow(); } catch (final SecurityException e) { - throw ClassGraphException.newClassGraphException("Could not shut down ExecutorService -- need " + throw new RuntimeException("Could not shut down ExecutorService -- need " + "java.lang.RuntimePermission(\"modifyThread\"), " + "or the security manager's checkAccess method denies access", e); } 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 70cc7aa6e..a0325ef72 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java @@ -44,7 +44,6 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import io.github.classgraph.ClassGraphException; import nonapi.io.github.classgraph.fileslice.ArraySlice; import nonapi.io.github.classgraph.fileslice.reader.RandomAccessReader; import nonapi.io.github.classgraph.utils.CollectionUtils; @@ -225,7 +224,7 @@ private static Entry getManifestValue(final byte[] manifest, fi val = buf.toString("UTF-8"); } catch (final UnsupportedEncodingException e) { // Should not happen - throw ClassGraphException.newClassGraphException("UTF-8 encoding unsupported", e); + throw new RuntimeException("UTF-8 encoding is not supported in your JRE", e); } } return new SimpleEntry<>(val.endsWith(" ") ? val.trim() : val, curr); 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 9e69e0bad..68d261fe9 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/ClassFieldCache.java +++ b/src/main/java/nonapi/io/github/classgraph/json/ClassFieldCache.java @@ -61,8 +61,6 @@ import java.util.concurrent.LinkedTransferQueue; import java.util.concurrent.TransferQueue; -import io.github.classgraph.ClassGraphException; - /** * A cache of field types and associated constructors for each encountered class, used to speed up constructor * lookup. @@ -95,8 +93,8 @@ class ClassFieldCache { NO_CONSTRUCTOR = NoConstructor.class.getDeclaredConstructor(); } catch (NoSuchMethodException | SecurityException e) { // Should not happen - throw ClassGraphException.newClassGraphException( - "Could not find or access constructor for " + NoConstructor.class.getName(), e); + throw new RuntimeException("Could not find or access constructor for " + NoConstructor.class.getName(), + e); } } 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 ace423441..2b2ce173e 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/JSONDeserializer.java +++ b/src/main/java/nonapi/io/github/classgraph/json/JSONDeserializer.java @@ -40,7 +40,6 @@ import java.util.Map; import java.util.Map.Entry; -import io.github.classgraph.ClassGraphException; import nonapi.io.github.classgraph.types.ParseException; /** @@ -72,7 +71,7 @@ private static Object jsonBasicValueToObject(final Object jsonVal, final Type ex if (jsonVal == null) { return null; } else if (jsonVal instanceof JSONArray || jsonVal instanceof JSONObject) { - throw ClassGraphException.newClassGraphException("Expected a basic value type"); + throw new RuntimeException("Expected a basic value type"); } if (expectedType instanceof ParameterizedType) { if (((ParameterizedType) expectedType).getRawType().getClass() == Class.class) { @@ -403,7 +402,7 @@ private static void populateObjectFromJsonObject(final Object objectInstance, fi itemJsonValue = jsonArray.items.get(i); } else { // Can't happen (keep static analyzers happy) - throw ClassGraphException.newClassGraphException("This exception should not be thrown"); + throw new RuntimeException("This exception should not be thrown"); } final boolean itemJsonValueIsJsonObject = itemJsonValue instanceof JSONObject; final boolean itemJsonValueIsJsonArray = itemJsonValue instanceof JSONArray; 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 6a0fd92eb..4b8d53b34 100644 --- a/src/main/java/nonapi/io/github/classgraph/json/JSONSerializer.java +++ b/src/main/java/nonapi/io/github/classgraph/json/JSONSerializer.java @@ -43,7 +43,6 @@ import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; -import io.github.classgraph.ClassGraphException; import nonapi.io.github.classgraph.utils.CollectionUtils; /** @@ -94,14 +93,14 @@ private static void assignObjectIds(final Object jsonVal, final Object refdObj = ((JSONReference) jsonVal).idObject; if (refdObj == null) { // Should not happen - throw ClassGraphException.newClassGraphException("Internal inconsistency"); + throw new RuntimeException("Internal inconsistency"); } // Look up the JSON object corresponding to the referenced object final ReferenceEqualityKey refdObjKey = new ReferenceEqualityKey<>(refdObj); final JSONObject refdJsonVal = objToJSONVal.get(refdObjKey); if (refdJsonVal == null) { // Should not happen - throw ClassGraphException.newClassGraphException("Internal inconsistency"); + throw new RuntimeException("Internal inconsistency"); } // See if the JSON object has an @Id field // (for serialization, typeResolutions can be null) @@ -383,8 +382,8 @@ private static Object toJSONGraph(final Object obj, final Set Date: Mon, 25 Jan 2021 09:28:24 -0700 Subject: [PATCH 1009/1778] Fix type inference problem for `javac` (#495) --- src/main/java/io/github/classgraph/ClassRefTypeSignature.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/ClassRefTypeSignature.java b/src/main/java/io/github/classgraph/ClassRefTypeSignature.java index b2b4792ea..881f6c347 100644 --- a/src/main/java/io/github/classgraph/ClassRefTypeSignature.java +++ b/src/main/java/io/github/classgraph/ClassRefTypeSignature.java @@ -479,7 +479,7 @@ static ClassRefTypeSignature parse(final Parser parser, final String definingCla if (!TypeUtils.getIdentifierToken(parser, /* stopAtDollarSign = */ true)) { // Got the empty string as the next token after '$', i.e. found an empty suffix. suffixes.add(""); - suffixTypeArguments.add(Collections.emptyList()); + suffixTypeArguments.add(Collections. emptyList()); dropSuffixes = true; } else { suffixes.add(parser.currToken()); From 35da04c83711410aabbf7db81934a969fd6ab876 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 26 Jan 2021 00:27:11 -0700 Subject: [PATCH 1010/1778] [maven-release-plugin] prepare release classgraph-4.8.100 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 5ee93526f..5e93707d1 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.100-SNAPSHOT + 4.8.100 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.98 + classgraph-4.8.100 From 744ab77cf6db89d8770716e4370f50e155de56d1 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 26 Jan 2021 00:27:18 -0700 Subject: [PATCH 1011/1778] [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 5e93707d1..c856bee25 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.100 + 4.8.101-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.100 + classgraph-4.8.98 From af6bc64f961368708f8d223a3eab5cadb29dc34f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 26 Jan 2021 05:31:45 -0700 Subject: [PATCH 1012/1778] Add more info on source of type signature parsing exceptions (#495) --- src/main/java/io/github/classgraph/ClassInfo.java | 3 ++- src/main/java/io/github/classgraph/FieldInfo.java | 3 ++- src/main/java/io/github/classgraph/MethodInfo.java | 5 +++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index 5f83c499d..1336f2b8d 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -2639,7 +2639,8 @@ public ClassTypeSignature getTypeSignature() { } } } catch (final ParseException e) { - throw new IllegalArgumentException(e); + throw new IllegalArgumentException( + "Invalid type signature for class " + getName() + ": " + typeSignatureStr, e); } } return typeSignature; diff --git a/src/main/java/io/github/classgraph/FieldInfo.java b/src/main/java/io/github/classgraph/FieldInfo.java index ed6329805..935a52280 100644 --- a/src/main/java/io/github/classgraph/FieldInfo.java +++ b/src/main/java/io/github/classgraph/FieldInfo.java @@ -259,7 +259,8 @@ public TypeSignature getTypeSignature() { } } } catch (final ParseException e) { - throw new IllegalArgumentException(e); + throw new IllegalArgumentException("Invalid type signature for class " + getClassInfo() + ", field " + + getName() + ": " + typeSignatureStr, e); } } return typeSignature; diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index c6dd283cd..7dfc6a5a6 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -248,7 +248,8 @@ public MethodTypeSignature getTypeSignature() { } } } catch (final ParseException e) { - throw new IllegalArgumentException(e); + throw new IllegalArgumentException("Invalid type signature for class " + getClassInfo() + + ", method " + getName() + ": " + typeSignatureStr, e); } } return typeSignature; @@ -417,7 +418,7 @@ public MethodParameterInfo[] getParameterInfo() { final List paramTypeSignatures = getTypeSignature() != null ? getTypeSignature().getParameterTypeSignatures() : null; - + // Figure out the number of params in the alignment (should be num params in type descriptor) final int numParams = paramTypeDescriptors.size(); if (paramTypeSignatures != null && paramTypeSignatures.size() > numParams) { From 7af65f936291afec73ac7d713e7ce0245b3091f6 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 26 Jan 2021 05:41:24 -0700 Subject: [PATCH 1013/1778] Fix Javadoc --- .../java/io/github/classgraph/ClassGraphClassLoader.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java index 73a92678d..67fa18e2a 100644 --- a/src/main/java/io/github/classgraph/ClassGraphClassLoader.java +++ b/src/main/java/io/github/classgraph/ClassGraphClassLoader.java @@ -314,7 +314,11 @@ protected Class findClass(final String className) throw new ClassNotFoundException("Could not find or load classfile for class " + className); } - /** Get classpath URLs. */ + /** + * Get classpath URLs. + * + * @return The classpath URLs in the {@link ScanResult} handled by this {@link ClassLoader}. + */ public URL[] getURLs() { return scanResult.getClasspathURLs().toArray(new URL[0]); } From 0a469b64777292f736f9ea26b3e0c4069944a27f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 26 Jan 2021 05:47:51 -0700 Subject: [PATCH 1014/1778] Improve exception message --- src/main/java/io/github/classgraph/FieldInfo.java | 2 +- src/main/java/io/github/classgraph/MethodInfo.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/github/classgraph/FieldInfo.java b/src/main/java/io/github/classgraph/FieldInfo.java index 935a52280..11f2a1bcc 100644 --- a/src/main/java/io/github/classgraph/FieldInfo.java +++ b/src/main/java/io/github/classgraph/FieldInfo.java @@ -259,7 +259,7 @@ public TypeSignature getTypeSignature() { } } } catch (final ParseException e) { - throw new IllegalArgumentException("Invalid type signature for class " + getClassInfo() + ", field " + throw new IllegalArgumentException("Invalid type signature for field " + getClassName() + "." + getName() + ": " + typeSignatureStr, e); } } diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index 7dfc6a5a6..155b679f5 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -248,8 +248,8 @@ public MethodTypeSignature getTypeSignature() { } } } catch (final ParseException e) { - throw new IllegalArgumentException("Invalid type signature for class " + getClassInfo() - + ", method " + getName() + ": " + typeSignatureStr, e); + throw new IllegalArgumentException("Invalid type signature for method " + getClassName() + "." + + getName() + ": " + typeSignatureStr, e); } } return typeSignature; From 253a7c02b947e74de30310648a75c46d3e850e61 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 26 Jan 2021 05:53:18 -0700 Subject: [PATCH 1015/1778] Expose the `getClassName()` method --- .../java/io/github/classgraph/FieldInfo.java | 16 +++++++++------- .../java/io/github/classgraph/MethodInfo.java | 15 +++++++++------ 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/main/java/io/github/classgraph/FieldInfo.java b/src/main/java/io/github/classgraph/FieldInfo.java index 11f2a1bcc..056e64a2d 100644 --- a/src/main/java/io/github/classgraph/FieldInfo.java +++ b/src/main/java/io/github/classgraph/FieldInfo.java @@ -134,10 +134,11 @@ public String getName() { } /** - * Get the {@link ClassInfo} object for the declaring class (i.e. the class that declares this field). + * Get the {@link ClassInfo} object for the class that declares this field. * - * @return The {@link ClassInfo} object for the declaring class (i.e. the class that declares this field), or - * null if the class representing the type of the field was not encountered during scanning. + * @return The {@link ClassInfo} object for the declaring class. + * + * @see #getClassName() */ @Override public ClassInfo getClassInfo() { @@ -421,13 +422,14 @@ void handleRepeatableAnnotations(final Set allRepeatableAnnotationNames) // ------------------------------------------------------------------------------------------------------------- /** - * Returns the name of the declaring class, so that super.getClassInfo() returns the {@link ClassInfo} object - * for the declaring class. + * Get the name of the class that declares this field. * - * @return the name of the declaring class. + * @return The name of the declaring class. + * + * @see #getClassInfo() */ @Override - protected String getClassName() { + public String getClassName() { return declaringClassName; } diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index 155b679f5..16bcb73c7 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -188,9 +188,11 @@ public String getModifiersStr() { } /** - * Get the {@link ClassInfo} object for the declaring class (i.e. the class that declares this method). + * Get the {@link ClassInfo} object for the class that declares this method. * - * @return The {@link ClassInfo} object for the declaring class (i.e. the class that declares this method). + * @return The {@link ClassInfo} object for the declaring class. + * + * @see #getClassName() */ @Override public ClassInfo getClassInfo() { @@ -703,13 +705,14 @@ void handleRepeatableAnnotations(final Set allRepeatableAnnotationNames) // ------------------------------------------------------------------------------------------------------------- /** - * Returns the declaring class name, so that super.getClassInfo() returns the {@link ClassInfo} object for the - * declaring class. + * Get the name of the class that declares this method. * - * @return the class name + * @return The name of the declaring class. + * + * @see #getClassInfo() */ @Override - protected String getClassName() { + public String getClassName() { return declaringClassName; } From 339c662f6ca3d3fe98869f331ebd1df5b0d1e904 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Jan 2021 02:43:19 -0700 Subject: [PATCH 1016/1778] Better normalization of "file:" URLs --- .../io/github/classgraph/utils/FastPathResolver.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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 436543ede..abfe6f824 100644 --- a/src/main/java/nonapi/io/github/classgraph/utils/FastPathResolver.java +++ b/src/main/java/nonapi/io/github/classgraph/utils/FastPathResolver.java @@ -225,8 +225,13 @@ public static String resolve(final String resolveBasePath, final String relative prefix = "jrt:"; isAbsolutePath = true; } else if (relativePath.regionMatches(true, startIdx, "file:", 0, 5)) { - // Strip off any "file:" prefix from relative path + // 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) From ed3b095a9e73099cc0346cfaf79a7ff4c2e9b7be Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Jan 2021 03:34:25 -0700 Subject: [PATCH 1017/1778] Make parsing robust to erroneous Scala type signatures (#495) --- .../java/io/github/classgraph/ClassInfo.java | 21 ++++-- .../java/io/github/classgraph/FieldInfo.java | 43 +++++++---- .../java/io/github/classgraph/MethodInfo.java | 73 +++++++++++++----- .../issues/issue407/Issue407Test.java | 2 +- .../issues/issue495/Issue495Test.java | 74 +++++++++++++++++++ 5 files changed, 173 insertions(+), 40 deletions(-) create mode 100644 src/test/java/io/github/classgraph/issues/issue495/Issue495Test.java diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index 1336f2b8d..ed9b42648 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -2639,8 +2639,8 @@ public ClassTypeSignature getTypeSignature() { } } } catch (final ParseException e) { - throw new IllegalArgumentException( - "Invalid type signature for class " + getName() + ": " + typeSignatureStr, e); + throw new IllegalArgumentException("Invalid type signature for class " + getName() + + " in classpath element " + getClasspathElementURI() + " : " + typeSignatureStr, e); } } return typeSignature; @@ -2934,9 +2934,13 @@ protected void findReferencedClassInfo(final Map classNameToC if (annotationDefaultParamValues != null) { annotationDefaultParamValues.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); } - final ClassTypeSignature classSig = getTypeSignature(); - if (classSig != null) { - classSig.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); + try { + final ClassTypeSignature classSig = getTypeSignature(); + if (classSig != null) { + classSig.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); + } + } catch (final Exception e) { + // Ignore } } @@ -3031,7 +3035,12 @@ protected void toString(final boolean useSimpleNames, final StringBuilder buf) { annotation.toString(useSimpleNames, buf); } } - final ClassTypeSignature typeSig = getTypeSignature(); + ClassTypeSignature typeSig = null; + try { + typeSig = getTypeSignature(); + } catch (final Exception e) { + // Ignore + } if (typeSig != null) { // Generic classes typeSig.toStringInternal(useSimpleNames ? ClassInfo.getSimpleName(name) : name, diff --git a/src/main/java/io/github/classgraph/FieldInfo.java b/src/main/java/io/github/classgraph/FieldInfo.java index 056e64a2d..275bd53ef 100644 --- a/src/main/java/io/github/classgraph/FieldInfo.java +++ b/src/main/java/io/github/classgraph/FieldInfo.java @@ -260,8 +260,13 @@ public TypeSignature getTypeSignature() { } } } catch (final ParseException e) { - throw new IllegalArgumentException("Invalid type signature for field " + getClassName() + "." - + getName() + ": " + typeSignatureStr, e); + throw new IllegalArgumentException( + "Invalid type signature for field " + getClassName() + "." + getName() + + (getClassInfo() != null + ? " in classpath element " + getClassInfo().getClasspathElementURI() + : "") + + " : " + typeSignatureStr, + e); } } return typeSignature; @@ -287,12 +292,16 @@ public String getTypeSignatureStr() { * field. */ public TypeSignature getTypeSignatureOrTypeDescriptor() { - final TypeSignature typeSig = getTypeSignature(); - if (typeSig != null) { - return typeSig; - } else { - return getTypeDescriptor(); + TypeSignature typeSig = null; + try { + typeSig = getTypeSignature(); + if (typeSig != null) { + return typeSig; + } + } catch (final Exception e) { + // Ignore } + return getTypeDescriptor(); } /** @@ -463,13 +472,21 @@ void setScanResult(final ScanResult scanResult) { @Override protected void findReferencedClassInfo(final Map classNameToClassInfo, final Set refdClassInfo) { - final TypeSignature methodSig = getTypeSignature(); - if (methodSig != null) { - methodSig.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); + try { + final TypeSignature methodSig = getTypeSignature(); + if (methodSig != null) { + methodSig.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); + } + } catch (final Exception e) { + // Ignore } - final TypeSignature methodDesc = getTypeDescriptor(); - if (methodDesc != null) { - methodDesc.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); + try { + final TypeSignature methodDesc = getTypeDescriptor(); + if (methodDesc != null) { + methodDesc.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); + } + } catch (final Exception e) { + // Ignore } if (annotationInfo != null) { for (final AnnotationInfo ai : annotationInfo) { diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index 16bcb73c7..3f43ccaaf 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -250,8 +250,13 @@ public MethodTypeSignature getTypeSignature() { } } } catch (final ParseException e) { - throw new IllegalArgumentException("Invalid type signature for method " + getClassName() + "." - + getName() + ": " + typeSignatureStr, e); + throw new IllegalArgumentException( + "Invalid type signature for method " + getClassName() + "." + getName() + + (getClassInfo() != null + ? " in classpath element " + getClassInfo().getClasspathElementURI() + : "") + + " : " + typeSignatureStr, + e); } } return typeSignature; @@ -277,12 +282,16 @@ public String getTypeSignatureStr() { * method. */ public MethodTypeSignature getTypeSignatureOrTypeDescriptor() { - final MethodTypeSignature typeSig = getTypeSignature(); - if (typeSig != null) { - return typeSig; - } else { - return getTypeDescriptor(); + MethodTypeSignature typeSig = null; + try { + typeSig = getTypeSignature(); + if (typeSig != null) { + return typeSig; + } + } catch (final Exception e) { + // Ignore } + return getTypeDescriptor(); } /** @@ -416,13 +425,28 @@ public boolean isDefault() { public MethodParameterInfo[] getParameterInfo() { if (parameterInfo == null) { // Get params from the type descriptor, and from the type signature if available - final List paramTypeDescriptors = getTypeDescriptor().getParameterTypeSignatures(); - final List paramTypeSignatures = getTypeSignature() != null - ? getTypeSignature().getParameterTypeSignatures() - : null; + 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 + } + List paramTypeSignatures = null; + try { + final MethodTypeSignature typeSig = getTypeSignature(); + if (typeSig != null) { + paramTypeSignatures = typeSig.getParameterTypeSignatures(); + } + } catch (final Exception e) { + // Ignore + } // Figure out the number of params in the alignment (should be num params in type descriptor) - final int numParams = paramTypeDescriptors.size(); if (paramTypeSignatures != null && paramTypeSignatures.size() > numParams) { // Should not happen throw new ClassGraphException( @@ -493,7 +517,7 @@ public MethodParameterInfo[] getParameterInfo() { } List paramTypeSignaturesAligned = null; if (paramTypeSignatures != null && numParams > 0) { - if (paramTypeSignatures.size() == paramTypeDescriptors.size()) { + if (paramTypeSignatures.size() == numParams) { // No alignment necessary paramTypeSignaturesAligned = paramTypeSignatures; } else { @@ -512,7 +536,8 @@ public MethodParameterInfo[] getParameterInfo() { for (int i = 0; i < numParams; i++) { parameterInfo[i] = new MethodParameterInfo(this, paramAnnotationInfoAligned == null ? null : paramAnnotationInfoAligned[i], - paramModifiersAligned == null ? 0 : paramModifiersAligned[i], paramTypeDescriptors.get(i), + paramModifiersAligned == null ? 0 : paramModifiersAligned[i], + paramTypeDescriptors == null ? null : paramTypeDescriptors.get(i), paramTypeSignaturesAligned == null ? null : paramTypeSignaturesAligned.get(i), paramNamesAligned == null ? null : paramNamesAligned[i]); parameterInfo[i].setScanResult(scanResult); @@ -760,13 +785,21 @@ void setScanResult(final ScanResult scanResult) { @Override protected void findReferencedClassInfo(final Map classNameToClassInfo, final Set refdClassInfo) { - final MethodTypeSignature methodSig = getTypeSignature(); - if (methodSig != null) { - methodSig.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); + try { + final MethodTypeSignature methodSig = getTypeSignature(); + if (methodSig != null) { + methodSig.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); + } + } catch (final Exception e) { + // Ignore } - final MethodTypeSignature methodDesc = getTypeDescriptor(); - if (methodDesc != null) { - methodDesc.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); + try { + final MethodTypeSignature methodDesc = getTypeDescriptor(); + if (methodDesc != null) { + methodDesc.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); + } + } catch (final Exception e) { + // Ignore } if (annotationInfo != null) { for (final AnnotationInfo ai : annotationInfo) { diff --git a/src/test/java/io/github/classgraph/issues/issue407/Issue407Test.java b/src/test/java/io/github/classgraph/issues/issue407/Issue407Test.java index a8cd439d8..90296f6e1 100644 --- a/src/test/java/io/github/classgraph/issues/issue407/Issue407Test.java +++ b/src/test/java/io/github/classgraph/issues/issue407/Issue407Test.java @@ -43,7 +43,7 @@ import io.github.classgraph.ScanResult; /** - * Issue193Test. + * Test. */ public class Issue407Test { /** diff --git a/src/test/java/io/github/classgraph/issues/issue495/Issue495Test.java b/src/test/java/io/github/classgraph/issues/issue495/Issue495Test.java new file mode 100644 index 000000000..415a695ea --- /dev/null +++ b/src/test/java/io/github/classgraph/issues/issue495/Issue495Test.java @@ -0,0 +1,74 @@ +/* + * 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.issues.issue495; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ScanResult; + +/** + * Test. + */ +public class Issue495Test { + /** + * Test. + * + * @throws IOException + * Signals that an I/O exception has occurred. + */ + @Test + public void testScalaTypeSignatures() throws Exception { + final File scalaClassfile = new File( + getClass().getClassLoader().getResource("scalapackage/ScalaClass.class").toURI()); + assertThat(scalaClassfile).canRead(); + + final ClassLoader classLoader = new URLClassLoader( + new URL[] { scalaClassfile.getParentFile().getParentFile().toURI().toURL() }, null); + + try (ScanResult scanResult = new ClassGraph() // + .enableClassInfo().enableInterClassDependencies() // + .acceptPackages("scalapackage") // + .overrideClassLoaders(classLoader) // + .scan()) { + final List classNames = scanResult // + .getAllClasses() // + .getNames(); + assertThat(classNames).containsOnly("scalapackage.ScalaClass"); + } + } +} From a4131c4ef69adb10e5abe6ac6b4263f5b539f79f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Jan 2021 03:35:59 -0700 Subject: [PATCH 1018/1778] [maven-release-plugin] prepare release classgraph-4.8.101 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index c856bee25..579664b04 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.101-SNAPSHOT + 4.8.101 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.98 + classgraph-4.8.101 From 0ead27c931b669534264bbf2b44c6c073932f787 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Jan 2021 03:36:09 -0700 Subject: [PATCH 1019/1778] [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 579664b04..44d8c16f8 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.101 + 4.8.102-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.101 + classgraph-4.8.98 From 3572c32bef46b836b7ee4dda90bdaa8d366e54a1 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Jan 2021 03:44:49 -0700 Subject: [PATCH 1020/1778] Improve test --- .../io/github/classgraph/issues/issue495/Issue495Test.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/test/java/io/github/classgraph/issues/issue495/Issue495Test.java b/src/test/java/io/github/classgraph/issues/issue495/Issue495Test.java index 415a695ea..ccde63cfc 100644 --- a/src/test/java/io/github/classgraph/issues/issue495/Issue495Test.java +++ b/src/test/java/io/github/classgraph/issues/issue495/Issue495Test.java @@ -53,8 +53,9 @@ public class Issue495Test { */ @Test public void testScalaTypeSignatures() throws Exception { - final File scalaClassfile = new File( - getClass().getClassLoader().getResource("scalapackage/ScalaClass.class").toURI()); + final URL resource = getClass().getClassLoader().getResource("scalapackage/ScalaClass.class"); + assertThat(resource).isNotNull(); + final File scalaClassfile = new File(resource.toURI()); assertThat(scalaClassfile).canRead(); final ClassLoader classLoader = new URLClassLoader( From aee567fae2512a89e494c9ec284b0b193bd2b2c8 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Jan 2021 03:46:21 -0700 Subject: [PATCH 1021/1778] Drop version back down --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 44d8c16f8..c856bee25 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.102-SNAPSHOT + 4.8.101-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 4fee0fdcf1eb776212a5403546fbbd975683b57f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Jan 2021 03:48:48 -0700 Subject: [PATCH 1022/1778] Update test --- .../java/io/github/classgraph/issues/issue495/Issue495Test.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/io/github/classgraph/issues/issue495/Issue495Test.java b/src/test/java/io/github/classgraph/issues/issue495/Issue495Test.java index ccde63cfc..47f7dc7a3 100644 --- a/src/test/java/io/github/classgraph/issues/issue495/Issue495Test.java +++ b/src/test/java/io/github/classgraph/issues/issue495/Issue495Test.java @@ -53,7 +53,7 @@ public class Issue495Test { */ @Test public void testScalaTypeSignatures() throws Exception { - final URL resource = getClass().getClassLoader().getResource("scalapackage/ScalaClass.class"); + final URL resource = Issue495Test.class.getClassLoader().getResource("scalapackage/ScalaClass.class"); assertThat(resource).isNotNull(); final File scalaClassfile = new File(resource.toURI()); assertThat(scalaClassfile).canRead(); From 91b3ebbfd6762de569b380a3a8809cb6656713e5 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Jan 2021 03:49:18 -0700 Subject: [PATCH 1023/1778] [maven-release-plugin] prepare release classgraph-4.8.101 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index c856bee25..579664b04 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.101-SNAPSHOT + 4.8.101 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.98 + classgraph-4.8.101 From ec5639589cb2acf5cb510e550c32786e5c8ac568 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Jan 2021 03:49:25 -0700 Subject: [PATCH 1024/1778] [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 579664b04..44d8c16f8 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.101 + 4.8.102-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.101 + classgraph-4.8.98 From e21f13e7344e168e4cefd1f850dc47501bb49284 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Jan 2021 03:53:15 -0700 Subject: [PATCH 1025/1778] Update test --- .../classgraph/issues/issue495/Issue495Test.java | 10 ++++------ src/test/resources/scalapackage.zip | Bin 0 -> 1179 bytes 2 files changed, 4 insertions(+), 6 deletions(-) create mode 100644 src/test/resources/scalapackage.zip diff --git a/src/test/java/io/github/classgraph/issues/issue495/Issue495Test.java b/src/test/java/io/github/classgraph/issues/issue495/Issue495Test.java index 47f7dc7a3..436616a85 100644 --- a/src/test/java/io/github/classgraph/issues/issue495/Issue495Test.java +++ b/src/test/java/io/github/classgraph/issues/issue495/Issue495Test.java @@ -53,13 +53,11 @@ public class Issue495Test { */ @Test public void testScalaTypeSignatures() throws Exception { - final URL resource = Issue495Test.class.getClassLoader().getResource("scalapackage/ScalaClass.class"); - assertThat(resource).isNotNull(); - final File scalaClassfile = new File(resource.toURI()); - assertThat(scalaClassfile).canRead(); + final URL resourceURL = Issue495Test.class.getClassLoader().getResource("scalapackage.zip"); + assertThat(resourceURL).isNotNull(); + assertThat(new File(resourceURL.toURI())).canRead(); - final ClassLoader classLoader = new URLClassLoader( - new URL[] { scalaClassfile.getParentFile().getParentFile().toURI().toURL() }, null); + final ClassLoader classLoader = new URLClassLoader(new URL[] { resourceURL }, null); try (ScanResult scanResult = new ClassGraph() // .enableClassInfo().enableInterClassDependencies() // diff --git a/src/test/resources/scalapackage.zip b/src/test/resources/scalapackage.zip new file mode 100644 index 0000000000000000000000000000000000000000..83ce31671677b1e0b42482b00a3a8745272f0387 GIT binary patch literal 1179 zcmWIWW@Zs#009R{>mV=#O7Jo$Fcc>z<|Gy*CTAz6r|O4>@G`Lfv=&VG55yq4w1S&~ zk>v$50|S@{02{@@z`>v#l?EbAQEi!41K|b=Rji>3^0st z4av>F6((YLTwg$#X=i}b9kUWPt%JMRR9{WovScDpW(13;_ri&{OsB4Uc&zZ%^lN6( z#<}}fUsB7x|9$Z?*~vL;&3rei&aa+V9Vd7H`HlKNKTqc~d|-IR@igO5?7u$$dqvS( z+&V9pR*EcT6yz&E*3b6*PxZZ`o%!46MLkav+Ft&jd+$#ktAg0X;_EzbXR81Cak9O$ zLfpFXzFWk<@+2t{C;QUa`I{YA@I31d)9{Ps+Wo||C};lh%>evO7pnqz1qlA)3@@LefafpMZIzNwUgWK-+z<;RC`vC@SRr) zX{ElG*WQww<`s3>D4u9p6 z?O8-u)2m14GhUoXsoW7^x`p+>xbk|LTPQ{PkzaqL8zBhKpaV=gE_DoN~)R zdi%EXbLCfuF5Z9WlJChi?-!o(5@*X(kk)(np~N%Ti+`!KY_aL;SH})rJo$}%_u+`Q z0aII#Z-~3|>6fS#`?nWw-kQhX+-bhG`(D?l+}FF`E?BOVIq6nmjru*;73S-f%wWk= zV&%M9WRoved|GsEqwvwU&kUVD-`X(u)Xv5RBrvGmR6UCzh1-=6O3Eva#Ut4V0SrC^iYkNJVi3ys#*dTL$fUB;wn zUrxf)Z*p$SA%z7`lI5&A>shn@`)Kq2SlDsC>7}t;k^d1T6{DGf2HfgN_dS!o zJ~y^6vnhPz!lycWpZpOUMaRaUmgkx8FI~s}FVo@p_wUCU*gQ&dPPUe$CkUqOaJ^?2 z*_re1bpM7w+9Gjcc{MGc-nV!Z%$_&%XC9-7$3dyJVS0-bMZZ*TmC-rqyDDO4ddfq- zNKww1Wo6SB@Wh|V?ebtSbz30MrpYZ8!g)bporGg=OytxD`T^eT9G9krpX3B)I#A9E z@MdHZVaA<-VHx_ZBZx&}c7_-Y%h1Tyf-*D=ENS#cH=5M!9pKH%1~QHb2#*2j{XpL_ GFaQAF@6dk$ literal 0 HcmV?d00001 From 6510757f27fe894111850e4e7c9ac9b10795a27c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Jan 2021 03:54:04 -0700 Subject: [PATCH 1026/1778] Bump version back down --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 44d8c16f8..c856bee25 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.102-SNAPSHOT + 4.8.101-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 6dad7f9762ac6de58fa3a78cd427e2bc6211d406 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Jan 2021 03:54:40 -0700 Subject: [PATCH 1027/1778] [maven-release-plugin] prepare release classgraph-4.8.101 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index c856bee25..579664b04 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.101-SNAPSHOT + 4.8.101 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.98 + classgraph-4.8.101 From c887e54f35efe7a7d635d562424977b35e1fd124 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Jan 2021 03:54:47 -0700 Subject: [PATCH 1028/1778] [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 579664b04..44d8c16f8 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.101 + 4.8.102-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.101 + classgraph-4.8.98 From 9aae2a7a210efddeb6cfad06a4ae9fc7560118d0 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Jan 2021 04:00:21 -0700 Subject: [PATCH 1029/1778] Make type resolution more robust to erroneous type signatures (#495) --- .../java/io/github/classgraph/TypeVariableSignature.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/TypeVariableSignature.java b/src/main/java/io/github/classgraph/TypeVariableSignature.java index 0aaa290ce..b616221e2 100644 --- a/src/main/java/io/github/classgraph/TypeVariableSignature.java +++ b/src/main/java/io/github/classgraph/TypeVariableSignature.java @@ -101,7 +101,12 @@ public TypeParameter resolve() { if (containingClassInfo == null) { throw new IllegalArgumentException("Could not find ClassInfo object for " + definingClassName); } - final ClassTypeSignature containingClassSignature = containingClassInfo.getTypeSignature(); + ClassTypeSignature containingClassSignature = null; + try { + containingClassSignature = containingClassInfo.getTypeSignature(); + } catch (Exception e) { + // Ignore + } if (containingClassSignature != null && containingClassSignature.typeParameters != null && !containingClassSignature.typeParameters.isEmpty()) { for (final TypeParameter typeParameter : containingClassSignature.typeParameters) { From ff6d8ff3f640dfa93589da9749e5bc5dd8d0266d Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Jan 2021 05:56:30 -0700 Subject: [PATCH 1030/1778] Log malformed type sigs when finding inter-class deps (#495) --- .../io/github/classgraph/AnnotationInfo.java | 7 +++-- .../github/classgraph/AnnotationInfoList.java | 7 +++-- .../classgraph/AnnotationParameterValue.java | 6 ++-- .../AnnotationParameterValueList.java | 8 +++-- .../io/github/classgraph/ArrayClassInfo.java | 6 ++-- .../java/io/github/classgraph/ClassInfo.java | 24 ++++++++------ .../github/classgraph/ClassTypeSignature.java | 3 +- .../java/io/github/classgraph/FieldInfo.java | 31 ++++++++++++------- .../io/github/classgraph/FieldInfoList.java | 8 +++-- .../classgraph/GraphvizDotfileGenerator.java | 4 +-- .../java/io/github/classgraph/MethodInfo.java | 25 +++++++++------ .../io/github/classgraph/MethodInfoList.java | 8 +++-- .../classgraph/MethodTypeSignature.java | 3 +- .../classgraph/ObjectTypedValueWrapper.java | 10 +++--- .../java/io/github/classgraph/ScanResult.java | 15 ++++++--- .../github/classgraph/ScanResultObject.java | 12 +++++-- .../io/github/classgraph/TypeSignature.java | 3 +- .../classgraph/TypeVariableSignature.java | 2 +- 18 files changed, 119 insertions(+), 63 deletions(-) diff --git a/src/main/java/io/github/classgraph/AnnotationInfo.java b/src/main/java/io/github/classgraph/AnnotationInfo.java index e40627c89..a619edaa2 100644 --- a/src/main/java/io/github/classgraph/AnnotationInfo.java +++ b/src/main/java/io/github/classgraph/AnnotationInfo.java @@ -40,6 +40,7 @@ import java.util.Map.Entry; import java.util.Set; +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. */ @@ -227,11 +228,11 @@ void setScanResult(final ScanResult scanResult) { */ @Override protected void findReferencedClassInfo(final Map classNameToClassInfo, - final Set refdClassInfo) { - super.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); + final Set refdClassInfo, final LogNode log) { + super.findReferencedClassInfo(classNameToClassInfo, refdClassInfo, log); if (annotationParamValues != null) { for (final AnnotationParameterValue annotationParamValue : annotationParamValues) { - annotationParamValue.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); + annotationParamValue.findReferencedClassInfo(classNameToClassInfo, refdClassInfo, log); } } } diff --git a/src/main/java/io/github/classgraph/AnnotationInfoList.java b/src/main/java/io/github/classgraph/AnnotationInfoList.java index 28f6093a7..03b60fb5b 100644 --- a/src/main/java/io/github/classgraph/AnnotationInfoList.java +++ b/src/main/java/io/github/classgraph/AnnotationInfoList.java @@ -37,6 +37,7 @@ import io.github.classgraph.ClassInfo.RelType; import nonapi.io.github.classgraph.utils.CollectionUtils; +import nonapi.io.github.classgraph.utils.LogNode; /** A list of {@link AnnotationInfo} objects. */ public class AnnotationInfoList extends MappableInfoList { @@ -155,11 +156,13 @@ public AnnotationInfoList filter(final AnnotationInfoFilter filter) { * the map from class name to {@link ClassInfo}. * @param refdClassInfo * the referenced class info + * @param log + * the log */ protected void findReferencedClassInfo(final Map classNameToClassInfo, - final Set refdClassInfo) { + final Set refdClassInfo, final LogNode log) { for (final AnnotationInfo ai : this) { - ai.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); + ai.findReferencedClassInfo(classNameToClassInfo, refdClassInfo, log); } } diff --git a/src/main/java/io/github/classgraph/AnnotationParameterValue.java b/src/main/java/io/github/classgraph/AnnotationParameterValue.java index 930a4d30d..ddbbec91d 100644 --- a/src/main/java/io/github/classgraph/AnnotationParameterValue.java +++ b/src/main/java/io/github/classgraph/AnnotationParameterValue.java @@ -33,6 +33,8 @@ import java.util.Objects; import java.util.Set; +import nonapi.io.github.classgraph.utils.LogNode; + /** A wrapper used to pair annotation parameter names with annotation parameter values. */ public class AnnotationParameterValue extends ScanResultObject implements HasName, Comparable { @@ -145,9 +147,9 @@ void setScanResult(final ScanResult scanResult) { */ @Override protected void findReferencedClassInfo(final Map classNameToClassInfo, - final Set refdClassInfo) { + final Set refdClassInfo, final LogNode log) { if (value != null) { - value.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); + value.findReferencedClassInfo(classNameToClassInfo, refdClassInfo, log); } } diff --git a/src/main/java/io/github/classgraph/AnnotationParameterValueList.java b/src/main/java/io/github/classgraph/AnnotationParameterValueList.java index ac67629cf..f6dd71ef1 100644 --- a/src/main/java/io/github/classgraph/AnnotationParameterValueList.java +++ b/src/main/java/io/github/classgraph/AnnotationParameterValueList.java @@ -32,6 +32,8 @@ import java.util.Map; import java.util.Set; +import nonapi.io.github.classgraph.utils.LogNode; + /** A list of {@link AnnotationParameterValue} objects. */ public class AnnotationParameterValueList extends MappableInfoList { /** serialVersionUID */ @@ -90,11 +92,13 @@ public AnnotationParameterValueList( * the map from class name to {@link ClassInfo}. * @param refdClassInfo * the referenced class info + * @param log + * the log */ protected void findReferencedClassInfo(final Map classNameToClassInfo, - final Set refdClassInfo) { + final Set refdClassInfo, final LogNode log) { for (final AnnotationParameterValue apv : this) { - apv.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); + apv.findReferencedClassInfo(classNameToClassInfo, refdClassInfo, log); } } diff --git a/src/main/java/io/github/classgraph/ArrayClassInfo.java b/src/main/java/io/github/classgraph/ArrayClassInfo.java index 75c82eee2..56b512b94 100644 --- a/src/main/java/io/github/classgraph/ArrayClassInfo.java +++ b/src/main/java/io/github/classgraph/ArrayClassInfo.java @@ -31,6 +31,8 @@ import java.util.Map; import java.util.Set; +import nonapi.io.github.classgraph.utils.LogNode; + /** * Holds metadata about an array class. This class extends {@link ClassInfo} with additional methods relevant to * array classes, in particular {@link #getArrayTypeSignature()}, {@link #getTypeSignatureStr()}, @@ -225,8 +227,8 @@ public Class loadClass() { */ @Override protected void findReferencedClassInfo(final Map classNameToClassInfo, - final Set refdClassInfo) { - super.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); + final Set refdClassInfo, final LogNode log) { + super.findReferencedClassInfo(classNameToClassInfo, refdClassInfo, log); } // ------------------------------------------------------------------------------------------------------------- diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index ed9b42648..6fe4324b3 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -56,6 +56,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.LogNode; /** Holds metadata about a class encountered during a scan. */ public class ClassInfo extends ScanResultObject implements Comparable, HasName { @@ -190,6 +191,7 @@ public class ClassInfo extends ScanResultObject implements Comparable * @param classfileResource * the classfile resource */ + @SuppressWarnings("null") protected ClassInfo(final String name, final int classModifiers, final Resource classfileResource) { super(); this.name = name; @@ -2915,12 +2917,14 @@ void addReferencedClassNames(final Set refdClassNames) { * the map from class name to {@link ClassInfo}. * @param refdClassInfo * the referenced class info + * @param log + * the log */ @Override protected void findReferencedClassInfo(final Map classNameToClassInfo, - final Set refdClassInfo) { + final Set refdClassInfo, final LogNode log) { // Add this class to the set of references - super.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); + super.findReferencedClassInfo(classNameToClassInfo, refdClassInfo, log); if (this.referencedClassNames != null) { for (final String refdClassName : this.referencedClassNames) { final ClassInfo classInfo = ClassInfo.getOrCreateClassInfo(refdClassName, classNameToClassInfo); @@ -2928,19 +2932,21 @@ protected void findReferencedClassInfo(final Map classNameToC refdClassInfo.add(classInfo); } } - getMethodInfo().findReferencedClassInfo(classNameToClassInfo, refdClassInfo); - getFieldInfo().findReferencedClassInfo(classNameToClassInfo, refdClassInfo); - getAnnotationInfo().findReferencedClassInfo(classNameToClassInfo, refdClassInfo); + getMethodInfo().findReferencedClassInfo(classNameToClassInfo, refdClassInfo, log); + getFieldInfo().findReferencedClassInfo(classNameToClassInfo, refdClassInfo, log); + getAnnotationInfo().findReferencedClassInfo(classNameToClassInfo, refdClassInfo, log); if (annotationDefaultParamValues != null) { - annotationDefaultParamValues.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); + annotationDefaultParamValues.findReferencedClassInfo(classNameToClassInfo, refdClassInfo, log); } try { final ClassTypeSignature classSig = getTypeSignature(); if (classSig != null) { - classSig.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); + classSig.findReferencedClassInfo(classNameToClassInfo, refdClassInfo, log); + } + } catch (final IllegalArgumentException e) { + if (log != null) { + log.log("Illegal type signature for class " + getClassName() + ": " + getTypeSignatureStr()); } - } catch (final Exception e) { - // Ignore } } diff --git a/src/main/java/io/github/classgraph/ClassTypeSignature.java b/src/main/java/io/github/classgraph/ClassTypeSignature.java index a02812aaf..ac70756f5 100644 --- a/src/main/java/io/github/classgraph/ClassTypeSignature.java +++ b/src/main/java/io/github/classgraph/ClassTypeSignature.java @@ -40,6 +40,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.LogNode; /** A class type signature (called "ClassSignature" in the classfile documentation). */ public final class ClassTypeSignature extends HierarchicalTypeSignature { @@ -184,7 +185,7 @@ protected void findReferencedClassNames(final Set refdClassNames) { */ @Override protected void findReferencedClassInfo(final Map classNameToClassInfo, - final Set refdClassInfo) { + final Set refdClassInfo, final LogNode log) { final Set refdClassNames = new HashSet<>(); findReferencedClassNames(refdClassNames); for (final String refdClassName : refdClassNames) { diff --git a/src/main/java/io/github/classgraph/FieldInfo.java b/src/main/java/io/github/classgraph/FieldInfo.java index 275bd53ef..d6b7f6d57 100644 --- a/src/main/java/io/github/classgraph/FieldInfo.java +++ b/src/main/java/io/github/classgraph/FieldInfo.java @@ -40,6 +40,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.LogNode; /** * Holds metadata about fields of a class encountered during a scan. All values are taken directly out of the @@ -471,26 +472,32 @@ void setScanResult(final ScanResult scanResult) { */ @Override protected void findReferencedClassInfo(final Map classNameToClassInfo, - final Set refdClassInfo) { + final Set refdClassInfo, final LogNode log) { try { - final TypeSignature methodSig = getTypeSignature(); - if (methodSig != null) { - methodSig.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); + final TypeSignature fieldSig = getTypeSignature(); + if (fieldSig != null) { + fieldSig.findReferencedClassInfo(classNameToClassInfo, refdClassInfo, log); + } + } catch (final IllegalArgumentException e) { + if (log != null) { + log.log("Illegal type signature for field " + getClassName() + "." + getName() + ": " + + getTypeSignatureStr()); } - } catch (final Exception e) { - // Ignore } try { - final TypeSignature methodDesc = getTypeDescriptor(); - if (methodDesc != null) { - methodDesc.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); + final TypeSignature fieldDesc = getTypeDescriptor(); + if (fieldDesc != null) { + fieldDesc.findReferencedClassInfo(classNameToClassInfo, refdClassInfo, log); + } + } catch (final IllegalArgumentException e) { + if (log != null) { + log.log("Illegal type descriptor for field " + getClassName() + "." + getName() + ": " + + getTypeDescriptorStr()); } - } catch (final Exception e) { - // Ignore } if (annotationInfo != null) { for (final AnnotationInfo ai : annotationInfo) { - ai.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); + ai.findReferencedClassInfo(classNameToClassInfo, refdClassInfo, log); } } } diff --git a/src/main/java/io/github/classgraph/FieldInfoList.java b/src/main/java/io/github/classgraph/FieldInfoList.java index 2c100aa0c..efd3b8468 100644 --- a/src/main/java/io/github/classgraph/FieldInfoList.java +++ b/src/main/java/io/github/classgraph/FieldInfoList.java @@ -32,6 +32,8 @@ import java.util.Map; import java.util.Set; +import nonapi.io.github.classgraph.utils.LogNode; + /** A list of {@link FieldInfo} objects. */ public class FieldInfoList extends MappableInfoList { /** serialVersionUID */ @@ -88,11 +90,13 @@ public FieldInfoList(final Collection fieldInfoCollection) { * the map from class name to {@link ClassInfo}. * @param refdClassInfo * the referenced class info + * @param log + * the log */ protected void findReferencedClassInfo(final Map classNameToClassInfo, - final Set refdClassInfo) { + final Set refdClassInfo, final LogNode log) { for (final FieldInfo fi : this) { - fi.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); + fi.findReferencedClassInfo(classNameToClassInfo, refdClassInfo, log); } } diff --git a/src/main/java/io/github/classgraph/GraphvizDotfileGenerator.java b/src/main/java/io/github/classgraph/GraphvizDotfileGenerator.java index 9d02d62a5..c6469f56b 100644 --- a/src/main/java/io/github/classgraph/GraphvizDotfileGenerator.java +++ b/src/main/java/io/github/classgraph/GraphvizDotfileGenerator.java @@ -573,7 +573,7 @@ static String generateGraphVizDotFile(final ClassInfoList classInfoList, final f if (showFieldTypeDependencyEdges && classNode.fieldInfo != null) { for (final FieldInfo fi : classNode.fieldInfo) { - for (final ClassInfo referencedFieldType : fi.findReferencedClassInfo()) { + for (final ClassInfo referencedFieldType : fi.findReferencedClassInfo(/* log = */ null)) { if (allVisibleNodes.contains(referencedFieldType.getName())) { // class --[ ] field type (open box) buf.append(" \"").append(referencedFieldType.getName()).append("\" -> \"") @@ -586,7 +586,7 @@ static String generateGraphVizDotFile(final ClassInfoList classInfoList, final f if (showMethodTypeDependencyEdges && classNode.methodInfo != null) { for (final MethodInfo mi : classNode.methodInfo) { - for (final ClassInfo referencedMethodType : mi.findReferencedClassInfo()) { + for (final ClassInfo referencedMethodType : mi.findReferencedClassInfo(/* log = */ null)) { if (allVisibleNodes.contains(referencedMethodType.getName())) { // class --[#] field type (open box) buf.append(" \"").append(referencedMethodType.getName()).append("\" -> \"") diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index 3f43ccaaf..dd6a06d18 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -42,6 +42,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.LogNode; /** * Holds metadata about methods of a class encountered during a scan. All values are taken directly out of the @@ -784,33 +785,39 @@ void setScanResult(final ScanResult scanResult) { */ @Override protected void findReferencedClassInfo(final Map classNameToClassInfo, - final Set refdClassInfo) { + final Set refdClassInfo, final LogNode log) { try { final MethodTypeSignature methodSig = getTypeSignature(); if (methodSig != null) { - methodSig.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); + methodSig.findReferencedClassInfo(classNameToClassInfo, refdClassInfo, log); + } + } catch (final IllegalArgumentException e) { + if (log != null) { + log.log("Illegal type signature for method " + getClassName() + "." + getName() + ": " + + getTypeSignatureStr()); } - } catch (final Exception e) { - // Ignore } try { final MethodTypeSignature methodDesc = getTypeDescriptor(); if (methodDesc != null) { - methodDesc.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); + methodDesc.findReferencedClassInfo(classNameToClassInfo, refdClassInfo, log); + } + } catch (final IllegalArgumentException e) { + if (log != null) { + log.log("Illegal type descriptor for method " + getClassName() + "." + getName() + ": " + + getTypeDescriptorStr()); } - } catch (final Exception e) { - // Ignore } if (annotationInfo != null) { for (final AnnotationInfo ai : annotationInfo) { - ai.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); + ai.findReferencedClassInfo(classNameToClassInfo, refdClassInfo, log); } } for (final MethodParameterInfo mpi : getParameterInfo()) { final AnnotationInfo[] aiArr = mpi.annotationInfo; if (aiArr != null) { for (final AnnotationInfo ai : aiArr) { - ai.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); + ai.findReferencedClassInfo(classNameToClassInfo, refdClassInfo, log); } } } diff --git a/src/main/java/io/github/classgraph/MethodInfoList.java b/src/main/java/io/github/classgraph/MethodInfoList.java index 5d8495901..3eed537d4 100644 --- a/src/main/java/io/github/classgraph/MethodInfoList.java +++ b/src/main/java/io/github/classgraph/MethodInfoList.java @@ -33,6 +33,8 @@ import java.util.Map; import java.util.Set; +import nonapi.io.github.classgraph.utils.LogNode; + /** A list of {@link MethodInfo} objects. */ public class MethodInfoList extends InfoList { /** serialVersionUID */ @@ -88,11 +90,13 @@ public MethodInfoList(final Collection methodInfoCollection) { * the map from class name to {@link ClassInfo}. * @param refdClassInfo * the referenced class info + * @param log + * the log */ protected void findReferencedClassInfo(final Map classNameToClassInfo, - final Set refdClassInfo) { + final Set refdClassInfo, final LogNode log) { for (final MethodInfo mi : this) { - mi.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); + mi.findReferencedClassInfo(classNameToClassInfo, refdClassInfo, log); } } diff --git a/src/main/java/io/github/classgraph/MethodTypeSignature.java b/src/main/java/io/github/classgraph/MethodTypeSignature.java index 979b9e091..9a45ca164 100644 --- a/src/main/java/io/github/classgraph/MethodTypeSignature.java +++ b/src/main/java/io/github/classgraph/MethodTypeSignature.java @@ -38,6 +38,7 @@ import io.github.classgraph.Classfile.TypePathNode; import nonapi.io.github.classgraph.types.ParseException; import nonapi.io.github.classgraph.types.Parser; +import nonapi.io.github.classgraph.utils.LogNode; /** A method type signature (called "MethodSignature" in the classfile documentation). */ public final class MethodTypeSignature extends HierarchicalTypeSignature { @@ -229,7 +230,7 @@ protected void findReferencedClassNames(final Set refdClassNames) { */ @Override protected void findReferencedClassInfo(final Map classNameToClassInfo, - final Set refdClassInfo) { + final Set refdClassInfo, final LogNode log) { final Set refdClassNames = new HashSet<>(); findReferencedClassNames(refdClassNames); for (final String refdClassName : refdClassNames) { diff --git a/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java b/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java index 3ad0ec083..a18f175e0 100644 --- a/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java +++ b/src/main/java/io/github/classgraph/ObjectTypedValueWrapper.java @@ -34,6 +34,8 @@ import java.util.Objects; import java.util.Set; +import nonapi.io.github.classgraph.utils.LogNode; + /** A union type, used for typesafe serialization/deserialization to/from JSON. Only one field is ever set. */ class ObjectTypedValueWrapper extends ScanResultObject { // Parameter value is split into different fields by type, so that serialization and deserialization @@ -555,19 +557,19 @@ void setScanResult(final ScanResult scanResult) { */ @Override protected void findReferencedClassInfo(final Map classNameToClassInfo, - final Set refdClassInfo) { + final Set refdClassInfo, final LogNode log) { if (annotationEnumValue != null) { - annotationEnumValue.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); + annotationEnumValue.findReferencedClassInfo(classNameToClassInfo, refdClassInfo, log); } else if (annotationClassRef != null) { final ClassInfo classInfo = annotationClassRef.getClassInfo(); if (classInfo != null) { refdClassInfo.add(classInfo); } } else if (annotationInfo != null) { - annotationInfo.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); + annotationInfo.findReferencedClassInfo(classNameToClassInfo, refdClassInfo, log); } else if (objectArrayValue != null) { for (final ObjectTypedValueWrapper item : objectArrayValue) { - item.findReferencedClassInfo(classNameToClassInfo, refdClassInfo); + item.findReferencedClassInfo(classNameToClassInfo, refdClassInfo, log); } } } diff --git a/src/main/java/io/github/classgraph/ScanResult.java b/src/main/java/io/github/classgraph/ScanResult.java index ac8a6435a..f54cdfd26 100644 --- a/src/main/java/io/github/classgraph/ScanResult.java +++ b/src/main/java/io/github/classgraph/ScanResult.java @@ -259,7 +259,7 @@ static void init() { this.topLevelLog = topLevelLog; if (classNameToClassInfo != null) { - indexResourcesAndClassInfo(); + indexResourcesAndClassInfo(topLevelLog); } if (classNameToClassInfo != null) { @@ -299,8 +299,13 @@ static void init() { nonClosedWeakReferences.add(this.weakReference); } - /** Index {@link Resource} and {@link ClassInfo} objects. */ - private void indexResourcesAndClassInfo() { + /** + * Index {@link Resource} and {@link ClassInfo} objects. + * + * @param log + * the log + */ + private void indexResourcesAndClassInfo(final LogNode log) { // Add backrefs from Info objects back to this ScanResult final Collection allClassInfo = classNameToClassInfo.values(); for (final ClassInfo classInfo : allClassInfo) { @@ -312,7 +317,7 @@ private void indexResourcesAndClassInfo() { if (scanSpec.enableInterClassDependencies) { for (final ClassInfo ci : new ArrayList<>(classNameToClassInfo.values())) { final Set refdClassesFiltered = new HashSet<>(); - for (final ClassInfo refdClassInfo : ci.findReferencedClassInfo()) { + for (final ClassInfo refdClassInfo : ci.findReferencedClassInfo(log)) { // Don't add self-references, or references to Object if (refdClassInfo != null && !ci.equals(refdClassInfo) && !refdClassInfo.getName().equals("java.lang.Object") @@ -1348,7 +1353,7 @@ public static ScanResult fromJSON(final String json) { } // Index Resource and ClassInfo objects - scanResult.indexResourcesAndClassInfo(); + scanResult.indexResourcesAndClassInfo(/* log = */ null); scanResult.isObtainedFromDeserialization = true; return scanResult; diff --git a/src/main/java/io/github/classgraph/ScanResultObject.java b/src/main/java/io/github/classgraph/ScanResultObject.java index 8200ed91f..97e24a528 100644 --- a/src/main/java/io/github/classgraph/ScanResultObject.java +++ b/src/main/java/io/github/classgraph/ScanResultObject.java @@ -32,6 +32,8 @@ import java.util.Map; import java.util.Set; +import nonapi.io.github.classgraph.utils.LogNode; + /** * A superclass of objects accessible from a {@link ScanResult} that are associated with a {@link ClassInfo} object. */ @@ -60,12 +62,14 @@ void setScanResult(final ScanResult scanResult) { /** * Get {@link ClassInfo} objects for any classes referenced by this object. * + * @param log + * the log * @return the referenced class info. */ - final Set findReferencedClassInfo() { + final Set findReferencedClassInfo(final LogNode log) { final Set refdClassInfo = new LinkedHashSet<>(); if (scanResult != null) { - findReferencedClassInfo(scanResult.classNameToClassInfo, refdClassInfo); + findReferencedClassInfo(scanResult.classNameToClassInfo, refdClassInfo, log); } return refdClassInfo; } @@ -77,9 +81,11 @@ final Set findReferencedClassInfo() { * the map from class name to {@link ClassInfo}. * @param refdClassInfo * the referenced class info + * @param log + * the log */ protected void findReferencedClassInfo(final Map classNameToClassInfo, - final Set refdClassInfo) { + final Set refdClassInfo, final LogNode log) { final ClassInfo ci = getClassInfo(); if (ci != null) { refdClassInfo.add(ci); diff --git a/src/main/java/io/github/classgraph/TypeSignature.java b/src/main/java/io/github/classgraph/TypeSignature.java index d143ee8e4..4e8b96b68 100644 --- a/src/main/java/io/github/classgraph/TypeSignature.java +++ b/src/main/java/io/github/classgraph/TypeSignature.java @@ -36,6 +36,7 @@ import io.github.classgraph.Classfile.TypePathNode; import nonapi.io.github.classgraph.types.ParseException; import nonapi.io.github.classgraph.types.Parser; +import nonapi.io.github.classgraph.utils.LogNode; /** * A type signature for a reference type or base type. Subclasses are {@link ReferenceTypeSignature} (whose own @@ -71,7 +72,7 @@ protected void findReferencedClassNames(final Set refdClassNames) { */ @Override final protected void findReferencedClassInfo(final Map classNameToClassInfo, - final Set refdClassInfo) { + final Set refdClassInfo, final LogNode log) { final Set refdClassNames = new HashSet<>(); findReferencedClassNames(refdClassNames); for (final String refdClassName : refdClassNames) { diff --git a/src/main/java/io/github/classgraph/TypeVariableSignature.java b/src/main/java/io/github/classgraph/TypeVariableSignature.java index b616221e2..d8a46a4ee 100644 --- a/src/main/java/io/github/classgraph/TypeVariableSignature.java +++ b/src/main/java/io/github/classgraph/TypeVariableSignature.java @@ -104,7 +104,7 @@ public TypeParameter resolve() { ClassTypeSignature containingClassSignature = null; try { containingClassSignature = containingClassInfo.getTypeSignature(); - } catch (Exception e) { + } catch (final Exception e) { // Ignore } if (containingClassSignature != null && containingClassSignature.typeParameters != null From 99241e6b18e524b49fd2cb8fd4662a5b1f9dcb54 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Jan 2021 06:02:36 -0700 Subject: [PATCH 1031/1778] Document throwing of IllegalArgumentException by getTypeSignature() --- src/main/java/io/github/classgraph/ClassInfo.java | 4 ++++ src/main/java/io/github/classgraph/FieldInfo.java | 4 ++++ src/main/java/io/github/classgraph/MethodInfo.java | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/src/main/java/io/github/classgraph/ClassInfo.java b/src/main/java/io/github/classgraph/ClassInfo.java index 6fe4324b3..271c5aa7c 100644 --- a/src/main/java/io/github/classgraph/ClassInfo.java +++ b/src/main/java/io/github/classgraph/ClassInfo.java @@ -2626,6 +2626,10 @@ ClassInfoList getClassesWithFieldAnnotationDirectOnly() { * * @return The parsed type signature for the class, including any generic type parameters, or null if not * available (probably indicating the class is not generic). + * @throws IllegalArgumentException + * if the class 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 ClassTypeSignature getTypeSignature() { if (typeSignatureStr == null) { diff --git a/src/main/java/io/github/classgraph/FieldInfo.java b/src/main/java/io/github/classgraph/FieldInfo.java index d6b7f6d57..f98734c86 100644 --- a/src/main/java/io/github/classgraph/FieldInfo.java +++ b/src/main/java/io/github/classgraph/FieldInfo.java @@ -246,6 +246,10 @@ public String getTypeDescriptorStr() { * instead. * * @return The parsed type signature for the field, or null if not available. + * @throws IllegalArgumentException + * if the field 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 TypeSignature getTypeSignature() { if (typeSignatureStr == null) { diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index dd6a06d18..7006986ab 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -239,6 +239,10 @@ public String getTypeDescriptorStr() { * instead. * * @return The parsed type signature for the method, or null if not available. + * @throws IllegalArgumentException + * if the method 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 MethodTypeSignature getTypeSignature() { if (typeSignature == null && typeSignatureStr != null) { From 9a90f5be52fab864d4871ac9c751b09f5e4aa496 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Jan 2021 06:17:12 -0700 Subject: [PATCH 1032/1778] Add comment --- src/main/java/io/github/classgraph/MethodInfo.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/io/github/classgraph/MethodInfo.java b/src/main/java/io/github/classgraph/MethodInfo.java index 7006986ab..577360ebb 100644 --- a/src/main/java/io/github/classgraph/MethodInfo.java +++ b/src/main/java/io/github/classgraph/MethodInfo.java @@ -479,6 +479,11 @@ public MethodParameterInfo[] getParameterInfo() { // 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." + String[] paramNamesAligned = null; if (parameterNames != null && numParams > 0) { if (parameterNames.length == numParams) { From ebf9542ec02e857083b31fa47016ffcd984d0553 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Jan 2021 06:50:26 -0700 Subject: [PATCH 1033/1778] Improve test --- .../classgraph/issues/issue495/Issue495Test.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/test/java/io/github/classgraph/issues/issue495/Issue495Test.java b/src/test/java/io/github/classgraph/issues/issue495/Issue495Test.java index 436616a85..51d65aa0b 100644 --- a/src/test/java/io/github/classgraph/issues/issue495/Issue495Test.java +++ b/src/test/java/io/github/classgraph/issues/issue495/Issue495Test.java @@ -34,11 +34,11 @@ import java.io.IOException; import java.net.URL; import java.net.URLClassLoader; -import java.util.List; import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; +import io.github.classgraph.ClassInfoList; import io.github.classgraph.ScanResult; /** @@ -64,10 +64,10 @@ public void testScalaTypeSignatures() throws Exception { .acceptPackages("scalapackage") // .overrideClassLoaders(classLoader) // .scan()) { - final List classNames = scanResult // - .getAllClasses() // - .getNames(); - assertThat(classNames).containsOnly("scalapackage.ScalaClass"); + final ClassInfoList allClasses = scanResult.getAllClasses(); + assertThat(allClasses.getNames()).containsOnly("scalapackage.ScalaClass"); + Class scalaClassClass = allClasses.get(0).loadClass(); + assertThat(scalaClassClass).isNotNull(); } } } From ddcd7e6b8ef31bbe40e2706a15d0e6ce4b345c68 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Jan 2021 07:22:43 -0700 Subject: [PATCH 1034/1778] Support @throws annotation on Scala classes (#495) --- .../github/classgraph/ClassTypeSignature.java | 80 ++++++++++++++++++- .../issues/issue495/Issue495Test.java | 8 +- 2 files changed, 82 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassTypeSignature.java b/src/main/java/io/github/classgraph/ClassTypeSignature.java index ac70756f5..3ac60c937 100644 --- a/src/main/java/io/github/classgraph/ClassTypeSignature.java +++ b/src/main/java/io/github/classgraph/ClassTypeSignature.java @@ -57,6 +57,12 @@ public final class ClassTypeSignature extends HierarchicalTypeSignature { /** The superinterface signatures. */ private final List superinterfaceSignatures; + /** + * The throws signatures (usually null). These are only present in Scala classes, if the class is marked up with + * {@code @throws}, and they violate the classfile spec (#495), but we parse them anyway. + */ + private final List throwsSignatures; + // ------------------------------------------------------------------------------------------------------------- /** @@ -70,15 +76,19 @@ public final class ClassTypeSignature extends HierarchicalTypeSignature { * The superclass signature. * @param superinterfaceSignatures * The superinterface signature(s). + * @param throwsSignatures + * the throws signatures (these are actually invalid, but can be added by Scala: #495). Usually null. */ private ClassTypeSignature(final ClassInfo classInfo, final List typeParameters, final ClassRefTypeSignature superclassSignature, - final List superinterfaceSignatures) { + final List superinterfaceSignatures, + final List throwsSignatures) { super(); this.classInfo = classInfo; this.typeParameters = typeParameters; this.superclassSignature = superclassSignature; this.superinterfaceSignatures = superinterfaceSignatures; + this.throwsSignatures = throwsSignatures; } // ------------------------------------------------------------------------------------------------------------- @@ -111,6 +121,16 @@ public List getSuperinterfaceSignatures() { return superinterfaceSignatures; } + /** + * Gets the throws signatures. These are invalid according to the classfile spec (so this method is currently + * non-public), but they are added by the Scala compiler. + * + * @return the throws signatures + */ + List getThrowsSignatures() { + return throwsSignatures; + } + @Override protected void addTypeAnnotation(final List typePath, final AnnotationInfo annotationInfo) { // Individual parts of a class' type each have their own addTypeAnnotation methods @@ -173,6 +193,11 @@ protected void findReferencedClassNames(final Set refdClassNames) { for (final ClassRefTypeSignature typeSignature : superinterfaceSignatures) { typeSignature.findReferencedClassNames(refdClassNames); } + if (throwsSignatures != null) { + for (final ClassRefOrTypeVariableSignature typeSignature : throwsSignatures) { + typeSignature.findReferencedClassNames(refdClassNames); + } + } } /** @@ -245,11 +270,22 @@ public boolean equals(final Object obj) { void toStringInternal(final String className, final boolean useSimpleNames, final int modifiers, final boolean isAnnotation, final boolean isInterface, final AnnotationInfoList annotationsToExclude, final StringBuilder buf) { + if (throwsSignatures != null) { + for (final ClassRefOrTypeVariableSignature throwsSignature : throwsSignatures) { + if (buf.length() > 0) { + buf.append(' '); + } + buf.append("@throws(classOf[" + throwsSignature + "])"); + } + } if (modifiers != 0) { - TypeUtils.modifiersToString(modifiers, ModifierType.CLASS, /* ignored */ false, buf); if (buf.length() > 0) { buf.append(' '); } + TypeUtils.modifiersToString(modifiers, ModifierType.CLASS, /* ignored */ false, buf); + } + if (buf.length() > 0) { + buf.append(' '); } buf.append(isAnnotation ? "@interface" : isInterface ? "interface" : (modifiers & 0x4000) != 0 ? "enum" : "class"); @@ -330,6 +366,10 @@ static ClassTypeSignature parse(final String typeDescriptor, final ClassInfo cla if (parser.hasMore()) { superinterfaceSignatures = new ArrayList<>(); while (parser.hasMore()) { + if (parser.peek() == '^') { + // Illegal "throws" suffix in class type signature -- fall through + break; + } final ClassRefTypeSignature superinterfaceSignature = ClassRefTypeSignature.parse(parser, definingClassNameNull); if (superinterfaceSignature == null) { @@ -340,9 +380,43 @@ static ClassTypeSignature parse(final String typeDescriptor, final ClassInfo cla } else { superinterfaceSignatures = Collections.emptyList(); } + List throwsSignatures; + if (parser.peek() == '^') { + // There is an illegal "throws" suffix at the end of this class type signature. + // Scala adds these if you tag a class with "@throws" (#495). + // Classes with this sort of type signature are rejected by javac and javap, and they will throw + // GenericSignatureFormatError if you call getClass().getGenericSuperclass() on a subclass. + // But the JVM ignores type signatures due to type erasure, and Scala seems to rely on this + // -- or at the very least, the Scala team never noticed the issue, because the classes work + // fine at runtime if you live in a Scala-only world. + // Since this issue is probably widespread in the Scala world, it's probably better to accept + // these invalid type signatures, and actually parse out any "throws" suffixes, rather than + // throwing an exception and refusing to parse the type signature. + throwsSignatures = new ArrayList<>(); + while (parser.peek() == '^') { + parser.expect('^'); + final ClassRefTypeSignature classTypeSignature = ClassRefTypeSignature.parse(parser, + classInfo.getName()); + if (classTypeSignature != null) { + throwsSignatures.add(classTypeSignature); + } else { + final TypeVariableSignature typeVariableSignature = TypeVariableSignature.parse(parser, + classInfo.getName()); + if (typeVariableSignature != null) { + throwsSignatures.add(typeVariableSignature); + } else { + throw new ParseException(parser, "Missing type variable signature"); + } + } + } + } else { + throwsSignatures = null; + } if (parser.hasMore()) { throw new ParseException(parser, "Extra characters at end of type descriptor"); } - return new ClassTypeSignature(classInfo, typeParameters, superclassSignature, superinterfaceSignatures); + final ClassTypeSignature classTypeSignature = new ClassTypeSignature(classInfo, typeParameters, + superclassSignature, superinterfaceSignatures, throwsSignatures); + return classTypeSignature; } } \ No newline at end of file diff --git a/src/test/java/io/github/classgraph/issues/issue495/Issue495Test.java b/src/test/java/io/github/classgraph/issues/issue495/Issue495Test.java index 51d65aa0b..695525227 100644 --- a/src/test/java/io/github/classgraph/issues/issue495/Issue495Test.java +++ b/src/test/java/io/github/classgraph/issues/issue495/Issue495Test.java @@ -38,6 +38,7 @@ import org.junit.jupiter.api.Test; import io.github.classgraph.ClassGraph; +import io.github.classgraph.ClassInfo; import io.github.classgraph.ClassInfoList; import io.github.classgraph.ScanResult; @@ -56,9 +57,7 @@ public void testScalaTypeSignatures() throws Exception { final URL resourceURL = Issue495Test.class.getClassLoader().getResource("scalapackage.zip"); assertThat(resourceURL).isNotNull(); assertThat(new File(resourceURL.toURI())).canRead(); - final ClassLoader classLoader = new URLClassLoader(new URL[] { resourceURL }, null); - try (ScanResult scanResult = new ClassGraph() // .enableClassInfo().enableInterClassDependencies() // .acceptPackages("scalapackage") // @@ -66,7 +65,10 @@ public void testScalaTypeSignatures() throws Exception { .scan()) { final ClassInfoList allClasses = scanResult.getAllClasses(); assertThat(allClasses.getNames()).containsOnly("scalapackage.ScalaClass"); - Class scalaClassClass = allClasses.get(0).loadClass(); + final ClassInfo scalaClassInfo = allClasses.get(0); + assertThat(scalaClassInfo.getTypeSignature()).isNotNull(); + System.out.println(scalaClassInfo.getTypeSignature()); + final Class scalaClassClass = scalaClassInfo.loadClass(); assertThat(scalaClassClass).isNotNull(); } } From 32d9b2af367cc95e5e2bb1872a09a315bd1e6ce2 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Jan 2021 07:24:22 -0700 Subject: [PATCH 1035/1778] Remove println --- .../java/io/github/classgraph/issues/issue495/Issue495Test.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/java/io/github/classgraph/issues/issue495/Issue495Test.java b/src/test/java/io/github/classgraph/issues/issue495/Issue495Test.java index 695525227..02855031d 100644 --- a/src/test/java/io/github/classgraph/issues/issue495/Issue495Test.java +++ b/src/test/java/io/github/classgraph/issues/issue495/Issue495Test.java @@ -67,7 +67,6 @@ public void testScalaTypeSignatures() throws Exception { assertThat(allClasses.getNames()).containsOnly("scalapackage.ScalaClass"); final ClassInfo scalaClassInfo = allClasses.get(0); assertThat(scalaClassInfo.getTypeSignature()).isNotNull(); - System.out.println(scalaClassInfo.getTypeSignature()); final Class scalaClassClass = scalaClassInfo.loadClass(); assertThat(scalaClassClass).isNotNull(); } From 1ece77bafb0b08627b77ceba62b0800c72c0f527 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Jan 2021 11:59:42 -0700 Subject: [PATCH 1036/1778] Revise toString method (#495) --- src/main/java/io/github/classgraph/ClassTypeSignature.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClassTypeSignature.java b/src/main/java/io/github/classgraph/ClassTypeSignature.java index 3ac60c937..89a150bc5 100644 --- a/src/main/java/io/github/classgraph/ClassTypeSignature.java +++ b/src/main/java/io/github/classgraph/ClassTypeSignature.java @@ -123,7 +123,7 @@ public List getSuperinterfaceSignatures() { /** * Gets the throws signatures. These are invalid according to the classfile spec (so this method is currently - * non-public), but they are added by the Scala compiler. + * non-public), but may be added by the Scala compiler. (See bug #495.) * * @return the throws signatures */ @@ -275,7 +275,7 @@ void toStringInternal(final String className, final boolean useSimpleNames, fina if (buf.length() > 0) { buf.append(' '); } - buf.append("@throws(classOf[" + throwsSignature + "])"); + buf.append("@throws(" + throwsSignature + ")"); } } if (modifiers != 0) { From 83d95264992ce6d298dd64ebd0935ff8c2517417 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Jan 2021 12:06:41 -0700 Subject: [PATCH 1037/1778] [maven-release-plugin] prepare release classgraph-4.8.102 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 44d8c16f8..dcffff187 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.102-SNAPSHOT + 4.8.102 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.98 + classgraph-4.8.102 From 12383190c80878dd7a2d05ca88a1cd908f94e6ac Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Wed, 27 Jan 2021 12:06:49 -0700 Subject: [PATCH 1038/1778] [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 dcffff187..a105c81b1 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.102 + 4.8.103-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.102 + classgraph-4.8.98 From 72f69b740833fe636a9782aebe7209951ec08524 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 Feb 2021 02:00:57 +0000 Subject: [PATCH 1039/1778] Bump junit-jupiter from 5.7.0 to 5.7.1 Bumps [junit-jupiter](https://github.com/junit-team/junit5) from 5.7.0 to 5.7.1. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.7.0...r5.7.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 a105c81b1..843e92dff 100644 --- a/pom.xml +++ b/pom.xml @@ -69,7 +69,7 @@ org.junit.jupiter junit-jupiter - 5.7.0 + 5.7.1 test From 49a3cddc533b3883cdccb5447018f194ec0daca0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 Feb 2021 02:01:00 +0000 Subject: [PATCH 1040/1778] Bump animal-sniffer-enforcer-rule from 1.19 to 1.20 Bumps [animal-sniffer-enforcer-rule](https://github.com/mojohaus/animal-sniffer) from 1.19 to 1.20. - [Release notes](https://github.com/mojohaus/animal-sniffer/releases) - [Commits](https://github.com/mojohaus/animal-sniffer/compare/animal-sniffer-parent-1.19...animal-sniffer-parent-1.20) Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 843e92dff..d46aff669 100644 --- a/pom.xml +++ b/pom.xml @@ -240,7 +240,7 @@ org.codehaus.mojo animal-sniffer-enforcer-rule - 1.19 + 1.20 From eb7b18515fc6ebd2a11b12c8ba9afad9fb78c2f1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Mar 2021 02:00:59 +0000 Subject: [PATCH 1041/1778] Bump jmh-core from 1.27 to 1.28 Bumps jmh-core from 1.27 to 1.28. Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d46aff669..f394d33c6 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ org.openjdk.jmh jmh-core - 1.27 + 1.28 test From 1c0cf02dff573299715a4737e5ab99796813e2a1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Mar 2021 02:01:00 +0000 Subject: [PATCH 1042/1778] Bump jmh-generator-annprocess from 1.27 to 1.28 Bumps jmh-generator-annprocess from 1.27 to 1.28. Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d46aff669..4421a49c4 100644 --- a/pom.xml +++ b/pom.xml @@ -81,7 +81,7 @@ org.openjdk.jmh jmh-generator-annprocess - 1.27 + 1.28 test From a84c1d731dbc47f499c99e3fa6e025f22631b032 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 22 Mar 2021 15:35:18 -0600 Subject: [PATCH 1043/1778] Don't duplicate package prefixes (possible fix for #505) --- .../classgraph/ClasspathElementZip.java | 31 ++++--------------- 1 file changed, 6 insertions(+), 25 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementZip.java b/src/main/java/io/github/classgraph/ClasspathElementZip.java index 779babfad..a39974072 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -309,8 +309,7 @@ void open(final WorkQueue workQueue, final LogNode log) * the path relative to package root * @return the resource */ - private Resource newResource(final FastZipEntry zipEntry, final String strippedPackageRootPrefix, - final String pathRelativeToPackageRoot) { + 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(); @@ -326,20 +325,10 @@ public String getPath() { @Override public String getPathRelativeToClasspathElement() { - return zipEntry.entryName; - } - - @Override - public URI getClasspathElementURI() { - final URI classpathElementURI = super.getClasspathElementURI(); - if (strippedPackageRootPrefix.isEmpty()) { - return classpathElementURI; + if (zipEntry.entryName.startsWith(packageRootPrefix)) { + return zipEntry.entryName.substring(packageRootPrefix.length()); } else { - try { - return new URI(classpathElementURI.toString() + "!/" + strippedPackageRootPrefix); - } catch (final URISyntaxException e) { - throw new IllegalArgumentException("Could not construct valid URI for resource"); - } + return zipEntry.entryName; } } @@ -574,8 +563,6 @@ void scanPaths(final LogNode log) { } // Strip the package root prefix from the relative path - // N.B. these semantics should mirror those in getResource() - String strippedPackageRootPrefix = ""; if (!packageRootPrefix.isEmpty()) { relativePath = relativePath.substring(packageRootPrefix.length()); } else { @@ -591,12 +578,6 @@ void scanPaths(final LogNode log) { : packageRoot; // Store package root for use by getAllURIs() strippedAutomaticPackageRootPrefixes.add(packageRootWithoutFinalSlash); - // - if (strippedPackageRootPrefix.isEmpty()) { - strippedPackageRootPrefix = packageRootWithoutFinalSlash; - } else { - strippedPackageRootPrefix += "/" + packageRootWithoutFinalSlash; - } } } } @@ -627,7 +608,7 @@ void scanPaths(final LogNode log) { } // Add the ZipEntry path as a Resource - final Resource resource = newResource(zipEntry, strippedPackageRootPrefix, relativePath); + final Resource resource = newResource(zipEntry, relativePath); if (relativePathToResource.putIfAbsent(relativePath, resource) == null) { // If resource is accepted if (parentMatchStatus == ScanSpecPathMatch.HAS_ACCEPTED_PATH_PREFIX @@ -699,7 +680,7 @@ URI getURI() { /** * Return URI for classpath element, plus URIs for any stripped nested automatic package root prefixes, e.g. - * "/BOOT-INF/classes". + * "!/BOOT-INF/classes". */ @Override List getAllURIs() { From 454a048258d5eca220a74be71d35da924b265a13 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 22 Mar 2021 15:47:32 -0600 Subject: [PATCH 1044/1778] Ensure underlying Resource is properly closed when InputStream is closed --- .../classgraph/ClasspathElementFileDir.java | 9 ++++++++- .../classgraph/ClasspathElementPathDir.java | 9 ++++++++- .../github/classgraph/ClasspathElementZip.java | 9 ++++++++- .../io/github/classgraph/fileslice/Slice.java | 17 ++++++++++++++++- .../classgraph/issues/issue400/Issue400.java | 2 +- 5 files changed, 41 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementFileDir.java b/src/main/java/io/github/classgraph/ClasspathElementFileDir.java index 21dfa4bfc..9e4de125d 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementFileDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementFileDir.java @@ -261,7 +261,14 @@ 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(); + inputStream = fileSlice.open(new Runnable() { + @Override + public void run() { + if (isOpen.getAndSet(false)) { + close(); + } + } + }); length = fileSlice.sliceLength; return inputStream; } diff --git a/src/main/java/io/github/classgraph/ClasspathElementPathDir.java b/src/main/java/io/github/classgraph/ClasspathElementPathDir.java index 7899bb437..c90f4ca87 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementPathDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementPathDir.java @@ -266,7 +266,14 @@ 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(); + inputStream = pathSlice.open(new Runnable() { + @Override + public void run() { + if (isOpen.getAndSet(false)) { + close(); + } + } + }); 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 a39974072..01eb1394a 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementZip.java +++ b/src/main/java/io/github/classgraph/ClasspathElementZip.java @@ -387,7 +387,14 @@ public InputStream open() throws IOException { "Resource is already open -- cannot open it again without first calling close()"); } try { - inputStream = zipEntry.getSlice().open(); + inputStream = zipEntry.getSlice().open(new Runnable() { + @Override + public void run() { + if (isOpen.getAndSet(false)) { + close(); + } + } + }); length = zipEntry.uncompressedSize; 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 a564bbc79..8a4f29c20 100644 --- a/src/main/java/nonapi/io/github/classgraph/fileslice/Slice.java +++ b/src/main/java/nonapi/io/github/classgraph/fileslice/Slice.java @@ -152,6 +152,19 @@ public abstract Slice slice(long offset, long length, boolean isDeflatedZipEntry * if an inflater cannot be created for this {@link Slice}. */ public InputStream open() throws IOException { + return open(null); + } + + /** + * 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. + * @return the input stream + * @throws IOException + * if an inflater cannot be created for this {@link Slice}. + */ + public InputStream open(final Runnable onClose) throws IOException { final InputStream rawInputStream = new InputStream() { RandomAccessReader randomAccessReader = randomAccessReader(); private long currOff; @@ -222,7 +235,9 @@ public boolean markSupported() { @Override public void close() { closed.getAndSet(true); - // Nothing to close + if (onClose != null) { + onClose.run(); + } } }; return isDeflatedZipEntry ? nestedJarHandler.openInflaterInputStream(rawInputStream) : rawInputStream; 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 3e3b87840..8cb370b6b 100644 --- a/src/test/perf/io/github/classgraph/issues/issue400/Issue400.java +++ b/src/test/perf/io/github/classgraph/issues/issue400/Issue400.java @@ -19,7 +19,7 @@ public class Issue400 { private static final long MB = 1024 * 1024; - private static final long MEMORY_TOLERANCE = 4 * MB; + private static final long MEMORY_TOLERANCE = 5 * MB; /** * @return used JVM heap size allocated in RAM From 5f8d22c55dc5231f97b024672c481b0693083540 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 22 Mar 2021 15:48:37 -0600 Subject: [PATCH 1045/1778] [maven-release-plugin] prepare release classgraph-4.8.103 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 3fa73d83e..76a9f20c5 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.103-SNAPSHOT + 4.8.103 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.98 + classgraph-4.8.103 From 2f0b681e847ab1a07feb4a8a4981385044e724dc Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Mon, 22 Mar 2021 15:50:40 -0600 Subject: [PATCH 1046/1778] [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 76a9f20c5..c207f1aeb 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.103 + 4.8.104-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. From 41e11716e85d24d76cc5731a5b57bb986862817f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 25 Mar 2021 02:00:48 +0000 Subject: [PATCH 1047/1778] Bump jmh-core from 1.28 to 1.29 Bumps jmh-core from 1.28 to 1.29. Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c207f1aeb..18c46c03b 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ org.openjdk.jmh jmh-core - 1.28 + 1.29 test From 2a78f88fb01ae79eb6631b824abf6cd5db3f4db8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 25 Mar 2021 02:00:50 +0000 Subject: [PATCH 1048/1778] Bump jmh-generator-annprocess from 1.28 to 1.29 Bumps jmh-generator-annprocess from 1.28 to 1.29. Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c207f1aeb..e436044ef 100644 --- a/pom.xml +++ b/pom.xml @@ -81,7 +81,7 @@ org.openjdk.jmh jmh-generator-annprocess - 1.28 + 1.29 test From aac9972dd7d83cabf33c7e042db37e7c466fc58f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 26 Mar 2021 01:05:04 -0600 Subject: [PATCH 1049/1778] Improve logging --- src/main/java/io/github/classgraph/Classfile.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/github/classgraph/Classfile.java b/src/main/java/io/github/classgraph/Classfile.java index 179037b76..3f301a10e 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -1981,7 +1981,7 @@ public void decorate(final ClassTypeSignature classTypeSignature) { subLog.log("Interfaces: " + StringUtils.join(", ", implementedInterfaces)); } if (classAnnotations != null) { - subLog.log("Class annotations: " + StringUtils.join(", ", classAnnotations.getNames())); + subLog.log("Class annotations: " + StringUtils.join(", ", classAnnotations)); } if (annotationParamDefaultValues != null) { for (final AnnotationParameterValue apv : annotationParamDefaultValues) { @@ -1990,12 +1990,12 @@ public void decorate(final ClassTypeSignature classTypeSignature) { } if (fieldInfoList != null) { for (final FieldInfo fieldInfo : fieldInfoList) { - subLog.log("Field: " + fieldInfo.getName()); + subLog.log("Field: " + fieldInfo); } } if (methodInfoList != null) { for (final MethodInfo methodInfo : methodInfoList) { - subLog.log("Method: " + methodInfo.getName()); + subLog.log("Method: " + methodInfo); } } if (typeSignatureStr != null) { @@ -2004,7 +2004,7 @@ public void decorate(final ClassTypeSignature classTypeSignature) { if (refdClassNames != null) { final List refdClassNamesSorted = new ArrayList<>(refdClassNames); CollectionUtils.sortIfNotEmpty(refdClassNamesSorted); - subLog.log("Referenced class names: " + StringUtils.join(", ", refdClassNamesSorted)); + subLog.log("Additional referenced class names: " + StringUtils.join(", ", refdClassNamesSorted)); } } From 273c92427824482f92ce2e1540619744cbd3e3ca Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 26 Mar 2021 01:17:17 -0600 Subject: [PATCH 1050/1778] generateGraphVizDotFileFromClassDependencies -> ...FromInterClass... --- .../java/io/github/classgraph/ClassInfoList.java | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/ClassInfoList.java b/src/main/java/io/github/classgraph/ClassInfoList.java index 8295c9160..7aa8526b1 100644 --- a/src/main/java/io/github/classgraph/ClassInfoList.java +++ b/src/main/java/io/github/classgraph/ClassInfoList.java @@ -595,7 +595,7 @@ public String generateGraphVizDotFileFromInterClassDependencies(final float size * if this {@link ClassInfoList} is empty or {@link ClassGraph#enableInterClassDependencies()} was * not called before scanning (since there would be nothing to graph). */ - public String generateGraphVizDotFileFromClassDependencies() { + public String generateGraphVizDotFileFromInterClassDependencies() { if (isEmpty()) { throw new IllegalArgumentException("List is empty"); } @@ -608,6 +608,20 @@ public String generateGraphVizDotFileFromClassDependencies() { scanSpec.enableExternalClasses); } + /** + * Deprecated: use {@link #generateGraphVizDotFileFromInterClassDependencies()} instead. + * + * @deprecated Use {@link #generateGraphVizDotFileFromInterClassDependencies()} instead. + * @return the GraphViz file contents. + * @throws IllegalArgumentException + * if this {@link ClassInfoList} is empty or {@link ClassGraph#enableInterClassDependencies()} was + * not called before scanning (since there would be nothing to graph). + */ + @Deprecated + public String generateGraphVizDotFileFromClassDependencies() { + return generateGraphVizDotFileFromInterClassDependencies(); + } + // ------------------------------------------------------------------------------------------------------------- /** From f926090d327f49e28dd07358ced02951a90e6b9b Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 26 Mar 2021 01:20:07 -0600 Subject: [PATCH 1051/1778] Add convenience method --- .../io/github/classgraph/ClassInfoList.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/main/java/io/github/classgraph/ClassInfoList.java b/src/main/java/io/github/classgraph/ClassInfoList.java index 7aa8526b1..5045baa5a 100644 --- a/src/main/java/io/github/classgraph/ClassInfoList.java +++ b/src/main/java/io/github/classgraph/ClassInfoList.java @@ -579,6 +579,39 @@ public String generateGraphVizDotFileFromInterClassDependencies(final float size includeExternalClasses); } + /** + * Generate a .dot file which can be fed into GraphViz for layout and visualization of the class graph. The + * returned graph shows inter-class dependencies only. The sizeX and sizeY parameters are the image output size + * to use (in inches) when GraphViz is asked to render the .dot file. You must have called + * {@link ClassGraph#enableInterClassDependencies()} before scanning to use this method. + * + *

+ * Equivalent to calling {@link #generateGraphVizDotFileFromInterClassDependencies(float, float, boolean)} with + * parameters of (10.5f, 8f, scanSpec.enableExternalClasses), where scanSpec.enableExternalClasses is true if + * {@link ClassGraph#enableExternalClasses()} was called before scanning. + * + * @param sizeX + * The GraphViz layout width in inches. + * @param sizeY + * The GraphViz layout width in inches. + * @return the GraphViz file contents. + * @throws IllegalArgumentException + * if this {@link ClassInfoList} is empty or {@link ClassGraph#enableInterClassDependencies()} was + * not called before scanning (since there would be nothing to graph). + */ + public String generateGraphVizDotFileFromInterClassDependencies(final float sizeX, final float sizeY) { + if (isEmpty()) { + throw new IllegalArgumentException("List is empty"); + } + final ScanSpec scanSpec = get(0).scanResult.scanSpec; + if (!scanSpec.enableInterClassDependencies) { + throw new IllegalArgumentException( + "Please call ClassGraph#enableInterClassDependencies() before #scan()"); + } + return GraphvizDotfileGenerator.generateGraphVizDotFileFromInterClassDependencies(this, sizeX, sizeY, + scanSpec.enableExternalClasses); + } + /** * Generate a .dot file which can be fed into GraphViz for layout and visualization of the class graph. The * returned graph shows inter-class dependencies only. The sizeX and sizeY parameters are the image output size From 6044fa4cd8e87508e27e299ba264526e609755b9 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 26 Mar 2021 01:38:59 -0600 Subject: [PATCH 1052/1778] [maven-release-plugin] prepare release classgraph-4.8.104 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 7317a1f3b..23aa433e1 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.104-SNAPSHOT + 4.8.104 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.103 + classgraph-4.8.104 From 046a86d5f74c140e9d010d39a44b02fe3b94c17c Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 26 Mar 2021 01:39:04 -0600 Subject: [PATCH 1053/1778] [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 23aa433e1..ec3deb6d8 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.104 + 4.8.105-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.104 + classgraph-4.8.103 From 770981d63485effb6e8fc9aec3a3f6667bdf01a4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Apr 2021 03:00:52 +0000 Subject: [PATCH 1054/1778] Bump actions/setup-java from v1.4.3 to v2 Bumps [actions/setup-java](https://github.com/actions/setup-java) from v1.4.3 to v2. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/v1.4.3...8764a52df183aa0ccea74521dfd9d506ffc7a19a) 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 6c990001e..c0b5c6014 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Set up JDK - uses: actions/setup-java@v1.4.3 + uses: actions/setup-java@v2 with: java-version: ${{ matrix.java }} - name: print Java version From 64de755b3e49488860da363ebd2bc9c2336d86ad Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 6 Apr 2021 11:32:07 -0600 Subject: [PATCH 1055/1778] Add JDK16; add "distribution: 'zulu'" --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c0b5c6014..250d30a72 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,12 +15,13 @@ jobs: fail-fast: false matrix: os: [ ubuntu-latest, windows-latest, macos-latest ] - java: [ '8', '11', '12', '13', '14', '15' ] + java: [ '8', '11', '12', '13', '14', '15', '16' ] steps: - uses: actions/checkout@v2 - name: Set up JDK uses: actions/setup-java@v2 with: + distribution: 'zulu' java-version: ${{ matrix.java }} - name: print Java version run: java -version From 7c50f7ea5037ab517c7a6fa08a364151a40b9f01 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Tue, 6 Apr 2021 11:33:29 -0600 Subject: [PATCH 1056/1778] Make CI work on branch "latest" --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 250d30a72..007fea6c8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,10 +3,10 @@ name: Java CI on: pull_request: branches: - - master + - latest push: branches: - - master + - latest jobs: build: From 24b41e128bc330d20e3071c8b245a5d52f6031e9 Mon Sep 17 00:00:00 2001 From: sullis Date: Sat, 17 Apr 2021 11:48:50 -0700 Subject: [PATCH 1057/1778] ci: enable Maven batch mode and no-transfer-progress --- .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 007fea6c8..3ef408cde 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,4 +26,4 @@ jobs: - name: print Java version run: java -version - name: Build with Maven - run: ./mvnw clean test + run: ./mvnw --no-transfer-progress -B clean test From 63bcc268e778d4099bee5f83fcbe836aa0e6eb43 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Apr 2021 02:00:55 +0000 Subject: [PATCH 1058/1778] Bump maven-release-plugin from 3.0.0-M1 to 3.0.0-M4 Bumps [maven-release-plugin](https://github.com/apache/maven-release) from 3.0.0-M1 to 3.0.0-M4. - [Release notes](https://github.com/apache/maven-release/releases) - [Commits](https://github.com/apache/maven-release/compare/maven-release-3.0.0-M1...maven-release-3.0.0-M4) Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ec3deb6d8..9c28afea9 100644 --- a/pom.xml +++ b/pom.xml @@ -204,7 +204,7 @@ org.apache.maven.plugins maven-release-plugin - 3.0.0-M1 + 3.0.0-M4 From e8a2a3e823c60850fa947ba9c0cefad01d807277 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 30 Apr 2021 17:49:27 -0600 Subject: [PATCH 1059/1778] getModifierStr -> getModifiersStr (for consistency with MethodInfo) --- src/main/java/io/github/classgraph/FieldInfo.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/FieldInfo.java b/src/main/java/io/github/classgraph/FieldInfo.java index f98734c86..519100b00 100644 --- a/src/main/java/io/github/classgraph/FieldInfo.java +++ b/src/main/java/io/github/classgraph/FieldInfo.java @@ -149,11 +149,22 @@ public ClassInfo getClassInfo() { // ------------------------------------------------------------------------------------------------------------- /** - * Get the field modifiers as a string, e.g. "public static final". For the modifier bits, call getModifiers(). + * Deprecated -- use {@link #getModifiersStr()} instead. * + * @deprecated Use {@link #getModifiersStr()} instead. * @return The field modifiers, as a string. */ + @Deprecated public String getModifierStr() { + return getModifiersStr(); + } + + /** + * Get the field modifiers as a string, e.g. "public static final". For the modifier bits, call getModifiers(). + * + * @return The field modifiers, as a string. + */ + public String getModifiersStr() { final StringBuilder buf = new StringBuilder(); TypeUtils.modifiersToString(modifiers, ModifierType.FIELD, /* ignored */ false, buf); return buf.toString(); From 3ec0d8e4b9f5c40288985e96627bb3778a4407f5 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 30 Apr 2021 17:50:05 -0600 Subject: [PATCH 1060/1778] getModifierStr -> getModifiersStr --- .../java/io/github/classgraph/GraphvizDotfileGenerator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/GraphvizDotfileGenerator.java b/src/main/java/io/github/classgraph/GraphvizDotfileGenerator.java index c6469f56b..37660145c 100644 --- a/src/main/java/io/github/classgraph/GraphvizDotfileGenerator.java +++ b/src/main/java/io/github/classgraph/GraphvizDotfileGenerator.java @@ -319,7 +319,7 @@ private static void labelClassNodeHTML(final ClassInfo ci, final String shape, f if (buf.charAt(buf.length() - 1) != ' ') { buf.append(' '); } - buf.append(fi.getModifierStr()); + buf.append(fi.getModifiersStr()); } // Field type From 18971a21c472b00111f100dfe53f6d640ce57a39 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 30 Apr 2021 17:51:12 -0600 Subject: [PATCH 1061/1778] Fix possible NPE when calling .verbose() due to FieldInfo/MethodInfo --- 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 3f301a10e..e24b8f5f5 100644 --- a/src/main/java/io/github/classgraph/Classfile.java +++ b/src/main/java/io/github/classgraph/Classfile.java @@ -1990,12 +1990,15 @@ public void decorate(final ClassTypeSignature classTypeSignature) { } if (fieldInfoList != null) { for (final FieldInfo fieldInfo : fieldInfoList) { - subLog.log("Field: " + fieldInfo); + final String modifierStr = fieldInfo.getModifiersStr(); + subLog.log("Field: " + modifierStr + (modifierStr.isEmpty() ? "" : " ") + fieldInfo.getName()); } } if (methodInfoList != null) { for (final MethodInfo methodInfo : methodInfoList) { - subLog.log("Method: " + methodInfo); + final String modifierStr = methodInfo.getModifiersStr(); + subLog.log( + "Method: " + modifierStr + (modifierStr.isEmpty() ? "" : " ") + methodInfo.getName()); } } if (typeSignatureStr != null) { From 5c0857e043dd77747748f3e66739c87dc5ae9c08 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 30 Apr 2021 18:18:01 -0600 Subject: [PATCH 1062/1778] Improve error logging --- .../fastzipfilereader/LogicalZipFile.java | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 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 a0325ef72..7d2127404 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java @@ -741,14 +741,32 @@ private void readCentralDirectory(final NestedJarHandler nestedJarHandler, final lastModifiedDateMSDOS = cenReader.readUnsignedShort(entOff + 14); } - if (compressedSize < 0 || pos < 0) { + if (compressedSize < 0) { + if (log != null) { + log.log("Skipping zip entry with invalid compressed size (" + compressedSize + "): " + + entryNameSanitized); + } + continue; + } + if (uncompressedSize < 0) { + if (log != null) { + log.log("Skipping zip entry with invalid uncompressed size (" + uncompressedSize + "): " + + entryNameSanitized); + } + continue; + } + if (pos < 0) { + if (log != null) { + log.log("Skipping zip entry with invalid pos (" + pos + "): " + entryNameSanitized); + } continue; } final long locHeaderPos = locPos + pos; if (locHeaderPos < 0) { if (log != null) { - log.log("Skipping zip entry with invalid loc header position: " + entryNameSanitized); + log.log("Skipping zip entry with invalid loc header position (" + locHeaderPos + "): " + + entryNameSanitized); } continue; } @@ -759,6 +777,13 @@ private void readCentralDirectory(final NestedJarHandler nestedJarHandler, final continue; } + if (getPhysicalFile().getName().endsWith("bigjar.jar")) { + System.out.println(entryNameSanitized); + if (entryNameSanitized.equals("aa/output16.bin")) { + System.out.println("here"); + } + } + // Add zip entry final FastZipEntry entry = new FastZipEntry(this, locHeaderPos, entryNameSanitized, isDeflated, compressedSize, uncompressedSize, lastModifiedMillis, lastModifiedTimeMSDOS, From f4a538e957436dcd32bd2db796dce59134a3e28f Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 30 Apr 2021 18:30:15 -0600 Subject: [PATCH 1063/1778] Read unsigned int values as required by Zip spec (#514) --- .../fastzipfilereader/LogicalZipFile.java | 41 ++++++++----------- 1 file changed, 17 insertions(+), 24 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 7d2127404..8d3a96d99 100644 --- a/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java +++ b/src/main/java/nonapi/io/github/classgraph/fastzipfilereader/LogicalZipFile.java @@ -447,7 +447,7 @@ private void readCentralDirectory(final NestedJarHandler nestedJarHandler, final // initially just try reading back a maximum of 32 characters. long eocdPos = -1; for (long i = slice.sliceLength - 22, iMin = slice.sliceLength - 22 - 32; i >= iMin && i >= 0L; --i) { - if (reader.readInt(i) == 0x06054b50) { + if (reader.readUnsignedInt(i) == 0x06054b50L) { eocdPos = i; break; } @@ -466,7 +466,7 @@ private void readCentralDirectory(final NestedJarHandler nestedJarHandler, final /* inflatedLengthHint = */ 0L, nestedJarHandler)) { final RandomAccessReader eocdReader = arraySlice.randomAccessReader(); for (long i = eocdBytes.length - 22L; i >= 0L; --i) { - if (eocdReader.readInt(i) == 0x06054b50) { + if (eocdReader.readUnsignedInt(i) == 0x06054b50L) { eocdPos = i + readStartOff; break; } @@ -491,17 +491,17 @@ private void readCentralDirectory(final NestedJarHandler nestedJarHandler, final // Check for Zip64 End Of Central Directory Locator record final long zip64cdLocIdx = eocdPos - 20; - if (zip64cdLocIdx >= 0 && reader.readInt(zip64cdLocIdx) == 0x07064b50) { - if (reader.readInt(zip64cdLocIdx + 4) > 0 || reader.readInt(zip64cdLocIdx + 16) > 1) { + if (zip64cdLocIdx >= 0 && reader.readUnsignedInt(zip64cdLocIdx) == 0x07064b50L) { + if (reader.readUnsignedInt(zip64cdLocIdx + 4) > 0 || reader.readUnsignedInt(zip64cdLocIdx + 16) > 1) { throw new IOException("Multi-disk jarfiles not supported: " + getPath()); } final long eocdPos64 = reader.readLong(zip64cdLocIdx + 8); - if (reader.readInt(eocdPos64) != 0x06064b50) { + if (reader.readUnsignedInt(eocdPos64) != 0x06064b50L) { throw new IOException("Zip64 central directory at location " + eocdPos64 + " does not have Zip64 central directory header: " + getPath()); } final long numEnt64 = reader.readLong(eocdPos64 + 24); - if (reader.readInt(eocdPos64 + 16) > 0 || reader.readInt(eocdPos64 + 20) > 0 + if (reader.readUnsignedInt(eocdPos64 + 16) > 0 || reader.readUnsignedInt(eocdPos64 + 20) > 0 || numEnt64 != reader.readLong(eocdPos64 + 32)) { throw new IOException("Multi-disk jarfiles not supported: " + getPath()); } @@ -565,10 +565,10 @@ private void readCentralDirectory(final NestedJarHandler nestedJarHandler, final // numEnt and numEnt64 were inconsistent -- manually count entries numEnt = 0; for (long entOff = 0; entOff + 46 <= cenSize;) { - final int sig = cenReader.readInt(entOff); - if (sig != 0x02014b50) { - throw new IOException("Invalid central directory signature: 0x" + Integer.toString(sig, 16) - + ": " + getPath()); + final long sig = cenReader.readUnsignedInt(entOff); + if (sig != 0x02014b50L) { + throw new IOException("Invalid central directory signature: 0x" + + Integer.toString((int) sig, 16) + ": " + getPath()); } final int filenameLen = cenReader.readUnsignedShort(entOff + 28); final int extraFieldLen = cenReader.readUnsignedShort(entOff + 30); @@ -597,10 +597,10 @@ private void readCentralDirectory(final NestedJarHandler nestedJarHandler, final try { int entSize = 0; for (long entOff = 0; entOff + 46 <= cenSize; entOff += entSize) { - final int sig = cenReader.readInt(entOff); - if (sig != 0x02014b50) { - throw new IOException("Invalid central directory signature: 0x" + Integer.toString(sig, 16) - + ": " + getPath()); + final long sig = cenReader.readUnsignedInt(entOff); + if (sig != 0x02014b50L) { + throw new IOException("Invalid central directory signature: 0x" + + Integer.toString((int) sig, 16) + ": " + getPath()); } final int filenameLen = cenReader.readUnsignedShort(entOff + 28); final int extraFieldLen = cenReader.readUnsignedShort(entOff + 30); @@ -651,7 +651,7 @@ private void readCentralDirectory(final NestedJarHandler nestedJarHandler, final // Get external file attributes final int fileAttributes = cenReader.readUnsignedShort(entOff + 40); - long pos = cenReader.readInt(entOff + 42); + long pos = cenReader.readUnsignedInt(entOff + 42); // Check for Zip64 header in extra fields // See: @@ -700,7 +700,7 @@ private void readCentralDirectory(final NestedJarHandler nestedJarHandler, final } else if (tag == 0x5455 && size >= 5) { // Extended Unix timestamp - final byte bits = cenReader.readByte(tagOff + 4 + 0); + final int bits = cenReader.readUnsignedByte(tagOff + 4 + 0); if ((bits & 1) == 1 && size >= 5 + 8) { lastModifiedMillis = cenReader.readLong(tagOff + 4 + 1) * 1000L; } @@ -715,7 +715,7 @@ private void readCentralDirectory(final NestedJarHandler nestedJarHandler, final } else if (tag == 0x7075) { // Info-ZIP Unicode path extra field - final byte version = cenReader.readByte(tagOff + 4 + 0); + final int version = cenReader.readUnsignedByte(tagOff + 4 + 0); if (version != 1) { throw new IOException("Unknown Unicode entry name format " + version + " in extra field: " + entryNameSanitized); @@ -777,13 +777,6 @@ private void readCentralDirectory(final NestedJarHandler nestedJarHandler, final continue; } - if (getPhysicalFile().getName().endsWith("bigjar.jar")) { - System.out.println(entryNameSanitized); - if (entryNameSanitized.equals("aa/output16.bin")) { - System.out.println("here"); - } - } - // Add zip entry final FastZipEntry entry = new FastZipEntry(this, locHeaderPos, entryNameSanitized, isDeflated, compressedSize, uncompressedSize, lastModifiedMillis, lastModifiedTimeMSDOS, From 65d469a8d43aadc262dd8f8593f616aeb53975fb Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 30 Apr 2021 18:31:32 -0600 Subject: [PATCH 1064/1778] [maven-release-plugin] prepare release classgraph-4.8.105 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 9c28afea9..83bbb7f4e 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.105-SNAPSHOT + 4.8.105 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.103 + classgraph-4.8.105 From 9aaae4272ac5a777228bce004c6122dd9024a168 Mon Sep 17 00:00:00 2001 From: Luke Hutchison Date: Fri, 30 Apr 2021 18:31:49 -0600 Subject: [PATCH 1065/1778] [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 83bbb7f4e..5fa4b55c9 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ io.github.classgraph classgraph - 4.8.105 + 4.8.106-SNAPSHOT ClassGraph The uber-fast, ultra-lightweight classpath and module scanner for JVM languages. 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 1066/1778] 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 1067/1778] 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 1068/1778] 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 1069/1778] 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 1070/1778] 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 1071/1778] 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 1072/1778] 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 1073/1778] 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 1074/1778] 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 1075/1778] 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 1076/1778] 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 1077/1778] 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 1078/1778] 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 1079/1778] 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 1080/1778] 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 1081/1778] 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 1082/1778] 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 1083/1778] 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 1084/1778] 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 1085/1778] 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 1086/1778] [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 1087/1778] [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 1088/1778] 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 1089/1778] 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 1090/1778] 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 1091/1778] 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 1092/1778] 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 1093/1778] 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 1094/1778] 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 1095/1778] 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 1096/1778] 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 1097/1778] 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 1098/1778] 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 1099/1778] [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 1100/1778] [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 1101/1778] 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 1102/1778] [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 1103/1778] [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 1104/1778] 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 1105/1778] 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 1106/1778] 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 1107/1778] 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 1108/1778] [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 1109/1778] [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 1110/1778] 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 1111/1778] 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 1112/1778] [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 1113/1778] [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 1114/1778] 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 1115/1778] 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 1116/1778] 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 1117/1778] [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 1118/1778] [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 1119/1778] 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 1120/1778] 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 1121/1778] 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 1122/1778] [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 1123/1778] [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 1124/1778] 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 1125/1778] 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 1126/1778] 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 1127/1778] 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 1128/1778] [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 1129/1778] [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 1130/1778] 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 1131/1778] 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 1132/1778] 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 1133/1778] 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 1134/1778] [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 1135/1778] [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 1136/1778] 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 1137/1778] 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 1138/1778] 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 1139/1778] 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 1140/1778] 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 1141/1778] [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 1142/1778] [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 1143/1778] 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 1144/1778] 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 1145/1778] 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 1146/1778] [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 1147/1778] [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 1148/1778] 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 1149/1778] 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 1150/1778] 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 1151/1778] 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 1152/1778] 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 1153/1778] '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 1154/1778] 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 1155/1778] 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 1156/1778] 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 1157/1778] 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 1158/1778] 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 1159/1778] 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 1160/1778] 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 1161/1778] 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 1162/1778] 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 1163/1778] 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 1164/1778] 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 1165/1778] 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 1166/1778] 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 1167/1778] 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 1168/1778] 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 1169/1778] 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 1170/1778] 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 1171/1778] 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 1172/1778] [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 1173/1778] [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 1174/1778] 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 1175/1778] 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 1176/1778] 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 1177/1778] 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 1178/1778] 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 1179/1778] 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 1180/1778] [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 1181/1778] [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 1182/1778] 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 1183/1778] 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 1184/1778] 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 1185/1778] 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 1186/1778] 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 1187/1778] 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 1188/1778] 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 1189/1778] 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 1190/1778] [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 1191/1778] [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 1192/1778] 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 1193/1778] 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 1194/1778] [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 1195/1778] [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 1196/1778] 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 1197/1778] [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 1198/1778] [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 1199/1778] 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 1200/1778] 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 1201/1778] 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 1202/1778] 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 1203/1778] 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 1204/1778] 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 1205/1778] 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 1206/1778] 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 1207/1778] 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 1208/1778] 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 1209/1778] [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 1210/1778] [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 1211/1778] 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 1212/1778] 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 1213/1778] 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 1214/1778] 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 1215/1778] [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 1216/1778] [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 1217/1778] 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 1218/1778] 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 1219/1778] [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 1220/1778] [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 1221/1778] 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 1222/1778] 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 1223/1778] 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 1224/1778] 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 1225/1778] 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 1226/1778] 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 1227/1778] [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 1228/1778] [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 1229/1778] 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 1230/1778] 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 1231/1778] [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 1232/1778] [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 1233/1778] 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 1234/1778] 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 1235/1778] 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 1236/1778] 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 1237/1778] 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 1238/1778] 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 1239/1778] 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 1240/1778] 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 1241/1778] 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 1242/1778] [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 1243/1778] [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 1244/1778] 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 1245/1778] 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 1246/1778] 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 1247/1778] 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 1248/1778] 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 1249/1778] 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 1250/1778] 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 1251/1778] 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 1252/1778] 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 1253/1778] 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 1254/1778] 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 1255/1778] 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 1256/1778] 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 1257/1778] 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 1258/1778] 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 1259/1778] 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 1260/1778] [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 1261/1778] [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 1262/1778] 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 1263/1778] 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 1264/1778] 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 1265/1778] 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 1266/1778] [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 1267/1778] [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 1268/1778] 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 1269/1778] 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 1270/1778] 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 1271/1778] 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 1272/1778] 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 1273/1778] 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 1274/1778] 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 1275/1778] 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 1276/1778] 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 1277/1778] 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 1278/1778] 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 1279/1778] [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 1280/1778] [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 1281/1778] 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 1282/1778] 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 1283/1778] 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 1284/1778] [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 1285/1778] [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 1286/1778] 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 1287/1778] 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 1288/1778] [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 1289/1778] [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 1290/1778] 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 1291/1778] 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 1292/1778] 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 1293/1778] 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 1294/1778] 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 1295/1778] 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 1296/1778] 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 1297/1778] 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 1298/1778] 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 1299/1778] 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 1300/1778] 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 1301/1778] 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 1302/1778] [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 1303/1778] [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 1304/1778] 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 1305/1778] 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 1306/1778] [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 1307/1778] [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 1308/1778] 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 1309/1778] 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 1310/1778] 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 1311/1778] 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 1312/1778] [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 1313/1778] [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 1314/1778] 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 1315/1778] [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 1316/1778] [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 1317/1778] 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 1318/1778] 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 1319/1778] 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 1320/1778] 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 1321/1778] [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 1322/1778] [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 1323/1778] 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 1324/1778] 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 1325/1778] 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 1326/1778] 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 1327/1778] 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 1328/1778] 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 1329/1778] 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 1330/1778] 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 1331/1778] 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 1332/1778] 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 1333/1778] 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 1334/1778] [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 1335/1778] 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 1336/1778] 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 1337/1778] 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 1338/1778] 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 1339/1778] 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 1340/1778] 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 1341/1778] 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 1342/1778] 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 1343/1778] 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 1344/1778] [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 1345/1778] [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 1346/1778] 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 1347/1778] 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 1348/1778] 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 1349/1778] 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 1350/1778] [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 1351/1778] [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 1352/1778] 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 1353/1778] 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 1354/1778] [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 1355/1778] [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 1356/1778] 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 1357/1778] 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 1358/1778] 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 1359/1778] 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 1360/1778] 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 1361/1778] 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 1362/1778] 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 1363/1778] 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 1364/1778] 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 1365/1778] 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 1366/1778] [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 1367/1778] [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 1368/1778] 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 1369/1778] 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 1370/1778] 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 1371/1778] 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 1372/1778] 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 1373/1778] 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 1374/1778] 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 1375/1778] 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 1376/1778] 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 1377/1778] 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 1378/1778] 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 1379/1778] 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 1380/1778] 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 1381/1778] 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 1382/1778] 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 1383/1778] 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 1384/1778] 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 1385/1778] 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 1386/1778] 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 1387/1778] 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 1388/1778] 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 1389/1778] 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 1390/1778] 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 1391/1778] 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 1392/1778] 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 1393/1778] 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 1394/1778] 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 1395/1778] 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 1396/1778] 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 1397/1778] 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 1398/1778] 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 1399/1778] 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 1400/1778] 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 1401/1778] 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 1402/1778] 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 1403/1778] 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 1404/1778] 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 1405/1778] 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 1406/1778] 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 1407/1778] 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 1408/1778] 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 1409/1778] [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 1410/1778] [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 1411/1778] 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 1412/1778] #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 1413/1778] 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 1414/1778] 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 1415/1778] [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 1416/1778] [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 1417/1778] 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 1418/1778] 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 1419/1778] 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 1420/1778] 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 1421/1778] 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 1422/1778] [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 1423/1778] [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 1424/1778] 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 1425/1778] 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 1426/1778] 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 1427/1778] 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 1428/1778] 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 1429/1778] 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 1430/1778] 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 1431/1778] 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 1432/1778] 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 1433/1778] 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 1434/1778] 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 1435/1778] 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 1436/1778] [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 1437/1778] 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 1438/1778] 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 1439/1778] 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 1440/1778] [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 1441/1778] [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 1442/1778] 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 1443/1778] 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 1444/1778] 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 1445/1778] 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 1446/1778] 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 1447/1778] 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 1448/1778] 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 1449/1778] 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 1450/1778] 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 1451/1778] 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 1452/1778] 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 1453/1778] 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 1454/1778] 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 1455/1778] 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 1456/1778] 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 1457/1778] 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 1458/1778] 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 1459/1778] [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 1460/1778] [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 1461/1778] 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 1462/1778] 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 1463/1778] 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 1464/1778] [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 1465/1778] [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 1466/1778] 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 1467/1778] 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 1468/1778] 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 1469/1778] [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 1470/1778] [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 1471/1778] 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 1472/1778] 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 1473/1778] 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 1474/1778] 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 1475/1778] 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 1476/1778] 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 1477/1778] 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 1478/1778] 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 1479/1778] 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 1480/1778] 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 1481/1778] 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 1482/1778] 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 1483/1778] 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 1484/1778] [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 1485/1778] [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 1486/1778] 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 1487/1778] 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 1488/1778] 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 1489/1778] 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 1490/1778] 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 1491/1778] [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 1492/1778] [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 1493/1778] 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 1494/1778] 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 1495/1778] [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 1496/1778] [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 1497/1778] 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 1498/1778] 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 1499/1778] 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 1500/1778] 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 1501/1778] 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 1502/1778] 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 1503/1778] 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 1504/1778] 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 1505/1778] 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 1506/1778] 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 1507/1778] 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 1508/1778] 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 1509/1778] 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 1510/1778] 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 1511/1778] 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 1512/1778] 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 1513/1778] 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 1514/1778] 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 1515/1778] 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 1516/1778] 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 1517/1778] 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 1518/1778] 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 1519/1778] 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 1520/1778] 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 1521/1778] [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 1522/1778] [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 1523/1778] 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 1524/1778] 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 1525/1778] 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 1526/1778] 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 1527/1778] 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 1528/1778] [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 1529/1778] [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 1530/1778] 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 1531/1778] 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 1532/1778] 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 1533/1778] 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 1534/1778] 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 1535/1778] 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 1536/1778] [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 1537/1778] [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 1538/1778] 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 1539/1778] 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 1540/1778] 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 1541/1778] 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 1542/1778] 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 1543/1778] 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 1544/1778] 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 1545/1778] 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 1546/1778] 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 1547/1778] 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 1548/1778] [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 1549/1778] [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 1550/1778] 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 1551/1778] 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 1552/1778] 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 1553/1778] [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 1554/1778] [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 1555/1778] 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 1556/1778] 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 1557/1778] 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 1558/1778] 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 1559/1778] 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 1560/1778] 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 1561/1778] [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 1562/1778] [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 1563/1778] 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 1564/1778] [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 1565/1778] [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 1566/1778] 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 1567/1778] 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 1568/1778] 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 1569/1778] [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 1570/1778] [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 1571/1778] 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 1572/1778] 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 1573/1778] 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 1574/1778] 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 1575/1778] 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 1576/1778] 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 1577/1778] 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 1578/1778] 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 1579/1778] 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 1580/1778] [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 1581/1778] [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 1582/1778] 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 1583/1778] [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 1584/1778] [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 1585/1778] 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 1586/1778] 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 1587/1778] 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 1588/1778] 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 1589/1778] 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 1590/1778] #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 1591/1778] 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 1592/1778] 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 1593/1778] 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 1594/1778] 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 1595/1778] [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 1596/1778] [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 1597/1778] 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 1598/1778] 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 1599/1778] 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 1600/1778] 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 1601/1778] 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 1602/1778] 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 1603/1778] 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 1604/1778] [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 1605/1778] [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 1606/1778] 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 1607/1778] 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 1608/1778] 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 1609/1778] 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 1610/1778] 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 1611/1778] 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 1612/1778] #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 1613/1778] 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 1614/1778] [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 1615/1778] [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 1616/1778] 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 1617/1778] 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 1618/1778] 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 1619/1778] [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 1620/1778] [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 1621/1778] 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 1622/1778] 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 1623/1778] 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 1624/1778] 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 1625/1778] 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 1626/1778] 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 1627/1778] 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 1628/1778] 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 1629/1778] 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 1630/1778] 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 1631/1778] 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 1632/1778] 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 1633/1778] 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 1634/1778] 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 1635/1778] 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 1636/1778] [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 1637/1778] [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 1638/1778] 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 1639/1778] 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 1640/1778] 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 1641/1778] 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 1642/1778] 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 1643/1778] [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 1644/1778] [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 1645/1778] 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 1646/1778] 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 1647/1778] [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 1648/1778] [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 1649/1778] 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 1650/1778] 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 1651/1778] 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 1652/1778] 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 1653/1778] 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 1654/1778] 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 1655/1778] 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 1656/1778] [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 1657/1778] 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 1658/1778] 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 1659/1778] 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 1660/1778] 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 1661/1778] 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 1662/1778] 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 1663/1778] 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 1664/1778] 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 1665/1778] 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 1666/1778] 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 1667/1778] [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 1668/1778] [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 1669/1778] 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 1670/1778] [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 1671/1778] [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 1672/1778] 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 1673/1778] 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 1674/1778] 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 1675/1778] 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 1676/1778] 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 1677/1778] 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 1678/1778] 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 1679/1778] 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 1680/1778] 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 1681/1778] 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 1682/1778] [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 1683/1778] [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 1684/1778] 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 1685/1778] 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 1686/1778] 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 1687/1778] [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 1688/1778] 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 1689/1778] 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 1690/1778] 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 1691/1778] [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 1692/1778] [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 1693/1778] 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 1694/1778] 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 1695/1778] 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 1696/1778] 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 1697/1778] 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 1698/1778] 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 1699/1778] 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 1700/1778] 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 1701/1778] 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 1702/1778] 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 1703/1778] 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 1704/1778] 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 1705/1778] 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 1706/1778] 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 1707/1778] [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 1708/1778] [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 1709/1778] 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 1710/1778] 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 1711/1778] [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 1712/1778] [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 1713/1778] 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 1714/1778] 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 1715/1778] 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 1716/1778] 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 1717/1778] 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 1718/1778] 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 1719/1778] 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 1720/1778] 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 1721/1778] 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 1722/1778] [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 1723/1778] 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 1724/1778] 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 1725/1778] 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 1726/1778] [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 1727/1778] [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 1728/1778] 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 1729/1778] 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 1730/1778] 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 1731/1778] 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 1732/1778] 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 1733/1778] 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 1734/1778] 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 1735/1778] 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 1736/1778] 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 1737/1778] 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 1738/1778] [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 1739/1778] [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 1740/1778] 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 1741/1778] [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 1742/1778] [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 1743/1778] 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 1744/1778] 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 1745/1778] 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 1746/1778] [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 1747/1778] [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 1748/1778] 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 1749/1778] [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 1750/1778] 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 1751/1778] 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 1752/1778] [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 1753/1778] [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 1754/1778] [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 1755/1778] [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 1756/1778] 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 1757/1778] 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 1758/1778] 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 1759/1778] 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 1760/1778] [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 1761/1778] [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 1762/1778] 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 1763/1778] [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 1764/1778] [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 1765/1778] 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 1766/1778] 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 1767/1778] 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 1768/1778] 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 1769/1778] 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 1770/1778] 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 1771/1778] [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 1772/1778] 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 1773/1778] 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 1774/1778] 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 1775/1778] 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 1776/1778] 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 1777/1778] 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 1778/1778] 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.