From 298b76883ab699c49d71804afb17207f78ccde1d Mon Sep 17 00:00:00 2001 From: "Mark S. Lewis" Date: Wed, 21 Oct 2026 17:33:46 +0100 Subject: [PATCH] build(deps): update classgraph from 4.8.197 to 5.0.0 Classgraph 4.x is end-of-life. Classgraph 6.x supports only Java 25 and later. Classgraph 5.x is an interim version that continues support for Java 17 to 24. This change adopts Classgraph 5.x to maintain compatibility with the current build, which uses Java 17. Signed-off-by: Mark S. Lewis --- .../DefaultExtensionCatalogTest.java | 6 ++- gradle/libs.versions.toml | 2 +- isthmus-cli/build.gradle.kts | 8 ++-- .../isthmus/cli/RegisterAtRuntime.java | 37 ++++++++++++++----- 4 files changed, 38 insertions(+), 15 deletions(-) diff --git a/core/src/test/java/io/substrait/extension/DefaultExtensionCatalogTest.java b/core/src/test/java/io/substrait/extension/DefaultExtensionCatalogTest.java index a64db3506..48151fddf 100644 --- a/core/src/test/java/io/substrait/extension/DefaultExtensionCatalogTest.java +++ b/core/src/test/java/io/substrait/extension/DefaultExtensionCatalogTest.java @@ -80,7 +80,11 @@ private static String parseUrn(String resourceName) throws IOException { private static List getExtensionYamlFiles() { try (ScanResult scan = - new ClassGraph().acceptPathsNonRecursive("substrait/extensions").scan()) { + new ClassGraph() + .enableClasspath() + .enableNonSystemModules() + .acceptPathsNonRecursive("substrait/extensions") + .scan()) { ResourceList resources = scan.getResourcesWithExtension(".yaml"); return resources.stream() .map(r -> r.getPath().substring("substrait/extensions/".length())) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 664182d6e..341733fe9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] antlr = "4.13.2" calcite = "1.42.0" -classgraph = "4.8.197" +classgraph = "5.0.0" graal = "25.0.2" graal-plugin = "1.1.14" gradle-extensions = "4.0.0" diff --git a/isthmus-cli/build.gradle.kts b/isthmus-cli/build.gradle.kts index 93a99276d..7f70518ce 100644 --- a/isthmus-cli/build.gradle.kts +++ b/isthmus-cli/build.gradle.kts @@ -68,13 +68,13 @@ graalvmNative { // src/main/resources/META-INF/native-image/.../reachability-metadata.json. buildArgs.add("-H:+ReportExceptionStackTraces") buildArgs.add("--features=io.substrait.isthmus.cli.RegisterAtRuntime") + // ClassGraph registers this FileSystemProvider via META-INF/services. RegisterAtRuntime + // scans with ClassGraph inside the image builder, which leaves the provider in the JDK's + // cached list of installed providers, and so in the image heap. + buildArgs.add("--initialize-at-build-time=io.github.classgraph.vfs.VfsFileSystemProvider") // Removed due to https://github.com/oracle/graal/issues/13316 // buildArgs.add("--future-defaults=all") jvmArgs.add("--sun-misc-unsafe-memory-access=allow") - - // Capture the classpath since native-compile erases it - val isthmusClasspath = classpath.joinToString(":") - jvmArgs.add("-Disthmus.classpath=$isthmusClasspath") } } } diff --git a/isthmus-cli/src/main/java/io/substrait/isthmus/cli/RegisterAtRuntime.java b/isthmus-cli/src/main/java/io/substrait/isthmus/cli/RegisterAtRuntime.java index 44b58d55b..3ce3264c3 100644 --- a/isthmus-cli/src/main/java/io/substrait/isthmus/cli/RegisterAtRuntime.java +++ b/isthmus-cli/src/main/java/io/substrait/isthmus/cli/RegisterAtRuntime.java @@ -4,10 +4,13 @@ import com.google.protobuf.GeneratedMessage; import com.google.protobuf.ProtocolMessageEnum; import io.github.classgraph.ClassGraph; +import io.github.classgraph.ClassInfo; import io.github.classgraph.ClassInfoList; import io.github.classgraph.ScanResult; import io.substrait.extension.SimpleExtension; import java.lang.annotation.Annotation; +import java.util.ArrayList; +import java.util.List; import org.apache.calcite.rel.metadata.BuiltInMetadata; import org.apache.calcite.rel.metadata.Metadata; import org.apache.calcite.rel.metadata.MetadataHandler; @@ -60,7 +63,7 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { // Empty class register(Empty.class); - try (PackageScanner substrait = new PackageScanner("io.substrait")) { + try (PackageScanner substrait = new PackageScanner(access, "io.substrait")) { // protobuf items substrait.registerByParent(GeneratedMessage.class); substrait.registerByParent(GeneratedMessage.Builder.class); @@ -83,7 +86,7 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { SqlStdOperatorTable.class, StandardConvertletTable.class); - try (PackageScanner calcite = new PackageScanner("org.apache.calcite")) { + try (PackageScanner calcite = new PackageScanner(access, "org.apache.calcite")) { calcite.registerByParent(Metadata.class); calcite.registerByParent(MetadataHandler.class); calcite.registerByParent(Resources.Element.class); @@ -146,33 +149,49 @@ private static void register(Class... classes) { } private static final class PackageScanner implements AutoCloseable { + private final Feature.FeatureAccess access; private final ScanResult scan; - PackageScanner(String... packageNames) { + PackageScanner(Feature.FeatureAccess access, String... packageNames) { + this.access = access; scan = new ClassGraph() - .enableAllInfo() // GraalVM native-compile erases the classloader classpath - .overrideClasspath(System.getProperty("isthmus.classpath")) + .enableClasspathEntries(access.getApplicationClassPath()) + .enableClassInfo() + .enableAnnotationInfo() + .ignoreClassVisibility() .acceptPackages(packageNames) .scan(); } void registerByAnnotation(Class annotation) { - scan.getClassesWithAnnotation(annotation).loadClasses().forEach(this::registerByParent); + loadClasses(scan.getClassesWithAnnotation(annotation)).forEach(this::registerByParent); } void registerByParent(Class c) { register(c); - getSubTypes(c).loadClasses().forEach(RegisterAtRuntime::register); + loadClasses(getSubTypes(c)).forEach(RegisterAtRuntime::register); } private ClassInfoList getSubTypes(Class c) { if (c.isInterface()) { - return scan.getClassesImplementing(c); + return scan.getAllClassesImplementing(c); } - return scan.getSubclasses(c); + return scan.getAllSubclasses(c); + } + + private List> loadClasses(ClassInfoList classInfos) { + List> classes = new ArrayList<>(); + for (ClassInfo classInfo : classInfos) { + Class c = access.findClassByName(classInfo.getName()); + if (c == null) { + throw new IllegalStateException("Class not found: " + classInfo.getName()); + } + classes.add(c); + } + return classes; } @Override