You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2026/05/02 05:50:21 UTC
(groovy) branch master updated: GROOVY-11978: RootLoader duplicates the core jar already on the bootstrap classpath
This is an automated email from the ASF dual-hosted git repository.
paulk-asert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/master by this push:
new 57662d1a1a GROOVY-11978: RootLoader duplicates the core jar already on the bootstrap classpath
57662d1a1a is described below
commit 57662d1a1aa870434d3b5e40389840f482b34938
Author: Paul King <pa...@asert.com.au>
AuthorDate: Sat May 2 09:20:01 2026 +1000
GROOVY-11978: RootLoader duplicates the core jar already on the bootstrap classpath
---
.../java/org/codehaus/groovy/tools/RootLoader.java | 43 +++++++++++++++++++++-
1 file changed, 42 insertions(+), 1 deletion(-)
diff --git a/src/main/java/org/codehaus/groovy/tools/RootLoader.java b/src/main/java/org/codehaus/groovy/tools/RootLoader.java
index ff8779c430..14c775a1cd 100644
--- a/src/main/java/org/codehaus/groovy/tools/RootLoader.java
+++ b/src/main/java/org/codehaus/groovy/tools/RootLoader.java
@@ -19,12 +19,16 @@
package org.codehaus.groovy.tools;
import java.io.File;
+import java.io.IOException;
import java.net.MalformedURLException;
+import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
+import java.util.Set;
/**
* This ClassLoader should be used as root of class loaders. Any
@@ -109,8 +113,16 @@ public class RootLoader extends URLClassLoader {
Thread.currentThread().setContextClassLoader(this);
+ // Skip URLs already on the JVM startup (system) classpath: the launcher
+ // puts the core groovy jar on -classpath to start GroovyStarter, and
+ // groovy-starter.conf's "load lib/*.jar" then re-globs it. Adding the
+ // same jar to both loaders lets each define its own copy of every core
+ // class, and trips duplicate-resource detection.
+ Set<File> startup = startupClasspathFiles();
for (URL url : lc.getClassPathUrls()) {
- addURL(url);
+ if (!startup.contains(canonicalFile(url))) {
+ addURL(url);
+ }
}
// TODO M12N eventually defer this until later when we have a full Groovy
// environment and use normal Grape.grab()
@@ -179,4 +191,33 @@ public class RootLoader extends URLClassLoader {
protected Class<?> findClass(final String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
+
+ private static Set<File> startupClasspathFiles() {
+ Set<File> files = new HashSet<>();
+ String cp = System.getProperty("java.class.path");
+ if (cp == null || cp.isEmpty()) return files;
+ for (String entry : cp.split(File.pathSeparator)) {
+ if (entry.isEmpty()) continue;
+ File f = canonicalFile(new File(entry));
+ if (f != null) files.add(f);
+ }
+ return files;
+ }
+
+ private static File canonicalFile(URL url) {
+ if (!"file".equals(url.getProtocol())) return null;
+ try {
+ return canonicalFile(new File(url.toURI()));
+ } catch (URISyntaxException | IllegalArgumentException e) {
+ return null;
+ }
+ }
+
+ private static File canonicalFile(File f) {
+ try {
+ return f.getCanonicalFile();
+ } catch (IOException e) {
+ return f.getAbsoluteFile();
+ }
+ }
}