View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.internal.aether;
20  
21  import javax.inject.Inject;
22  import javax.inject.Named;
23  import javax.inject.Singleton;
24  
25  import java.nio.file.Path;
26  import java.nio.file.Paths;
27  import java.util.ArrayList;
28  import java.util.Arrays;
29  import java.util.HashMap;
30  import java.util.LinkedHashMap;
31  import java.util.List;
32  import java.util.Map;
33  import java.util.Set;
34  import java.util.stream.Collectors;
35  
36  import org.apache.maven.RepositoryUtils;
37  import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
38  import org.apache.maven.artifact.repository.ArtifactRepository;
39  import org.apache.maven.bridge.MavenRepositorySystem;
40  import org.apache.maven.eventspy.internal.EventSpyDispatcher;
41  import org.apache.maven.execution.MavenExecutionRequest;
42  import org.apache.maven.internal.RepositorySystemSessionFactory;
43  import org.apache.maven.model.ModelBase;
44  import org.apache.maven.repository.supplier.SessionBuilderSupplier;
45  import org.apache.maven.rtinfo.RuntimeInformation;
46  import org.apache.maven.settings.Mirror;
47  import org.apache.maven.settings.Proxy;
48  import org.apache.maven.settings.Server;
49  import org.apache.maven.settings.building.SettingsProblem;
50  import org.apache.maven.settings.crypto.DefaultSettingsDecryptionRequest;
51  import org.apache.maven.settings.crypto.SettingsDecrypter;
52  import org.apache.maven.settings.crypto.SettingsDecryptionResult;
53  import org.codehaus.plexus.configuration.PlexusConfiguration;
54  import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration;
55  import org.codehaus.plexus.logging.Logger;
56  import org.codehaus.plexus.util.xml.Xpp3Dom;
57  import org.eclipse.aether.ConfigurationProperties;
58  import org.eclipse.aether.DefaultRepositorySystemSession;
59  import org.eclipse.aether.RepositorySystem;
60  import org.eclipse.aether.RepositorySystemSession;
61  import org.eclipse.aether.collection.VersionFilterBuilder;
62  import org.eclipse.aether.repository.AuthenticationSelector;
63  import org.eclipse.aether.repository.LocalRepository;
64  import org.eclipse.aether.repository.LocalRepositoryManager;
65  import org.eclipse.aether.repository.RepositoryPolicy;
66  import org.eclipse.aether.repository.WorkspaceReader;
67  import org.eclipse.aether.resolution.ResolutionErrorPolicy;
68  import org.eclipse.aether.util.ConfigUtils;
69  import org.eclipse.aether.util.graph.manager.TransitiveDependencyManager;
70  import org.eclipse.aether.util.listener.ChainedRepositoryListener;
71  import org.eclipse.aether.util.repository.AuthenticationBuilder;
72  import org.eclipse.aether.util.repository.ChainedLocalRepositoryManager;
73  import org.eclipse.aether.util.repository.DefaultAuthenticationSelector;
74  import org.eclipse.aether.util.repository.DefaultMirrorSelector;
75  import org.eclipse.aether.util.repository.DefaultProxySelector;
76  import org.eclipse.aether.util.repository.SimpleResolutionErrorPolicy;
77  import org.eclipse.aether.util.version.GenericVersionScheme;
78  import org.eclipse.aether.version.InvalidVersionSpecificationException;
79  import org.eclipse.aether.version.VersionConstraint;
80  import org.eclipse.sisu.Nullable;
81  
82  /**
83   * @since 3.3.0
84   */
85  @Singleton
86  @Named
87  public class DefaultRepositorySystemSessionFactory implements RepositorySystemSessionFactory {
88      /**
89       * User property for chained LRM: list of "tail" local repository paths (separated by comma), to be used with
90       * {@link ChainedLocalRepositoryManager}.
91       * Default value: {@code null}, no chained LRM is used.
92       *
93       * @since 3.9.0
94       */
95      private static final String MAVEN_REPO_LOCAL_TAIL = "maven.repo.local.tail";
96  
97      /**
98       * User property for chained LRM: the new "head" local repository to use, and "push" the existing into tail.
99       * Similar to <code>maven.repo.local.tail</code>, this property may contain comma separated list of paths to be
100      * used as local repositories (combine with chained local repository), but while latter is "appending" this
101      * one is "prepending".
102      *
103      * @since 3.9.10
104      */
105     public static final String MAVEN_REPO_LOCAL_HEAD = "maven.repo.local.head";
106 
107     /**
108      * User property for chained LRM: should artifact availability be ignored in tail local repositories or not.
109      * Default: {@code true}, will ignore availability from tail local repositories.
110      *
111      * @since 3.9.0
112      */
113     private static final String MAVEN_REPO_LOCAL_TAIL_IGNORE_AVAILABILITY = "maven.repo.local.tail.ignoreAvailability";
114 
115     /**
116      * User property for reverse dependency tree. If enabled, Maven will record ".tracking" directory into local
117      * repository with "reverse dependency tree", essentially explaining WHY given artifact is present in local
118      * repository.
119      * Default: {@code false}, will not record anything.
120      *
121      * @since 3.9.0
122      */
123     private static final String MAVEN_REPO_LOCAL_RECORD_REVERSE_TREE = "maven.repo.local.recordReverseTree";
124 
125     /**
126      * User property for version filter expression used in session, applied to resolving ranges.
127      * For syntax, see {@link VersionFilterBuilder} implementation.
128      *
129      * @since 3.10.0
130      * @see VersionFilterBuilder
131      */
132     private static final String MAVEN_VERSION_FILTER = "maven.session.versionFilter";
133 
134     /**
135      * User property for selecting dependency manager behavior regarding transitive dependencies and dependency
136      * management entries in their POMs. Maven 3 targeted full backward compatibility with Maven 2. Hence, it ignored
137      * dependency management entries in transitive dependency POMs. Maven 4 enables "transitivity" by default. Hence
138      * unlike Maven 3, it obeys dependency management entries deep in the dependency graph as well.
139      * <br/>
140      * Default (behave as whole Maven 3.x line): <code>"false"</code>.
141      *
142      * @since 3.10.0
143      */
144     public static final String MAVEN_RESOLVER_DEPENDENCY_MANAGER_TRANSITIVITY =
145             "maven.resolver.dependencyManagerTransitivity";
146 
147     /**
148      * User property selecting how server credentials configured in settings are scoped to repositories:
149      * <ul>
150      *     <li>{@code origin} (default): credentials for a server id are only used with a repository whose
151      *     origin (protocol, host and port) matches an origin declared for the same id, either by a
152      *     repository or mirror declared with that id in settings or on the command line, or by the
153      *     {@code <repositoryOrigins>} of that server in settings. For server ids without any such declared
154      *     origin (for example pure deployment servers whose URL comes from the project's
155      *     {@code distributionManagement}), credentials are used as before, but a warning identifying the
156      *     target origin is emitted.</li>
157      *     <li>{@code strict}: like {@code origin}, but credentials are refused for server ids that have no
158      *     declared origin at all.</li>
159      *     <li>{@code id}: legacy behavior, credentials are matched by server id only.</li>
160      * </ul>
161      * <p>
162      * Repositories declared inside a settings {@code <profile>} only contribute an origin when the profile
163      * id is listed in {@code <activeProfiles>}; profiles activated through {@code <activation>} or
164      * {@code -P} contribute none, which is what {@code <server><repositoryOrigins>} is for.
165      *
166      * @since 3.10.0
167      */
168     public static final String MAVEN_REPOSITORY_CREDENTIAL_SCOPE = "maven.repository.credentialScope";
169 
170     private static final String MAVEN_RESOLVER_TRANSPORT_KEY = "maven.resolver.transport";
171 
172     private static final String MAVEN_RESOLVER_TRANSPORT_DEFAULT = "default";
173 
174     private static final String MAVEN_RESOLVER_TRANSPORT_WAGON = "wagon";
175 
176     private static final String MAVEN_RESOLVER_TRANSPORT_APACHE = "native";
177 
178     private static final String MAVEN_RESOLVER_TRANSPORT_AUTO = "auto";
179 
180     private static final String WAGON_TRANSPORTER_PRIORITY_KEY = "aether.priority.WagonTransporterFactory";
181 
182     private static final String APACHE_HTTP_TRANSPORTER_PRIORITY_KEY = "aether.priority.ApacheTransporterFactory";
183 
184     private static final String FILE_TRANSPORTER_PRIORITY_KEY = "aether.priority.FileTransporterFactory";
185 
186     private static final String RESOLVER_MAX_PRIORITY = String.valueOf(Float.MAX_VALUE);
187 
188     @Inject
189     private Logger logger;
190 
191     @Inject
192     private ArtifactHandlerManager artifactHandlerManager;
193 
194     @Inject
195     private RepositorySystem repoSystem;
196 
197     @Inject
198     @Nullable
199     @Named("ide")
200     private WorkspaceReader workspaceRepository;
201 
202     @Inject
203     private SettingsDecrypter settingsDecrypter;
204 
205     @Inject
206     private EventSpyDispatcher eventSpyDispatcher;
207 
208     @Inject
209     private MavenRepositorySystem mavenRepositorySystem;
210 
211     @Inject
212     private RuntimeInformation runtimeInformation;
213 
214     @Inject
215     private VersionFilterBuilder versionFilterBuilder;
216 
217     private final GenericVersionScheme versionScheme = new GenericVersionScheme();
218 
219     /**
220      * For legacy consumers; hopefully nobody.
221      */
222     @Deprecated
223     public DefaultRepositorySystemSession newRepositorySession(MavenExecutionRequest request) {
224         return new DefaultRepositorySystemSession(
225                 newRepositorySessionBuilder(request).build());
226     }
227 
228     @SuppressWarnings("checkstyle:methodlength")
229     @Override
230     public RepositorySystemSession.SessionBuilder newRepositorySessionBuilder(MavenExecutionRequest request) {
231         // config
232         Map<Object, Object> configProps = new LinkedHashMap<>();
233         configProps.put(ConfigurationProperties.USER_AGENT, getUserAgent());
234         configProps.put(ConfigurationProperties.INTERACTIVE, request.isInteractiveMode());
235         configProps.put("maven.startTime", request.getStartTime());
236         // First add properties populated from settings.xml
237         configProps.putAll(getPropertiesFromRequestedProfiles(request));
238         // Resolver's ConfigUtils solely rely on config properties, that is why we need to add both here as well.
239         configProps.putAll(request.getSystemProperties());
240         configProps.putAll(request.getUserProperties());
241 
242         RepositorySystemSession.SessionBuilder mainSessionBuilder = new SessionBuilderSupplier(repoSystem).get();
243         mainSessionBuilder.setCache(request.getRepositoryCache());
244 
245         mainSessionBuilder.setOffline(request.isOffline());
246         mainSessionBuilder.setChecksumPolicy(request.getGlobalChecksumPolicy());
247 
248         if (request.getArtifactsUpdatePolicy() == null && request.getMetadataUpdatePolicy() == null) {
249             // we go "old" way
250             if (request.isNoSnapshotUpdates()) {
251                 mainSessionBuilder.setUpdatePolicy(RepositoryPolicy.UPDATE_POLICY_NEVER);
252             } else if (request.isUpdateSnapshots()) {
253                 mainSessionBuilder.setUpdatePolicy(RepositoryPolicy.UPDATE_POLICY_ALWAYS);
254             } else {
255                 mainSessionBuilder.setUpdatePolicy(null);
256             }
257         } else {
258             // we go "new" way
259             if (request.getArtifactsUpdatePolicy() != null) {
260                 mainSessionBuilder.setArtifactUpdatePolicy(request.getArtifactsUpdatePolicy());
261             }
262             if (request.getMetadataUpdatePolicy() != null) {
263                 mainSessionBuilder.setMetadataUpdatePolicy(request.getMetadataUpdatePolicy());
264             }
265         }
266 
267         int errorPolicy = 0;
268         errorPolicy |= request.isCacheNotFound()
269                 ? ResolutionErrorPolicy.CACHE_NOT_FOUND
270                 : ResolutionErrorPolicy.CACHE_DISABLED;
271         errorPolicy |= request.isCacheTransferError()
272                 ? ResolutionErrorPolicy.CACHE_TRANSFER_ERROR
273                 : ResolutionErrorPolicy.CACHE_DISABLED;
274         mainSessionBuilder.setResolutionErrorPolicy(
275                 new SimpleResolutionErrorPolicy(errorPolicy, errorPolicy | ResolutionErrorPolicy.CACHE_NOT_FOUND));
276 
277         mainSessionBuilder.setArtifactTypeRegistry(RepositoryUtils.newArtifactTypeRegistry(artifactHandlerManager));
278 
279         if (request.getWorkspaceReader() != null) {
280             mainSessionBuilder.setWorkspaceReader(request.getWorkspaceReader());
281         } else {
282             mainSessionBuilder.setWorkspaceReader(workspaceRepository);
283         }
284 
285         DefaultSettingsDecryptionRequest decrypt = new DefaultSettingsDecryptionRequest();
286         decrypt.setProxies(request.getProxies());
287         decrypt.setServers(request.getServers());
288         SettingsDecryptionResult decrypted = settingsDecrypter.decrypt(decrypt);
289 
290         if (logger.isDebugEnabled()) {
291             for (SettingsProblem problem : decrypted.getProblems()) {
292                 logger.debug(problem.getMessage(), problem.getException());
293             }
294         }
295 
296         versionFilterBuilder
297                 .buildVersionFilter((String) configProps.get(MAVEN_VERSION_FILTER), this::parseVersionConstraint)
298                 .ifPresent(mainSessionBuilder::setVersionFilter);
299 
300         boolean dependencyManagerTransitivity =
301                 ConfigUtils.getBoolean(configProps, false, MAVEN_RESOLVER_DEPENDENCY_MANAGER_TRANSITIVITY);
302         if (dependencyManagerTransitivity) {
303             mainSessionBuilder.setDependencyManager(new TransitiveDependencyManager());
304         }
305 
306         // origins of the repositories and mirrors the operator declared for a given server id, completed
307         // below with the origins declared on the servers themselves, used to scope that id's credentials
308         // to the origin(s) it was actually configured for
309         Map<String, Set<String>> declaredRepositoryOrigins = new HashMap<>();
310 
311         DefaultMirrorSelector mirrorSelector = new DefaultMirrorSelector();
312         for (Mirror mirror : request.getMirrors()) {
313             mirrorSelector.add(
314                     mirror.getId(),
315                     mirror.getUrl(),
316                     mirror.getLayout(),
317                     false,
318                     mirror.isBlocked(),
319                     mirror.getMirrorOf(),
320                     mirror.getMirrorOfLayouts());
321             OriginBoundAuthenticationSelector.addOrigin(declaredRepositoryOrigins, mirror.getId(), mirror.getUrl());
322         }
323         mainSessionBuilder.setMirrorSelector(mirrorSelector);
324         for (ArtifactRepository repository : request.getRemoteRepositories()) {
325             OriginBoundAuthenticationSelector.addOrigin(
326                     declaredRepositoryOrigins, repository.getId(), repository.getUrl());
327         }
328         for (ArtifactRepository repository : request.getPluginArtifactRepositories()) {
329             OriginBoundAuthenticationSelector.addOrigin(
330                     declaredRepositoryOrigins, repository.getId(), repository.getUrl());
331         }
332 
333         DefaultProxySelector proxySelector = new DefaultProxySelector();
334         for (Proxy proxy : decrypted.getProxies()) {
335             AuthenticationBuilder authBuilder = new AuthenticationBuilder();
336             authBuilder.addUsername(proxy.getUsername()).addPassword(proxy.getPassword());
337             proxySelector.add(
338                     new org.eclipse.aether.repository.Proxy(
339                             proxy.getProtocol(), proxy.getHost(), proxy.getPort(), authBuilder.build()),
340                     proxy.getNonProxyHosts());
341         }
342         mainSessionBuilder.setProxySelector(proxySelector);
343 
344         DefaultAuthenticationSelector authSelector = new DefaultAuthenticationSelector();
345         for (Server server : decrypted.getServers()) {
346             AuthenticationBuilder authBuilder = new AuthenticationBuilder();
347             authBuilder.addUsername(server.getUsername()).addPassword(server.getPassword());
348             authBuilder.addPrivateKey(server.getPrivateKey(), server.getPassphrase());
349             authSelector.add(server.getId(), authBuilder.build());
350 
351             // origins the operator bound to this id explicitly, added to the ones collected above from the
352             // mirrors and the repositories of the request
353             for (String repositoryOrigin : server.getRepositoryOrigins()) {
354                 OriginBoundAuthenticationSelector.addOrigin(
355                         declaredRepositoryOrigins, server.getId(), repositoryOrigin);
356             }
357 
358             if (server.getConfiguration() != null) {
359                 Xpp3Dom dom = (Xpp3Dom) server.getConfiguration();
360                 for (int i = dom.getChildCount() - 1; i >= 0; i--) {
361                     Xpp3Dom child = dom.getChild(i);
362                     if ("wagonProvider".equals(child.getName())) {
363                         dom.removeChild(i);
364                     }
365                 }
366 
367                 XmlPlexusConfiguration config = new XmlPlexusConfiguration(dom);
368                 configProps.put("aether.transport.wagon.config." + server.getId(), config);
369 
370                 // Translate to proper resolver configuration properties as well (as Plexus XML above is Wagon specific
371                 // only), but support only configuration/httpConfiguration/all, see
372                 // https://maven.apache.org/guides/mini/guide-http-settings.html
373                 Map<String, String> headers = null;
374                 Integer connectTimeout = null;
375                 Integer requestTimeout = null;
376 
377                 PlexusConfiguration httpHeaders = config.getChild("httpHeaders", false);
378                 if (httpHeaders != null) {
379                     PlexusConfiguration[] properties = httpHeaders.getChildren("property");
380                     if (properties != null && properties.length > 0) {
381                         headers = new HashMap<>();
382                         for (PlexusConfiguration property : properties) {
383                             headers.put(
384                                     property.getChild("name").getValue(),
385                                     property.getChild("value").getValue());
386                         }
387                     }
388                 }
389 
390                 PlexusConfiguration connectTimeoutXml = config.getChild("connectTimeout", false);
391                 if (connectTimeoutXml != null) {
392                     connectTimeout = Integer.parseInt(connectTimeoutXml.getValue());
393                 } else {
394                     // fallback configuration name
395                     PlexusConfiguration httpConfiguration = config.getChild("httpConfiguration", false);
396                     if (httpConfiguration != null) {
397                         PlexusConfiguration httpConfigurationAll = httpConfiguration.getChild("all", false);
398                         if (httpConfigurationAll != null) {
399                             connectTimeoutXml = httpConfigurationAll.getChild("connectionTimeout", false);
400                             if (connectTimeoutXml != null) {
401                                 connectTimeout = Integer.parseInt(connectTimeoutXml.getValue());
402                                 logger.warn("Settings for server " + server.getId() + " uses legacy format");
403                             }
404                         }
405                     }
406                 }
407 
408                 PlexusConfiguration requestTimeoutXml = config.getChild("requestTimeout", false);
409                 if (requestTimeoutXml != null) {
410                     requestTimeout = Integer.parseInt(requestTimeoutXml.getValue());
411                 } else {
412                     // fallback configuration name
413                     PlexusConfiguration httpConfiguration = config.getChild("httpConfiguration", false);
414                     if (httpConfiguration != null) {
415                         PlexusConfiguration httpConfigurationAll = httpConfiguration.getChild("all", false);
416                         if (httpConfigurationAll != null) {
417                             requestTimeoutXml = httpConfigurationAll.getChild("readTimeout", false);
418                             if (requestTimeoutXml != null) {
419                                 requestTimeout = Integer.parseInt(requestTimeoutXml.getValue());
420                                 logger.warn("Settings for server " + server.getId() + " uses legacy format");
421                             }
422                         }
423                     }
424                 }
425 
426                 // org.eclipse.aether.ConfigurationProperties.HTTP_HEADERS => Map<String, String>
427                 if (headers != null) {
428                     configProps.put(ConfigurationProperties.HTTP_HEADERS + "." + server.getId(), headers);
429                 }
430                 // org.eclipse.aether.ConfigurationProperties.CONNECT_TIMEOUT => int
431                 if (connectTimeout != null) {
432                     configProps.put(ConfigurationProperties.CONNECT_TIMEOUT + "." + server.getId(), connectTimeout);
433                 }
434                 // org.eclipse.aether.ConfigurationProperties.REQUEST_TIMEOUT => int
435                 if (requestTimeout != null) {
436                     configProps.put(ConfigurationProperties.REQUEST_TIMEOUT + "." + server.getId(), requestTimeout);
437                 }
438             }
439 
440             configProps.put("aether.transport.wagon.perms.fileMode." + server.getId(), server.getFilePermissions());
441             configProps.put("aether.transport.wagon.perms.dirMode." + server.getId(), server.getDirectoryPermissions());
442         }
443         String credentialScope = ConfigUtils.getString(
444                 configProps, OriginBoundAuthenticationSelector.SCOPE_ORIGIN, MAVEN_REPOSITORY_CREDENTIAL_SCOPE);
445         AuthenticationSelector effectiveAuthSelector = OriginBoundAuthenticationSelector.wrap(
446                 authSelector, credentialScope, declaredRepositoryOrigins, logger);
447         mainSessionBuilder.setAuthenticationSelector(effectiveAuthSelector);
448 
449         Object transport = configProps.getOrDefault(MAVEN_RESOLVER_TRANSPORT_KEY, MAVEN_RESOLVER_TRANSPORT_DEFAULT);
450         if (MAVEN_RESOLVER_TRANSPORT_DEFAULT.equals(transport)) {
451             // The "default" mode (user did not set anything) from now on defaults to AUTO
452         } else if (MAVEN_RESOLVER_TRANSPORT_APACHE.equals(transport)) {
453             // Make sure (whatever extra priority is set) that resolver native is selected
454             configProps.put(FILE_TRANSPORTER_PRIORITY_KEY, RESOLVER_MAX_PRIORITY);
455             configProps.put(APACHE_HTTP_TRANSPORTER_PRIORITY_KEY, RESOLVER_MAX_PRIORITY);
456         } else if (MAVEN_RESOLVER_TRANSPORT_WAGON.equals(transport)) {
457             // Make sure (whatever extra priority is set) that wagon is selected
458             configProps.put(WAGON_TRANSPORTER_PRIORITY_KEY, RESOLVER_MAX_PRIORITY);
459         } else if (!MAVEN_RESOLVER_TRANSPORT_AUTO.equals(transport)) {
460             throw new IllegalArgumentException("Unknown resolver transport '" + transport
461                     + "'. Supported transports are: " + MAVEN_RESOLVER_TRANSPORT_WAGON + ", "
462                     + MAVEN_RESOLVER_TRANSPORT_APACHE + ", " + MAVEN_RESOLVER_TRANSPORT_AUTO);
463         }
464 
465         mainSessionBuilder.setUserProperties(request.getUserProperties());
466         mainSessionBuilder.setSystemProperties(request.getSystemProperties());
467         mainSessionBuilder.setConfigProperties(configProps);
468 
469         mainSessionBuilder.setTransferListener(request.getTransferListener());
470 
471         mainSessionBuilder.setRepositoryListener(
472                 eventSpyDispatcher.chainListener(new LoggingRepositoryListener(logger)));
473 
474         mainSessionBuilder.setIgnoreArtifactDescriptorRepositories(request.isIgnoreTransitiveRepositories());
475 
476         try (RepositorySystemSession.CloseableSession protoSession =
477                 setUpLocalRepositoryManager(request, repoSystem, mainSessionBuilder)) {
478             boolean recordReverseTree =
479                     ConfigUtils.getBoolean(protoSession, false, MAVEN_REPO_LOCAL_RECORD_REVERSE_TREE);
480             if (recordReverseTree) {
481                 mainSessionBuilder.setRepositoryListener(new ChainedRepositoryListener(
482                         protoSession.getRepositoryListener(), new ReverseTreeRepositoryListener()));
483             }
484 
485             mavenRepositorySystem.injectMirror(request.getRemoteRepositories(), request.getMirrors());
486             mavenRepositorySystem.injectProxy(protoSession, request.getRemoteRepositories());
487             mavenRepositorySystem.injectAuthentication(protoSession, request.getRemoteRepositories());
488 
489             mavenRepositorySystem.injectMirror(request.getPluginArtifactRepositories(), request.getMirrors());
490             mavenRepositorySystem.injectProxy(protoSession, request.getPluginArtifactRepositories());
491             mavenRepositorySystem.injectAuthentication(protoSession, request.getPluginArtifactRepositories());
492 
493             return mainSessionBuilder;
494         }
495     }
496 
497     private static RepositorySystemSession.CloseableSession setUpLocalRepositoryManager(
498             MavenExecutionRequest request,
499             RepositorySystem repoSystem,
500             RepositorySystemSession.SessionBuilder sessionBuilder) {
501         Path requestLocalRepositoryPath = resolve(request.getLocalRepository().getBasedir());
502         RepositorySystemSession.CloseableSession protoSession = sessionBuilder
503                 .withLocalRepositoryBaseDirectories(requestLocalRepositoryPath)
504                 .build();
505         LocalRepositoryManager lrm =
506                 setUpLocalRepositoryManager(request.getLocalRepository().getBasedir(), repoSystem, protoSession);
507         sessionBuilder.setLocalRepositoryManager(lrm);
508         return protoSession;
509     }
510 
511     public static LocalRepositoryManager setUpLocalRepositoryManager(
512             String localRepository, RepositorySystem repoSystem, RepositorySystemSession session) {
513         List<Path> paths = new ArrayList<>();
514         String localRepoHead = ConfigUtils.getString(session, null, MAVEN_REPO_LOCAL_HEAD);
515         if (localRepoHead != null) {
516             Arrays.stream(localRepoHead.split(","))
517                     .filter(p -> !p.trim().isEmpty())
518                     .map(DefaultRepositorySystemSessionFactory::resolve)
519                     .forEach(paths::add);
520         }
521 
522         paths.add(resolve(localRepository));
523 
524         String localRepoTail = ConfigUtils.getString(session, null, MAVEN_REPO_LOCAL_TAIL);
525         if (localRepoTail != null) {
526             Arrays.stream(localRepoTail.split(","))
527                     .filter(p -> !p.trim().isEmpty())
528                     .map(DefaultRepositorySystemSessionFactory::resolve)
529                     .forEach(paths::add);
530         }
531 
532         LocalRepository localRepo = new LocalRepository(paths.remove(0));
533         LocalRepositoryManager lrm = repoSystem.newLocalRepositoryManager(session, localRepo);
534 
535         if (!paths.isEmpty()) {
536             List<LocalRepositoryManager> tail = new ArrayList<>();
537             for (Path path : paths) {
538                 tail.add(repoSystem.newLocalRepositoryManager(session, new LocalRepository(path)));
539             }
540             boolean ignoreTailAvailability =
541                     ConfigUtils.getBoolean(session, true, MAVEN_REPO_LOCAL_TAIL_IGNORE_AVAILABILITY);
542 
543             lrm = new ChainedLocalRepositoryManager(lrm, tail, ignoreTailAvailability);
544         }
545         return lrm;
546     }
547 
548     public static Path resolve(String string) {
549         if (string.startsWith("~/") || string.startsWith("~\\")) {
550             // resolve based on $HOME
551             return Paths.get(System.getProperty("user.home"))
552                     .resolve(string.substring(2))
553                     .normalize()
554                     .toAbsolutePath();
555         } else {
556             // resolve based on $CWD
557             return Paths.get(string).normalize().toAbsolutePath();
558         }
559     }
560 
561     private VersionConstraint parseVersionConstraint(String spec) {
562         try {
563             return versionScheme.parseVersionConstraint(spec);
564         } catch (InvalidVersionSpecificationException e) {
565             throw new RuntimeException(e);
566         }
567     }
568 
569     private Map<?, ?> getPropertiesFromRequestedProfiles(MavenExecutionRequest request) {
570 
571         List<String> activeProfileId = request.getActiveProfiles();
572         List<String> inactiveProfileId = request.getInactiveProfiles();
573 
574         return request.getProfiles().stream()
575                 .filter(profile -> activeProfileId.contains(profile.getId())
576                         || (!inactiveProfileId.contains(profile.getId())
577                                 && profile.getActivation() != null
578                                 && profile.getActivation().isActiveByDefault()))
579                 .map(ModelBase::getProperties)
580                 .flatMap(properties -> properties.entrySet().stream())
581                 .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (k1, k2) -> k2));
582     }
583 
584     private String getUserAgent() {
585         String version = runtimeInformation.getMavenVersion();
586         version = version.isEmpty() ? version : "/" + version;
587         return "Apache-Maven" + version + " (Java " + System.getProperty("java.version") + "; "
588                 + System.getProperty("os.name") + " " + System.getProperty("os.version") + ")";
589     }
590 }