Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -181,6 +205,7 @@ class DependencyResolverRequestBuilder {
boolean verbose;
PathScope pathScope;
Predicate<PathType> pathTypeFilter;
Version targetVersion;
List<RemoteRepository> repositories;

DependencyResolverRequestBuilder() {}
Expand Down Expand Up @@ -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;
Expand All @@ -365,6 +402,7 @@ public DependencyResolverRequest build() {
verbose,
pathScope,
pathTypeFilter,
targetVersion,
repositories);
}

Expand Down Expand Up @@ -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;

/**
Expand All @@ -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");
Expand All @@ -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");
Expand Down Expand Up @@ -495,6 +536,11 @@ public Predicate<PathType> getPathTypeFilter() {
return pathTypeFilter;
}

@Override
public Version getTargetVersion() {
return targetVersion;
}

@Override
public List<RemoteRepository> getRepositories() {
return repositories;
Expand All @@ -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);
}

Expand All @@ -527,6 +574,7 @@ public int hashCode() {
verbose,
pathScope,
pathTypeFilter,
targetVersion,
repositories);
}

Expand All @@ -541,7 +589,8 @@ public String toString() {
+ managedDependencies + ", verbose="
+ verbose + ", pathScope="
+ pathScope + ", pathTypeFilter="
+ pathTypeFilter + ", repositories="
+ pathTypeFilter + ", targetVersion="
+ targetVersion + ", repositories="
+ repositories + ']';
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down Expand Up @@ -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)) {
/*
Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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.
Expand All @@ -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);
}

/**
Expand All @@ -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());
}
Expand All @@ -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;
Expand Down
Loading