This is an automated email from the ASF dual-hosted git repository.
gnodet pushed a commit to branch maven-4.0.x
in repository https://gitbox.apache.org/repos/asf/maven.git
The following commit(s) were added to refs/heads/maven-4.0.x by this push:
new b2e4fd4763 Enable the search for `module-info.class` file in the
`META-INF/versions/` sub-directories of a JAR file. (#11153) (#11206)
b2e4fd4763 is described below
commit b2e4fd4763e216df3865c2b32dc568e439013bdf
Author: Guillaume Nodet <[email protected]>
AuthorDate: Mon Oct 6 23:14:21 2025 +0200
Enable the search for `module-info.class` file in the `META-INF/versions/`
sub-directories of a JAR file. (#11153) (#11206)
If the project specifies a target Java release, only the directories for
versions equal to lower to the target version will be scanned.
(cherry picked from commit f032cfe067a29d40e3cdfd51a23926ad6a4f4a74)
Co-authored-by: Martin Desruisseaux <[email protected]>
---
.../api/services/DependencyResolverRequest.java | 51 +++++++++++++++++++++-
.../maven/impl/DefaultDependencyResolver.java | 38 ++++++++++++++--
.../impl/DefaultDependencyResolverResult.java | 7 ++-
.../org/apache/maven/impl/PathModularization.java | 7 +--
.../apache/maven/impl/PathModularizationCache.java | 18 ++++++--
5 files changed, 108 insertions(+), 13 deletions(-)
diff --git
a/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverRequest.java
b/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverRequest.java
index f419d7ff60..e9b3ab956b 100644
---
a/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverRequest.java
+++
b/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverRequest.java
@@ -34,6 +34,8 @@
import org.apache.maven.api.Project;
import org.apache.maven.api.RemoteRepository;
import org.apache.maven.api.Session;
+import org.apache.maven.api.SourceRoot;
+import org.apache.maven.api.Version;
import org.apache.maven.api.annotations.Experimental;
import org.apache.maven.api.annotations.Immutable;
import org.apache.maven.api.annotations.Nonnull;
@@ -95,6 +97,28 @@ enum RequestType {
@Nullable
Predicate<PathType> getPathTypeFilter();
+ /**
+ * Returns the version of the platform where the code will be executed.
+ * It should be the highest value of the {@code <targetVersion>} elements
+ * inside the {@code <source>} elements of a <abbr>POM</abbr> file.
+ *
+ * <h4>Application to Java</h4>
+ * In the context of a Java project, this is the value given to the {@code
--release} compiler option.
+ * This value can determine whether a dependency will be placed on the
class-path or on the module-path.
+ * For example, if the {@code module-info.class} entry of a
<abbr>JAR</abbr> file exists only in the
+ * {@code META-INF/versions/17/} sub-directory, then the default location
of that dependency will be
+ * the module-path only if the {@code --release} option is equal or
greater than 17.
+ *
+ * <p>If this value is not provided, then the default value in the context
of Java projects
+ * is the Java version on which Maven is running, as given by {@link
Runtime#version()}.</p>
+ *
+ * @return version of the platform where the code will be executed, or
{@code null} for default
+ *
+ * @see SourceRoot#targetVersion()
+ */
+ @Nullable
+ Version getTargetVersion();
+
@Nullable
List<RemoteRepository> getRepositories();
@@ -181,6 +205,7 @@ class DependencyResolverRequestBuilder {
boolean verbose;
PathScope pathScope;
Predicate<PathType> pathTypeFilter;
+ Version targetVersion;
List<RemoteRepository> repositories;
DependencyResolverRequestBuilder() {}
@@ -345,6 +370,18 @@ public DependencyResolverRequestBuilder
pathTypeFilter(@Nonnull Collection<? ext
return pathTypeFilter(desiredTypes::contains);
}
+ /**
+ * Sets the version of the platform where the code will be executed.
+ *
+ * @param target version of the platform where the code will be
executed, or {@code null} for the default
+ * @return {@code this} for method call chaining
+ */
+ @Nonnull
+ public DependencyResolverRequestBuilder targetVersion(@Nullable
Version target) {
+ targetVersion = target;
+ return this;
+ }
+
@Nonnull
public DependencyResolverRequestBuilder repositories(@Nonnull
List<RemoteRepository> repositories) {
this.repositories = repositories;
@@ -365,6 +402,7 @@ public DependencyResolverRequest build() {
verbose,
pathScope,
pathTypeFilter,
+ targetVersion,
repositories);
}
@@ -404,6 +442,7 @@ public String toString() {
private final boolean verbose;
private final PathScope pathScope;
private final Predicate<PathType> pathTypeFilter;
+ private final Version targetVersion;
private final List<RemoteRepository> repositories;
/**
@@ -426,6 +465,7 @@ public String toString() {
boolean verbose,
@Nullable PathScope pathScope,
@Nullable Predicate<PathType> pathTypeFilter,
+ @Nullable Version targetVersion,
@Nullable List<RemoteRepository> repositories) {
super(session, trace);
this.requestType = requireNonNull(requestType, "requestType
cannot be null");
@@ -438,6 +478,7 @@ public String toString() {
this.verbose = verbose;
this.pathScope = requireNonNull(pathScope, "pathScope cannot
be null");
this.pathTypeFilter = (pathTypeFilter != null) ?
pathTypeFilter : DEFAULT_FILTER;
+ this.targetVersion = targetVersion;
this.repositories = repositories;
if (verbose && requestType != RequestType.COLLECT) {
throw new IllegalArgumentException("verbose cannot only be
true when collecting dependencies");
@@ -495,6 +536,11 @@ public Predicate<PathType> getPathTypeFilter() {
return pathTypeFilter;
}
+ @Override
+ public Version getTargetVersion() {
+ return targetVersion;
+ }
+
@Override
public List<RemoteRepository> getRepositories() {
return repositories;
@@ -512,6 +558,7 @@ public boolean equals(Object o) {
&& Objects.equals(managedDependencies,
that.managedDependencies)
&& Objects.equals(pathScope, that.pathScope)
&& Objects.equals(pathTypeFilter, that.pathTypeFilter)
+ && Objects.equals(targetVersion, that.targetVersion)
&& Objects.equals(repositories, that.repositories);
}
@@ -527,6 +574,7 @@ public int hashCode() {
verbose,
pathScope,
pathTypeFilter,
+ targetVersion,
repositories);
}
@@ -541,7 +589,8 @@ public String toString() {
+ managedDependencies + ", verbose="
+ verbose + ", pathScope="
+ pathScope + ", pathTypeFilter="
- + pathTypeFilter + ", repositories="
+ + pathTypeFilter + ", targetVersion="
+ + targetVersion + ", repositories="
+ repositories + ']';
}
}
diff --git
a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultDependencyResolver.java
b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultDependencyResolver.java
index 814e71bace..278d7feb7e 100644
---
a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultDependencyResolver.java
+++
b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultDependencyResolver.java
@@ -21,7 +21,9 @@
import java.io.IOException;
import java.nio.file.Path;
import java.util.Collection;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Predicate;
@@ -37,6 +39,7 @@
import org.apache.maven.api.Project;
import org.apache.maven.api.RemoteRepository;
import org.apache.maven.api.Session;
+import org.apache.maven.api.Version;
import org.apache.maven.api.annotations.Nonnull;
import org.apache.maven.api.annotations.Nullable;
import org.apache.maven.api.di.Named;
@@ -70,18 +73,41 @@ public class DefaultDependencyResolver implements
DependencyResolver {
/**
* Cache of information about the modules contained in a path element.
+ * Keys are the Java versions targeted by the project.
*
* <p><b>TODO:</b> This field should not be in this class, because the
cache should be global to the session.
* This field exists here only temporarily, until clarified where to store
session-wide caches.</p>
*/
- private final PathModularizationCache moduleCache;
+ private final Map<Runtime.Version, PathModularizationCache> moduleCaches;
/**
* Creates an initially empty resolver.
*/
public DefaultDependencyResolver() {
// TODO: the cache should not be instantiated here, but should rather
be session-wide.
- moduleCache = new PathModularizationCache();
+ moduleCaches = new HashMap<>();
+ }
+
+ /**
+ * {@return the cache for the given request}.
+ *
+ * @param request the request for which to get the target version
+ * @throws IllegalArgumentException if the version string cannot be
interpreted as a valid version
+ */
+ private PathModularizationCache moduleCache(DependencyResolverRequest
request) {
+ return moduleCaches.computeIfAbsent(getTargetVersion(request),
PathModularizationCache::new);
+ }
+
+ /**
+ * Returns the target version of the given request as a Java version
object.
+ *
+ * @param request the request for which to get the target version
+ * @return the target version as a Java object
+ * @throws IllegalArgumentException if the version string cannot be
interpreted as a valid version
+ */
+ static Runtime.Version getTargetVersion(DependencyResolverRequest request)
{
+ Version target = request.getTargetVersion();
+ return (target != null) ? Runtime.Version.parse(target.toString()) :
Runtime.version();
}
@Nonnull
@@ -143,7 +169,7 @@ public DependencyResolverResult collect(@Nonnull
DependencyResolverRequest reque
session.getRepositorySystem().collectDependencies(systemSession,
collectRequest);
return new DefaultDependencyResolverResult(
null,
- moduleCache,
+ moduleCache(request),
result.getExceptions(),
session.getNode(result.getRoot(),
request.getVerbose()),
0);
@@ -212,7 +238,11 @@ public DependencyResolverResult
resolve(DependencyResolverRequest request)
.collect(Collectors.toList());
Predicate<PathType> filter = request.getPathTypeFilter();
DefaultDependencyResolverResult resolverResult = new
DefaultDependencyResolverResult(
- null, moduleCache, collectorResult.getExceptions(),
collectorResult.getRoot(), nodes.size());
+ null,
+ moduleCache(request),
+ collectorResult.getExceptions(),
+ collectorResult.getRoot(),
+ nodes.size());
if (request.getRequestType() ==
DependencyResolverRequest.RequestType.FLATTEN) {
for (Node node : nodes) {
resolverResult.addNode(node);
diff --git
a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultDependencyResolverResult.java
b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultDependencyResolverResult.java
index 4b67c9ee32..a97062ae5a 100644
---
a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultDependencyResolverResult.java
+++
b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultDependencyResolverResult.java
@@ -114,7 +114,12 @@ public class DefaultDependencyResolverResult implements
DependencyResolverResult
*/
public DefaultDependencyResolverResult(
DependencyResolverRequest request, List<Exception> exceptions,
Node root, int count) {
- this(request, new PathModularizationCache(), exceptions, root, count);
+ this(
+ request,
+ new
PathModularizationCache(DefaultDependencyResolver.getTargetVersion(request)),
+ exceptions,
+ root,
+ count);
}
/**
diff --git
a/impl/maven-impl/src/main/java/org/apache/maven/impl/PathModularization.java
b/impl/maven-impl/src/main/java/org/apache/maven/impl/PathModularization.java
index db0c950995..a40e57215a 100644
---
a/impl/maven-impl/src/main/java/org/apache/maven/impl/PathModularization.java
+++
b/impl/maven-impl/src/main/java/org/apache/maven/impl/PathModularization.java
@@ -43,7 +43,7 @@
* or module hierarchy, but not module source hierarchy. The latter is
excluded because this class
* is for path elements of compiled codes.
*/
-class PathModularization {
+final class PathModularization {
/**
* A unique constant for all non-modular dependencies.
*/
@@ -132,10 +132,11 @@ private PathModularization() {
* Otherwise builds an empty map.
*
* @param path directory or JAR file to test
+ * @param target the target Java release for which the project is built
* @param resolve whether the module names are requested. If false, null
values may be used instead
* @throws IOException if an error occurred while reading the JAR file or
the module descriptor
*/
- PathModularization(Path path, boolean resolve) throws IOException {
+ PathModularization(Path path, Runtime.Version target, boolean resolve)
throws IOException {
filename = path.getFileName().toString();
if (Files.isDirectory(path)) {
/*
@@ -192,7 +193,7 @@ private PathModularization() {
* If no descriptor, the "Automatic-Module-Name" manifest
attribute is
* taken as a fallback.
*/
- try (JarFile jar = new JarFile(path.toFile())) {
+ try (JarFile jar = new JarFile(path.toFile(), false,
JarFile.OPEN_READ, target)) {
ZipEntry entry = jar.getEntry(MODULE_INFO);
if (entry != null) {
ModuleDescriptor descriptor = null;
diff --git
a/impl/maven-impl/src/main/java/org/apache/maven/impl/PathModularizationCache.java
b/impl/maven-impl/src/main/java/org/apache/maven/impl/PathModularizationCache.java
index e04ce136da..3ab71dc33c 100644
---
a/impl/maven-impl/src/main/java/org/apache/maven/impl/PathModularizationCache.java
+++
b/impl/maven-impl/src/main/java/org/apache/maven/impl/PathModularizationCache.java
@@ -24,6 +24,7 @@
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
+import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.StringJoiner;
@@ -38,7 +39,7 @@
* same dependency is used for different scope. For example a path used for
compilation
* is typically also used for tests.
*/
-class PathModularizationCache {
+final class PathModularizationCache {
/**
* Module information for each JAR file or output directories.
* Cached when first requested to avoid decoding the module descriptors
multiple times.
@@ -55,12 +56,21 @@ class PathModularizationCache {
*/
private final Map<Path, PathType> pathTypes;
+ /**
+ * The target Java version for which the project is built.
+ * If unknown, it should be {@link Runtime#version()}.
+ */
+ private final Runtime.Version targetVersion;
+
/**
* Creates an initially empty cache.
+ *
+ * @param target the target Java release for which the project is built
*/
- PathModularizationCache() {
+ PathModularizationCache(Runtime.Version target) {
moduleInfo = new HashMap<>();
pathTypes = new HashMap<>();
+ targetVersion = Objects.requireNonNull(target);
}
/**
@@ -70,7 +80,7 @@ class PathModularizationCache {
PathModularization getModuleInfo(Path path) throws IOException {
PathModularization info = moduleInfo.get(path);
if (info == null) {
- info = new PathModularization(path, true);
+ info = new PathModularization(path, targetVersion, true);
moduleInfo.put(path, info);
pathTypes.put(path, info.getPathType());
}
@@ -85,7 +95,7 @@ PathModularization getModuleInfo(Path path) throws
IOException {
private PathType getPathType(Path path) throws IOException {
PathType type = pathTypes.get(path);
if (type == null) {
- type = new PathModularization(path, false).getPathType();
+ type = new PathModularization(path, targetVersion,
false).getPathType();
pathTypes.put(path, type);
}
return type;