Skip to content

Commit

Permalink
Maven Resolver: initialize Maven properly, especially the local repos…
Browse files Browse the repository at this point in the history
…itory

The `BootstrapMavenContext.newRepositorySystemSession()` method is now
a lot closer to `DefaultRepositorySystemSessionFactory.newRepositorySession()`,
especially around how it initializes the `LocalRepositoryManager`. It is now
done _after_ setting up the config properties on the `RepositorySystemSession`,
which has also been fixed (to include the properties of all active profiles,
including those in `settings.xml`).

Furhter, this commit renames 2 files to match the name of the (non-`public`)
class declared in them, and fixes self type emulation in `BootstrapMavenContextConfig`.
  • Loading branch information
Ladicek committed Jan 23, 2025
1 parent 1c5b939 commit 9e56392
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 85 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
Expand All @@ -16,11 +17,13 @@
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import org.apache.maven.cli.transfer.BatchModeMavenTransferListener;
import org.apache.maven.cli.transfer.ConsoleMavenTransferListener;
import org.apache.maven.cli.transfer.QuietMavenTransferListener;
import org.apache.maven.model.Model;
import org.apache.maven.model.ModelBase;
import org.apache.maven.model.building.ModelBuilder;
import org.apache.maven.model.building.ModelProblemCollector;
import org.apache.maven.model.building.ModelProblemCollectorRequest;
Expand All @@ -45,7 +48,6 @@
import org.apache.maven.settings.building.SettingsProblem;
import org.apache.maven.settings.crypto.DefaultSettingsDecryptionRequest;
import org.apache.maven.settings.crypto.SettingsDecrypter;
import org.apache.maven.settings.crypto.SettingsDecryptionRequest;
import org.apache.maven.settings.crypto.SettingsDecryptionResult;
import org.codehaus.plexus.configuration.PlexusConfiguration;
import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration;
Expand All @@ -59,10 +61,8 @@
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.impl.RemoteRepositoryManager;
import org.eclipse.aether.repository.ArtifactRepository;
import org.eclipse.aether.repository.Authentication;
import org.eclipse.aether.repository.LocalRepository;
import org.eclipse.aether.repository.LocalRepositoryManager;
import org.eclipse.aether.repository.Proxy;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.repository.RepositoryPolicy;
import org.eclipse.aether.resolution.ArtifactDescriptorException;
Expand Down Expand Up @@ -488,33 +488,27 @@ private File resolveSettingsFile(String settingsArg, String alternatePomDir, Str
return null;
}

// mostly a copy of `DefaultRepositorySystemSessionFactory.newRepositorySession()`
private DefaultRepositorySystemSession newRepositorySystemSession() throws BootstrapMavenException {
final DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession();
final Settings settings = getEffectiveSettings();
final List<Mirror> mirrors = settings.getMirrors();
if (mirrors != null && !mirrors.isEmpty()) {
final DefaultMirrorSelector ms = new DefaultMirrorSelector();
for (Mirror m : mirrors) {
ms.add(m.getId(), m.getUrl(), m.getLayout(), false, m.isBlocked(), m.getMirrorOf(), m.getMirrorOfLayouts());
}
session.setMirrorSelector(ms);
}
DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession();
Settings settings = getEffectiveSettings();

final String localRepoPath = getLocalRepo();
final String[] localRepoTailPaths = getLocalRepoTail();
session.setCache(new DefaultRepositoryCache());

final LocalRepositoryManager head = getRepositorySystem().newLocalRepositoryManager(session,
new LocalRepository(localRepoPath));

if (localRepoTailPaths.length == 0) {
session.setLocalRepositoryManager(head);
} else {
final List<LocalRepositoryManager> tail = new ArrayList<>(localRepoTailPaths.length);
for (final String tailPath : localRepoTailPaths) {
tail.add(getRepositorySystem().newLocalRepositoryManager(session, new LocalRepository(tailPath)));
}
session.setLocalRepositoryManager(
new ChainedLocalRepositoryManager(head, tail, getLocalRepoTailIgnoreAvailability()));
Map<Object, Object> configProps = new LinkedHashMap<>();
configProps.put(ConfigurationProperties.USER_AGENT, getUserAgent());
configProps.put(ConfigurationProperties.INTERACTIVE, settings.isInteractiveMode());
// First add properties populated from settings.xml
Map<?, ?> propertiesFromActiveProfiles = getActiveSettingsProfiles()
.stream()
.map(ModelBase::getProperties)
.flatMap(it -> it.entrySet().stream())
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> b));
configProps.putAll(propertiesFromActiveProfiles);
// Resolver's ConfigUtils solely rely on config properties, that is why we need to add both here as well.
configProps.putAll(System.getProperties());
if (getCliOptions().getSystemProperties() != null) {
configProps.putAll(getCliOptions().getSystemProperties());
}

session.setOffline(isOffline());
Expand All @@ -533,40 +527,46 @@ private DefaultRepositorySystemSession newRepositorySystemSession() throws Boots
}
}

final SettingsDecryptionRequest decrypt = new DefaultSettingsDecryptionRequest();
if (workspace != null) {
session.setWorkspaceReader(workspace);
}

DefaultSettingsDecryptionRequest decrypt = new DefaultSettingsDecryptionRequest();
decrypt.setProxies(settings.getProxies());
decrypt.setServers(settings.getServers());
// set settings.security property to ~/.m2/settings-security.xml unless it's already set to some other value
File settingsSecurityXml = null;
final boolean setSettingsSecurity = !System.getProperties().contains(SETTINGS_SECURITY)
&& ((settingsSecurityXml = new File(getUserMavenConfigurationHome(), "settings-security.xml")).exists());
if (setSettingsSecurity) {
System.setProperty(SETTINGS_SECURITY, settingsSecurityXml.toString());
}
final SettingsDecryptionResult decrypted = getSettingsDecrypter().decrypt(decrypt);
if (setSettingsSecurity) {
System.clearProperty(SETTINGS_SECURITY);
}
SettingsDecryptionResult decrypted = getSettingsDecrypter().decrypt(decrypt);

if (!decrypted.getProblems().isEmpty() && log.isDebugEnabled()) {
// this is how maven handles these
for (SettingsProblem p : decrypted.getProblems()) {
log.debug(p.getMessage(), p.getException());
for (SettingsProblem problem : decrypted.getProblems()) {
log.debug(problem.getMessage(), problem.getException());
}
}

final DefaultProxySelector proxySelector = new DefaultProxySelector();
for (org.apache.maven.settings.Proxy p : decrypted.getProxies()) {
if (p.isActive()) {
proxySelector.add(toAetherProxy(p), p.getNonProxyHosts());
}
DefaultMirrorSelector mirrorSelector = new DefaultMirrorSelector();
for (Mirror mirror : settings.getMirrors()) {
mirrorSelector.add(
mirror.getId(),
mirror.getUrl(),
mirror.getLayout(),
false,
mirror.isBlocked(),
mirror.getMirrorOf(),
mirror.getMirrorOfLayouts());
}
session.setProxySelector(proxySelector);
session.setMirrorSelector(mirrorSelector);

final Map<Object, Object> configProps = new LinkedHashMap<>(session.getConfigProperties());
configProps.put(ConfigurationProperties.USER_AGENT, getUserAgent());
configProps.put(ConfigurationProperties.INTERACTIVE, settings.isInteractiveMode());
DefaultProxySelector proxySelector = new DefaultProxySelector();
for (org.apache.maven.settings.Proxy proxy : decrypted.getProxies()) {
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());
}
session.setProxySelector(proxySelector);

final DefaultAuthenticationSelector authSelector = new DefaultAuthenticationSelector();
DefaultAuthenticationSelector authSelector = new DefaultAuthenticationSelector();
for (Server server : decrypted.getServers()) {
AuthenticationBuilder authBuilder = new AuthenticationBuilder();
authBuilder.addUsername(server.getUsername()).addPassword(server.getPassword());
Expand All @@ -581,6 +581,7 @@ private DefaultRepositorySystemSession newRepositorySystemSession() throws Boots
dom.removeChild(i);
}
}

XmlPlexusConfiguration config = new XmlPlexusConfiguration(dom);
configProps.put("aether.connector.wagon.config." + server.getId(), config);

Expand Down Expand Up @@ -653,6 +654,7 @@ private DefaultRepositorySystemSession newRepositorySystemSession() throws Boots
configProps.put(ConfigurationProperties.REQUEST_TIMEOUT + "." + server.getId(), requestTimeout);
}
}

configProps.put("aether.connector.perms.fileMode." + server.getId(), server.getFilePermissions());
configProps.put("aether.connector.perms.dirMode." + server.getId(), server.getDirectoryPermissions());
}
Expand All @@ -674,17 +676,11 @@ private DefaultRepositorySystemSession newRepositorySystemSession() throws Boots
+ MAVEN_RESOLVER_TRANSPORT_NATIVE + ", " + MAVEN_RESOLVER_TRANSPORT_AUTO);
}

session.setUserProperties(getCliOptions().getSystemProperties());
session.setSystemProperties(System.getProperties());
session.setConfigProperties(configProps);

if (session.getCache() == null) {
session.setCache(new DefaultRepositoryCache());
}

if (workspace != null) {
session.setWorkspaceReader(workspace);
}

if (session.getTransferListener() == null && artifactTransferLogging) {
if (artifactTransferLogging) {
TransferListener transferListener;
if (mvnArgs.hasOption(BootstrapMavenOptions.NO_TRANSFER_PROGRESS)) {
transferListener = new QuietMavenTransferListener();
Expand All @@ -697,13 +693,30 @@ private DefaultRepositorySystemSession newRepositorySystemSession() throws Boots
session.setTransferListener(transferListener);
}

for (var e : System.getProperties().entrySet()) {
session.setSystemProperty(e.getKey().toString(), e.getValue().toString());
}
setUpLocalRepositoryManager(session);

return session;
}

private void setUpLocalRepositoryManager(DefaultRepositorySystemSession session) throws BootstrapMavenException {
String localRepoPath = getLocalRepo();
String[] localRepoTailPaths = getLocalRepoTail();

LocalRepositoryManager head = getRepositorySystem().newLocalRepositoryManager(session,
new LocalRepository(localRepoPath));

if (localRepoTailPaths.length == 0) {
session.setLocalRepositoryManager(head);
} else {
List<LocalRepositoryManager> tail = new ArrayList<>(localRepoTailPaths.length);
for (String tailPath : localRepoTailPaths) {
tail.add(getRepositorySystem().newLocalRepositoryManager(session, new LocalRepository(tailPath)));
}
session.setLocalRepositoryManager(
new ChainedLocalRepositoryManager(head, tail, getLocalRepoTailIgnoreAvailability()));
}
}

private List<RemoteRepository> resolveRemoteRepos() throws BootstrapMavenException {
final List<RemoteRepository> rawRepos = new ArrayList<>();
readMavenReposFromEnv(rawRepos, System.getenv());
Expand Down Expand Up @@ -905,26 +918,6 @@ private static boolean isEmpty(final CharSequence cs) {
return cs == null || cs.length() == 0;
}

/**
* Convert a {@link org.apache.maven.settings.Proxy} to a {@link Proxy}.
*
* @param proxy Maven proxy settings, may be {@code null}.
* @return Aether repository proxy or {@code null} if given {@link org.apache.maven.settings.Proxy} is {@code null}.
*/
private static Proxy toAetherProxy(org.apache.maven.settings.Proxy proxy) {
if (proxy == null) {
return null;
}
Authentication auth = null;
if (proxy.getUsername() != null) {
auth = new AuthenticationBuilder()
.addUsername(proxy.getUsername())
.addPassword(proxy.getPassword())
.build();
}
return new Proxy(proxy.getProtocol(), proxy.getHost(), proxy.getPort(), auth);
}

private void initRepoSystemAndManager() {
final MavenFactory factory = configureMavenFactory();
if (repoSystem == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import io.quarkus.bootstrap.resolver.maven.options.BootstrapMavenOptions;
import io.quarkus.bootstrap.resolver.maven.workspace.LocalProject;

public class BootstrapMavenContextConfig<T extends BootstrapMavenContextConfig<?>> {
public class BootstrapMavenContextConfig<T extends BootstrapMavenContextConfig<T>> {

/**
* Resolves the effective value of the {@code effective-model-builder} option by looking for the
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package io.quarkus.bootstrap.resolver.maven.test;

import static org.junit.jupiter.api.Assertions.assertEquals;

import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.repository.LocalRepositoryManager;
import org.eclipse.aether.repository.RemoteRepository;
import org.junit.jupiter.api.Test;

import io.quarkus.bootstrap.resolver.maven.BootstrapMavenContext;

public class SplitLocalRepositoryTest extends BootstrapMavenContextTestBase {
@Test
public void test() throws Exception {
BootstrapMavenContext mvn = bootstrapMavenContextForProject("custom-settings/split-local-repository");
LocalRepositoryManager lrm = mvn.getRepositorySystemSession().getLocalRepositoryManager();

assertEquals("installed/releases/foo/bar/1.0/bar-1.0.jar",
lrm.getPathForLocalArtifact(new DefaultArtifact("foo:bar:1.0")));
assertEquals("installed/snapshots/foo/bar/1.0-SNAPSHOT/bar-1.0-SNAPSHOT.jar",
lrm.getPathForLocalArtifact(new DefaultArtifact("foo:bar:1.0-SNAPSHOT")));

RemoteRepository remoteRepo = new RemoteRepository.Builder("remote-repo", "default", "https://example.com/repo/")
.build();

assertEquals("cached/releases/foo/bar/1.0/bar-1.0.jar",
lrm.getPathForRemoteArtifact(new DefaultArtifact("foo:bar:1.0"), remoteRepo, null));
assertEquals("cached/snapshots/foo/bar/1.0-SNAPSHOT/bar-1.0-SNAPSHOT.jar",
lrm.getPathForRemoteArtifact(new DefaultArtifact("foo:bar:1.0-SNAPSHOT"), remoteRepo, null));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<settings xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.1.0 http://maven.apache.org/xsd/settings-1.1.0.xsd"
xmlns="http://maven.apache.org/SETTINGS/1.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<profiles>
<profile>
<id>split-local-repo</id>
<properties>
<aether.enhancedLocalRepository.split>true</aether.enhancedLocalRepository.split>
<aether.enhancedLocalRepository.splitLocal>true</aether.enhancedLocalRepository.splitLocal>
<aether.enhancedLocalRepository.splitRemote>true</aether.enhancedLocalRepository.splitRemote>
</properties>
</profile>
</profiles>
<activeProfiles>
<activeProfile>split-local-repo</activeProfile>
</activeProfiles>
</settings>

0 comments on commit 9e56392

Please sign in to comment.