You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by gn...@apache.org on 2026/09/01 09:20:35 UTC
(maven) branch apirunner-is-the-main-entry-point-to-use-maven-4-s created (now 5f2ad8d4da)
This is an automated email from the ASF dual-hosted git repository.
gnodet pushed a change to branch apirunner-is-the-main-entry-point-to-use-maven-4-s
in repository https://gitbox.apache.org/repos/asf/maven.git
at 5f2ad8d4da [MNG-8xxx] Improve standalone ApiRunner to properly apply settings
This branch includes the following new commits:
new 5f2ad8d4da [MNG-8xxx] Improve standalone ApiRunner to properly apply settings
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(maven) 01/01: [MNG-8xxx] Improve standalone ApiRunner to properly apply settings
Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
gnodet pushed a commit to branch apirunner-is-the-main-entry-point-to-use-maven-4-s
in repository https://gitbox.apache.org/repos/asf/maven.git
commit 5f2ad8d4da10c1291544933ea63887400313db44
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Tue Sep 1 11:20:21 2026 +0200
[MNG-8xxx] Improve standalone ApiRunner to properly apply settings
The standalone ApiRunner is the main entry point for using the Maven 4
API outside of a full Maven build (e.g. in tools like mvnup). Previously
it had several gaps that made it unsuitable for production use:
- Hardcoded user.home to "target" (meant for unit tests only)
- Settings were built but never stored on the session (getSettings()
always returned empty)
- No proxy, mirror, or server authentication support
- Missing dependency resolution machinery (DependencySelector,
DependencyGraphTransformer, DependencyManager, etc.)
- getMavenVersion() returned null, violating @Nonnull contract
- Offline mode from settings was ignored
This commit fixes all of these issues:
- Remove the hardcoded user.home override so real settings.xml is loaded
- Store effective settings on the session so getSettings() returns them
- Apply proxy configuration from settings via DefaultProxySelector
- Apply mirror configuration from settings via DefaultMirrorSelector
- Apply server authentication from settings via DefaultAuthenticationSelector
- Configure dependency resolution via MavenSessionBuilderSupplier
(DependencyTraverser, DependencyManager, DependencySelector,
DependencyGraphTransformer, ArtifactTypeRegistry, ArtifactDescriptorPolicy)
- Detect and set Maven version from classpath pom.properties
- Honor offline mode from settings
- Clean up stale TODO comments and dead commented-out code
Also makes 5 getter methods in MavenSessionBuilderSupplier public
(from protected) so they can be reused by ApiRunner across packages.
Co-Authored-By: Claude Opus 4.6 <no...@anthropic.com>
---
.../impl/resolver/MavenSessionBuilderSupplier.java | 10 +-
.../apache/maven/impl/standalone/ApiRunner.java | 166 +++++++++++++++++----
2 files changed, 146 insertions(+), 30 deletions(-)
diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/resolver/MavenSessionBuilderSupplier.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/resolver/MavenSessionBuilderSupplier.java
index b6a0a711e8..f18d9f09a5 100644
--- a/impl/maven-impl/src/main/java/org/apache/maven/impl/resolver/MavenSessionBuilderSupplier.java
+++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/resolver/MavenSessionBuilderSupplier.java
@@ -80,7 +80,7 @@ public MavenSessionBuilderSupplier(RepositorySystem repositorySystem, boolean ma
: Maven4ScopeManagerConfiguration.INSTANCE);
}
- protected DependencyTraverser getDependencyTraverser() {
+ public DependencyTraverser getDependencyTraverser() {
return new FatArtifactTraverser();
}
@@ -99,7 +99,7 @@ public DependencyManager getDependencyManager(boolean transitive) {
return new ClassicDependencyManager(getScopeManager());
}
- protected DependencySelector getDependencySelector() {
+ public DependencySelector getDependencySelector() {
return new AndDependencySelector(
ScopeDependencySelector.legacy(
null, Arrays.asList(DependencyScope.TEST.id(), DependencyScope.PROVIDED.id())),
@@ -107,7 +107,7 @@ protected DependencySelector getDependencySelector() {
new ExclusionDependencySelector());
}
- protected DependencyGraphTransformer getDependencyGraphTransformer() {
+ public DependencyGraphTransformer getDependencyGraphTransformer() {
return new ChainedDependencyGraphTransformer(
new TypeCollector(),
new ConflictResolver(
@@ -126,13 +126,13 @@ protected DependencyGraphTransformer getDependencyGraphTransformer() {
* <p>
* Important: this "static" list of types should be in-sync with core provided types.
*/
- protected ArtifactTypeRegistry getArtifactTypeRegistry() {
+ public ArtifactTypeRegistry getArtifactTypeRegistry() {
DefaultArtifactTypeRegistry stereotypes = new DefaultArtifactTypeRegistry();
new DefaultTypeProvider().types().forEach(t -> stereotypes.add(t.toArtifactType()));
return stereotypes;
}
- protected ArtifactDescriptorPolicy getArtifactDescriptorPolicy() {
+ public ArtifactDescriptorPolicy getArtifactDescriptorPolicy() {
return new SimpleArtifactDescriptorPolicy(true, true);
}
diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/standalone/ApiRunner.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/standalone/ApiRunner.java
index 81ac6ec0c2..242bacee21 100644
--- a/impl/maven-impl/src/main/java/org/apache/maven/impl/standalone/ApiRunner.java
+++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/standalone/ApiRunner.java
@@ -18,6 +18,8 @@
*/
package org.apache.maven.impl.standalone;
+import java.io.IOException;
+import java.io.InputStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Instant;
@@ -29,6 +31,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
+import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@@ -57,6 +60,10 @@
import org.apache.maven.api.services.RepositoryFactory;
import org.apache.maven.api.services.SettingsBuilder;
import org.apache.maven.api.services.TypeRegistry;
+import org.apache.maven.api.services.VersionParser;
+import org.apache.maven.api.settings.Mirror;
+import org.apache.maven.api.settings.Proxy;
+import org.apache.maven.api.settings.Server;
import org.apache.maven.api.settings.Settings;
import org.apache.maven.api.spi.TypeProvider;
import org.apache.maven.api.toolchain.ToolchainModel;
@@ -66,6 +73,7 @@
import org.apache.maven.impl.AbstractSession;
import org.apache.maven.impl.InternalSession;
import org.apache.maven.impl.di.SessionScope;
+import org.apache.maven.impl.resolver.MavenSessionBuilderSupplier;
import org.apache.maven.impl.resolver.scopes.Maven4ScopeManagerConfiguration;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositorySystem;
@@ -73,16 +81,30 @@
import org.eclipse.aether.internal.impl.scope.ScopeManagerImpl;
import org.eclipse.aether.repository.LocalRepository;
import org.eclipse.aether.repository.LocalRepositoryManager;
+import org.eclipse.aether.util.repository.AuthenticationBuilder;
+import org.eclipse.aether.util.repository.DefaultAuthenticationSelector;
+import org.eclipse.aether.util.repository.DefaultMirrorSelector;
+import org.eclipse.aether.util.repository.DefaultProxySelector;
/**
* Provides functionality for running Maven API in a standalone mode.
* <p>
* This class serves as the main entry point for executing Maven operations outside
* of the standard Maven build environment. It provides methods for creating and
- * managing Maven sessions in a simplified context, primarily for testing and
- * specialized execution scenarios.
+ * managing Maven sessions in a simplified context, suitable for tools, IDE integrations,
+ * and specialized execution scenarios.
* </p>
*
+ * <p>The standalone session reads and applies the user's {@code settings.xml} including:</p>
+ * <ul>
+ * <li>Local repository location</li>
+ * <li>Server authentication (username, password, private key)</li>
+ * <li>Proxy configuration</li>
+ * <li>Mirror configuration</li>
+ * <li>Repository definitions from active profiles</li>
+ * <li>Offline mode</li>
+ * </ul>
+ *
* <p>Example usage:</p>
* <pre>
* Session session = ApiRunner.createSession();
@@ -162,6 +184,8 @@ static class DefaultSession extends AbstractSession {
private final Map<String, String> systemProperties;
private final Instant startTime = MonotonicClock.now();
+ private Settings settings;
+ private Version mavenVersion;
DefaultSession(RepositorySystemSession session, RepositorySystem repositorySystem, Lookup lookup) {
this(session, repositorySystem, Collections.emptyList(), null, lookup);
@@ -181,12 +205,24 @@ protected DefaultSession(
@Override
protected Session newSession(RepositorySystemSession session, List<RemoteRepository> repositories) {
- return new DefaultSession(session, repositorySystem, repositories, null, lookup);
+ DefaultSession newSession = new DefaultSession(session, repositorySystem, repositories, null, lookup);
+ newSession.settings = this.settings;
+ newSession.mavenVersion = this.mavenVersion;
+ return newSession;
+ }
+
+ void setSettings(Settings settings) {
+ this.settings = settings;
+ }
+
+ void setMavenVersion(Version mavenVersion) {
+ this.mavenVersion = mavenVersion;
}
@Override
+ @Nonnull
public Settings getSettings() {
- return Settings.newInstance();
+ return settings != null ? settings : Settings.newInstance();
}
@Override
@@ -217,7 +253,7 @@ public Map<String, String> getEffectiveProperties(Project project) {
@Override
public Version getMavenVersion() {
- return null;
+ return mavenVersion;
}
@Override
@@ -372,18 +408,22 @@ static Session newSession(RepositorySystem system, Lookup lookup, @Nullable Loca
// Java System properties
System.getProperties().forEach((k, v) -> properties.put(k.toString(), v.toString()));
- // Do not allow user settings to interfere with our unit tests
- // TODO: remove that when this go more public
- properties.put("user.home", "target");
-
Path userHome = Paths.get(properties.get("user.home"));
Path mavenUserHome = userHome.resolve(".m2");
Path mavenSystemHome = properties.containsKey("maven.home")
? Paths.get(properties.get("maven.home"))
: properties.containsKey("env.MAVEN_HOME") ? Paths.get(properties.get("env.MAVEN_HOME")) : null;
+ // Configure the resolver session with dependency resolution machinery
+ MavenSessionBuilderSupplier sessionBuilderSupplier = new MavenSessionBuilderSupplier(system, false);
DefaultRepositorySystemSession rsession = new DefaultRepositorySystemSession(h -> false);
rsession.setScopeManager(new ScopeManagerImpl(Maven4ScopeManagerConfiguration.INSTANCE));
+ rsession.setDependencyTraverser(sessionBuilderSupplier.getDependencyTraverser());
+ rsession.setDependencyManager(sessionBuilderSupplier.getDependencyManager(true));
+ rsession.setDependencySelector(sessionBuilderSupplier.getDependencySelector());
+ rsession.setDependencyGraphTransformer(sessionBuilderSupplier.getDependencyGraphTransformer());
+ rsession.setArtifactTypeRegistry(sessionBuilderSupplier.getArtifactTypeRegistry());
+ rsession.setArtifactDescriptorPolicy(sessionBuilderSupplier.getArtifactDescriptorPolicy());
rsession.setSystemProperties(properties);
rsession.setConfigProperties(properties);
@@ -402,6 +442,12 @@ static Session newSession(RepositorySystem system, Lookup lookup, @Nullable Loca
mavenUserHome.resolve("settings.xml"))
.getEffectiveSettings();
+ // Store the effective settings on the session
+ session.setSettings(settings);
+
+ // Set the Maven version
+ session.setMavenVersion(detectMavenVersion(lookup));
+
// local repository
String localRepository = settings.getLocalRepository() != null
&& !settings.getLocalRepository().isEmpty()
@@ -411,15 +457,56 @@ static Session newSession(RepositorySystem system, Lookup lookup, @Nullable Loca
: mavenUserHome.resolve("repository").toString();
LocalRepositoryManager llm = system.newLocalRepositoryManager(rsession, new LocalRepository(localRepository));
rsession.setLocalRepositoryManager(llm);
- // active proxies
- // TODO
- // active profiles
-
- Profile profile = session.getService(SettingsBuilder.class)
- .convert(org.apache.maven.api.settings.Profile.newBuilder()
- .repositories(settings.getRepositories())
- .pluginRepositories(settings.getPluginRepositories())
- .build());
+
+ // Apply offline mode from settings
+ if (settings.isOffline()) {
+ rsession.setOffline(true);
+ }
+
+ // Apply proxy configuration from settings
+ DefaultProxySelector proxySelector = new DefaultProxySelector();
+ for (Proxy proxy : settings.getProxies()) {
+ if (proxy.isActive()) {
+ AuthenticationBuilder authBuilder = new AuthenticationBuilder();
+ authBuilder.addUsername(proxy.getUsername()).addPassword(proxy.getPassword());
+ proxySelector.add(
+ new org.eclipse.aether.repository.Proxy(
+ proxy.getProtocol(), proxy.getHost(), proxy.getPort(), authBuilder.build()),
+ proxy.getNonProxyHosts());
+ }
+ }
+ rsession.setProxySelector(proxySelector);
+
+ // Apply mirror configuration from settings
+ DefaultMirrorSelector mirrorSelector = new DefaultMirrorSelector();
+ for (Mirror mirror : settings.getMirrors()) {
+ mirrorSelector.add(
+ mirror.getId(),
+ mirror.getUrl(),
+ mirror.getLayout(),
+ false,
+ mirror.isBlocked(),
+ mirror.getMirrorOf(),
+ mirror.getMirrorOfLayouts());
+ }
+ rsession.setMirrorSelector(mirrorSelector);
+
+ // Apply server authentication from settings
+ DefaultAuthenticationSelector authSelector = new DefaultAuthenticationSelector();
+ for (Server server : settings.getServers()) {
+ AuthenticationBuilder authBuilder = new AuthenticationBuilder();
+ authBuilder.addUsername(server.getUsername()).addPassword(server.getPassword());
+ authBuilder.addPrivateKey(server.getPrivateKey(), server.getPassphrase());
+ authSelector.add(server.getId(), authBuilder.build());
+ }
+ rsession.setAuthenticationSelector(authSelector);
+
+ // Build repositories from active profiles in settings
+ SettingsBuilder settingsBuilder = session.getService(SettingsBuilder.class);
+ Profile profile = settingsBuilder.convert(org.apache.maven.api.settings.Profile.newBuilder()
+ .repositories(settings.getRepositories())
+ .pluginRepositories(settings.getPluginRepositories())
+ .build());
RepositoryFactory repositoryFactory = session.getService(RepositoryFactory.class);
List<RemoteRepository> repositories = profile.getRepositories().stream()
.map(repositoryFactory::createRemote)
@@ -427,15 +514,44 @@ static Session newSession(RepositorySystem system, Lookup lookup, @Nullable Loca
InternalSession s = (InternalSession) session.withRemoteRepositories(repositories);
InternalSession.associate(rsession, s);
return s;
+ }
- // List<RemoteRepository> repositories = repositoryFactory.createRemote();
-
- // session.getService(SettingsBuilder.class).convert()
+ /**
+ * Detects the Maven version by reading the pom.properties resource from the classpath.
+ * Falls back to reading from maven-impl's own pom.properties if maven-core is not available.
+ *
+ * @param lookup the lookup service
+ * @return the detected Maven version, or {@code null} if it cannot be determined
+ */
+ private static Version detectMavenVersion(Lookup lookup) {
+ String version = loadVersionFromProperties("META-INF/maven/org.apache.maven/maven-core/pom.properties");
+ if (version == null) {
+ version = loadVersionFromProperties("META-INF/maven/org.apache.maven/maven-impl/pom.properties");
+ }
+ if (version != null) {
+ try {
+ return lookup.lookup(VersionParser.class).parseVersion(version);
+ } catch (Exception e) {
+ // ignore parse errors
+ }
+ }
+ return null;
+ }
- // settings.getDelegate().getRepositories().stream()
- // .map(r -> SettingsUtilsV4.)
- // defaultSession.getService(RepositoryFactory.class).createRemote()
- // return defaultSession;
+ private static String loadVersionFromProperties(String resource) {
+ try (InputStream is = ApiRunner.class.getResourceAsStream("/" + resource)) {
+ if (is != null) {
+ Properties props = new Properties();
+ props.load(is);
+ String version = props.getProperty("version", "").trim();
+ if (!version.isEmpty() && !version.startsWith("${")) {
+ return version;
+ }
+ }
+ } catch (IOException e) {
+ // ignore
+ }
+ return null;
}
record DumbPackaging(String id, Type type, Map<String, PluginContainer> plugins) implements Packaging {}