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