From d0324830e5fc0248ab4139b8972331a17efdd94b Mon Sep 17 00:00:00 2001 From: Ladislav Thon Date: Wed, 22 Jan 2025 15:03:06 +0100 Subject: [PATCH] Maven Resolver: initialize Maven properly, especially the local repository 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`. --- ...er.java => AbstractDependencyBuilder.java} | 0 .../resolver/maven/BootstrapMavenContext.java | 160 +++++++++--------- .../maven/BootstrapMavenContextConfig.java | 2 +- ...ava => DefaultEffectiveModelResolver.java} | 0 .../maven/test/SplitLocalRepositoryTest.java | 31 ++++ .../split-local-repository/settings.xml | 18 ++ 6 files changed, 126 insertions(+), 85 deletions(-) rename independent-projects/bootstrap/app-model/src/main/java/io/quarkus/maven/dependency/{ArtifactDependencyBuilder.java => AbstractDependencyBuilder.java} (100%) rename independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/{DeffaultEffectiveModelResolver.java => DefaultEffectiveModelResolver.java} (100%) create mode 100644 independent-projects/bootstrap/maven-resolver/src/test/java/io/quarkus/bootstrap/resolver/maven/test/SplitLocalRepositoryTest.java create mode 100644 independent-projects/bootstrap/maven-resolver/src/test/resources/custom-settings/split-local-repository/settings.xml diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/maven/dependency/ArtifactDependencyBuilder.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/maven/dependency/AbstractDependencyBuilder.java similarity index 100% rename from independent-projects/bootstrap/app-model/src/main/java/io/quarkus/maven/dependency/ArtifactDependencyBuilder.java rename to independent-projects/bootstrap/app-model/src/main/java/io/quarkus/maven/dependency/AbstractDependencyBuilder.java diff --git a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BootstrapMavenContext.java b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BootstrapMavenContext.java index 7dc987859495a..711a029b2bb25 100644 --- a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BootstrapMavenContext.java +++ b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BootstrapMavenContext.java @@ -16,11 +16,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; @@ -45,7 +47,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; @@ -59,10 +60,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; @@ -488,33 +487,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 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 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 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()); @@ -533,40 +526,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 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()); @@ -581,6 +580,7 @@ private DefaultRepositorySystemSession newRepositorySystemSession() throws Boots dom.removeChild(i); } } + XmlPlexusConfiguration config = new XmlPlexusConfiguration(dom); configProps.put("aether.connector.wagon.config." + server.getId(), config); @@ -653,6 +653,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()); } @@ -674,17 +675,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(); @@ -697,13 +692,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 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 resolveRemoteRepos() throws BootstrapMavenException { final List rawRepos = new ArrayList<>(); readMavenReposFromEnv(rawRepos, System.getenv()); @@ -905,26 +917,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) { diff --git a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BootstrapMavenContextConfig.java b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BootstrapMavenContextConfig.java index 47a80712c78b3..8d8aca42cf958 100644 --- a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BootstrapMavenContextConfig.java +++ b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BootstrapMavenContextConfig.java @@ -19,7 +19,7 @@ import io.quarkus.bootstrap.resolver.maven.options.BootstrapMavenOptions; import io.quarkus.bootstrap.resolver.maven.workspace.LocalProject; -public class BootstrapMavenContextConfig> { +public class BootstrapMavenContextConfig> { /** * Resolves the effective value of the {@code effective-model-builder} option by looking for the diff --git a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/DeffaultEffectiveModelResolver.java b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/DefaultEffectiveModelResolver.java similarity index 100% rename from independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/DeffaultEffectiveModelResolver.java rename to independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/DefaultEffectiveModelResolver.java diff --git a/independent-projects/bootstrap/maven-resolver/src/test/java/io/quarkus/bootstrap/resolver/maven/test/SplitLocalRepositoryTest.java b/independent-projects/bootstrap/maven-resolver/src/test/java/io/quarkus/bootstrap/resolver/maven/test/SplitLocalRepositoryTest.java new file mode 100644 index 0000000000000..70a5db0f8c8d7 --- /dev/null +++ b/independent-projects/bootstrap/maven-resolver/src/test/java/io/quarkus/bootstrap/resolver/maven/test/SplitLocalRepositoryTest.java @@ -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)); + } +} diff --git a/independent-projects/bootstrap/maven-resolver/src/test/resources/custom-settings/split-local-repository/settings.xml b/independent-projects/bootstrap/maven-resolver/src/test/resources/custom-settings/split-local-repository/settings.xml new file mode 100644 index 0000000000000..fc8c04e7a016e --- /dev/null +++ b/independent-projects/bootstrap/maven-resolver/src/test/resources/custom-settings/split-local-repository/settings.xml @@ -0,0 +1,18 @@ + + + + + split-local-repo + + true + true + true + + + + + split-local-repo + +