This is an automated email from the ASF dual-hosted git repository. slachiewicz pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/maven.git
commit 08ab375556c820281973f219459fbbc9d99b4d6a Author: Sylwester Lachiewicz <[email protected]> AuthorDate: Wed Oct 28 21:51:33 2026 +0000 Read jar resources without the JVM jar: cache The DI index, the core extension descriptor and lifecycle.xml were read with URL.openStream(), which leaves the plugin or extension jar open in the JVM-wide jar: cache for the rest of the build (JDK-8239054): it stays locked on Windows and is read stale under mvnd once rebuilt. --- .../maven/plugin/descriptor/PluginDescriptor.java | 6 +++- .../extension/internal/CoreExtensionEntry.java | 6 +++- .../org/apache/maven/di/impl/InjectorImpl.java | 6 +++- .../org/apache/maven/di/impl/InjectorImplTest.java | 39 ++++++++++++++++++++++ 4 files changed, 54 insertions(+), 3 deletions(-) diff --git a/compat/maven-plugin-api/src/main/java/org/apache/maven/plugin/descriptor/PluginDescriptor.java b/compat/maven-plugin-api/src/main/java/org/apache/maven/plugin/descriptor/PluginDescriptor.java index 776ba84c01..6ef652cffb 100644 --- a/compat/maven-plugin-api/src/main/java/org/apache/maven/plugin/descriptor/PluginDescriptor.java +++ b/compat/maven-plugin-api/src/main/java/org/apache/maven/plugin/descriptor/PluginDescriptor.java @@ -25,6 +25,7 @@ import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; +import java.net.URLConnection; import java.nio.file.Files; import java.util.ArrayList; import java.util.Collections; @@ -446,7 +447,10 @@ private InputStream getDescriptorStream(String descriptor) throws IOException { if (pluginFile.isFile()) { try { - return new URL("jar:" + pluginFile.toURI() + "!/" + descriptor).openStream(); + URLConnection connection = new URL("jar:" + pluginFile.toURI() + "!/" + descriptor).openConnection(); + // Uncached, so that closing the stream closes the jar (JDK-8239054) + connection.setUseCaches(false); + return connection.getInputStream(); } catch (MalformedURLException e) { throw new IllegalStateException(e); } diff --git a/impl/maven-core/src/main/java/org/apache/maven/extension/internal/CoreExtensionEntry.java b/impl/maven-core/src/main/java/org/apache/maven/extension/internal/CoreExtensionEntry.java index f19a5bf6ef..b1d5e39f45 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/extension/internal/CoreExtensionEntry.java +++ b/impl/maven-core/src/main/java/org/apache/maven/extension/internal/CoreExtensionEntry.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.io.InputStream; import java.net.URL; +import java.net.URLConnection; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; @@ -110,7 +111,10 @@ public static CoreExtensionEntry discoverFrom(ClassRealm loader) { Enumeration<URL> urls = loader.getResources(BUILDER.getExtensionDescriptorLocation()); while (urls.hasMoreElements()) { - try (InputStream is = urls.nextElement().openStream()) { + URLConnection connection = urls.nextElement().openConnection(); + // Uncached, so that closing the stream closes the jar (JDK-8239054) + connection.setUseCaches(false); + try (InputStream is = connection.getInputStream()) { ExtensionDescriptor descriptor = BUILDER.build(is); artifacts.addAll(descriptor.getExportedArtifacts()); packages.addAll(descriptor.getExportedPackages()); diff --git a/impl/maven-di/src/main/java/org/apache/maven/di/impl/InjectorImpl.java b/impl/maven-di/src/main/java/org/apache/maven/di/impl/InjectorImpl.java index fc32b7a072..fce1c24120 100644 --- a/impl/maven-di/src/main/java/org/apache/maven/di/impl/InjectorImpl.java +++ b/impl/maven-di/src/main/java/org/apache/maven/di/impl/InjectorImpl.java @@ -26,6 +26,7 @@ import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.net.URL; +import java.net.URLConnection; import java.util.AbstractList; import java.util.AbstractMap; import java.util.AbstractSet; @@ -97,7 +98,10 @@ public Injector discover(@Nonnull ClassLoader classLoader) { while (enumeration.hasMoreElements()) { URL url = enumeration.nextElement(); if (loadedUrls.add(url.toExternalForm())) { - try (InputStream is = url.openStream(); + URLConnection connection = url.openConnection(); + // Uncached, so that closing the stream closes the jar (JDK-8239054) + connection.setUseCaches(false); + try (InputStream is = connection.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(Objects.requireNonNull(is)))) { for (String line : reader.lines() diff --git a/impl/maven-di/src/test/java/org/apache/maven/di/impl/InjectorImplTest.java b/impl/maven-di/src/test/java/org/apache/maven/di/impl/InjectorImplTest.java index b2a5032644..91ff4d8127 100644 --- a/impl/maven-di/src/test/java/org/apache/maven/di/impl/InjectorImplTest.java +++ b/impl/maven-di/src/test/java/org/apache/maven/di/impl/InjectorImplTest.java @@ -18,14 +18,23 @@ */ package org.apache.maven.di.impl; +import java.io.IOException; +import java.io.OutputStream; import java.lang.annotation.Retention; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; +import java.util.jar.JarOutputStream; import java.util.stream.Collectors; +import java.util.zip.ZipEntry; import org.apache.maven.api.annotations.Nullable; import org.apache.maven.api.di.Inject; @@ -38,6 +47,7 @@ import org.apache.maven.di.Injector; import org.apache.maven.di.Key; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import static java.lang.annotation.RetentionPolicy.RUNTIME; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -52,6 +62,35 @@ @SuppressWarnings("unused") public class InjectorImplTest { + @Test + void discoverReadsRewrittenJar(@TempDir Path tempDir) throws Exception { + Path jar = tempDir.resolve("plugin.jar"); + writeIndex(jar, "# first"); + discover(jar); + + // Rewritten in place, as a plugin rebuilt in the reactor is. + writeIndex(jar, "# second, and longer than the first"); + discover(jar); + + // Nothing keeps the jar open, so it can be deleted (fails on Windows while it is open). + Files.delete(jar); + } + + private static void discover(Path jar) throws IOException { + try (URLClassLoader loader = new URLClassLoader(new URL[] {jar.toUri().toURL()}, null)) { + Injector.create().discover(loader); + } + } + + private static void writeIndex(Path jar, String content) throws IOException { + try (OutputStream out = Files.newOutputStream(jar); + JarOutputStream jarOut = new JarOutputStream(out)) { + jarOut.putNextEntry(new ZipEntry("META-INF/maven/org.apache.maven.api.di.Inject")); + jarOut.write(content.getBytes(StandardCharsets.UTF_8)); + jarOut.closeEntry(); + } + } + @Test void markerQualifierTest() { Injector injector = Injector.create().bindImplicit(QualifierTest.class);
