1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.internal.aether;
20
21 import java.nio.file.Path;
22 import java.nio.file.Paths;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.HashMap;
26 import java.util.HashSet;
27 import java.util.LinkedHashMap;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.function.Predicate;
31 import java.util.stream.Collectors;
32
33 import org.apache.maven.api.Constants;
34 import org.apache.maven.api.di.Inject;
35 import org.apache.maven.api.di.Named;
36 import org.apache.maven.api.di.Singleton;
37 import org.apache.maven.api.feature.Features;
38 import org.apache.maven.api.services.TypeRegistry;
39 import org.apache.maven.api.xml.XmlNode;
40 import org.apache.maven.eventspy.internal.EventSpyDispatcher;
41 import org.apache.maven.execution.MavenExecutionRequest;
42 import org.apache.maven.impl.resolver.MavenSessionBuilderSupplier;
43 import org.apache.maven.internal.xml.XmlPlexusConfiguration;
44 import org.apache.maven.model.ModelBase;
45 import org.apache.maven.resolver.RepositorySystemSessionFactory;
46 import org.apache.maven.rtinfo.RuntimeInformation;
47 import org.apache.maven.settings.Mirror;
48 import org.apache.maven.settings.Proxy;
49 import org.apache.maven.settings.Server;
50 import org.codehaus.plexus.configuration.PlexusConfiguration;
51 import org.eclipse.aether.ConfigurationProperties;
52 import org.eclipse.aether.RepositoryListener;
53 import org.eclipse.aether.RepositorySystem;
54 import org.eclipse.aether.RepositorySystemSession;
55 import org.eclipse.aether.RepositorySystemSession.SessionBuilder;
56 import org.eclipse.aether.artifact.Artifact;
57 import org.eclipse.aether.artifact.DefaultArtifact;
58 import org.eclipse.aether.collection.VersionFilter;
59 import org.eclipse.aether.repository.RepositoryPolicy;
60 import org.eclipse.aether.resolution.ResolutionErrorPolicy;
61 import org.eclipse.aether.util.graph.version.ChainedVersionFilter;
62 import org.eclipse.aether.util.graph.version.ContextualSnapshotVersionFilter;
63 import org.eclipse.aether.util.graph.version.HighestVersionFilter;
64 import org.eclipse.aether.util.graph.version.LowestVersionFilter;
65 import org.eclipse.aether.util.graph.version.PredicateVersionFilter;
66 import org.eclipse.aether.util.listener.ChainedRepositoryListener;
67 import org.eclipse.aether.util.repository.AuthenticationBuilder;
68 import org.eclipse.aether.util.repository.ChainedLocalRepositoryManager;
69 import org.eclipse.aether.util.repository.DefaultAuthenticationSelector;
70 import org.eclipse.aether.util.repository.DefaultMirrorSelector;
71 import org.eclipse.aether.util.repository.DefaultProxySelector;
72 import org.eclipse.aether.util.repository.SimpleArtifactDescriptorPolicy;
73 import org.eclipse.aether.util.repository.SimpleResolutionErrorPolicy;
74 import org.eclipse.aether.version.InvalidVersionSpecificationException;
75 import org.eclipse.aether.version.Version;
76 import org.eclipse.aether.version.VersionRange;
77 import org.eclipse.aether.version.VersionScheme;
78 import org.slf4j.Logger;
79 import org.slf4j.LoggerFactory;
80
81 import static java.util.Objects.requireNonNull;
82
83
84
85
86 @Named
87 @Singleton
88 public class DefaultRepositorySystemSessionFactory implements RepositorySystemSessionFactory {
89
90 public static final String MAVEN_RESOLVER_TRANSPORT_DEFAULT = "default";
91
92 public static final String MAVEN_RESOLVER_TRANSPORT_WAGON = "wagon";
93
94 public static final String MAVEN_RESOLVER_TRANSPORT_APACHE = "apache";
95
96 public static final String MAVEN_RESOLVER_TRANSPORT_JDK = "jdk";
97
98
99
100
101
102
103 @Deprecated
104 private static final String MAVEN_RESOLVER_TRANSPORT_NATIVE = "native";
105
106 public static final String MAVEN_RESOLVER_TRANSPORT_AUTO = "auto";
107
108 private static final String WAGON_TRANSPORTER_PRIORITY_KEY = "aether.priority.WagonTransporterFactory";
109
110 private static final String APACHE_HTTP_TRANSPORTER_PRIORITY_KEY = "aether.priority.ApacheTransporterFactory";
111
112 private static final String JDK_HTTP_TRANSPORTER_PRIORITY_KEY = "aether.priority.JdkTransporterFactory";
113
114 private static final String FILE_TRANSPORTER_PRIORITY_KEY = "aether.priority.FileTransporterFactory";
115
116 private static final String RESOLVER_MAX_PRIORITY = String.valueOf(Float.MAX_VALUE);
117
118 private final Logger logger = LoggerFactory.getLogger(getClass());
119
120 private final RepositorySystem repoSystem;
121
122 private final EventSpyDispatcher eventSpyDispatcher;
123
124 private final RuntimeInformation runtimeInformation;
125
126 private final TypeRegistry typeRegistry;
127
128 private final VersionScheme versionScheme;
129
130 private final Map<String, RepositorySystemSessionExtender> sessionExtenders;
131
132 @SuppressWarnings("checkstyle:ParameterNumber")
133 @Inject
134 DefaultRepositorySystemSessionFactory(
135 RepositorySystem repoSystem,
136 EventSpyDispatcher eventSpyDispatcher,
137 RuntimeInformation runtimeInformation,
138 TypeRegistry typeRegistry,
139 VersionScheme versionScheme,
140 Map<String, RepositorySystemSessionExtender> sessionExtenders) {
141 this.repoSystem = repoSystem;
142 this.eventSpyDispatcher = eventSpyDispatcher;
143 this.runtimeInformation = runtimeInformation;
144 this.typeRegistry = typeRegistry;
145 this.versionScheme = versionScheme;
146 this.sessionExtenders = sessionExtenders;
147 }
148
149 @Deprecated
150 public RepositorySystemSession newRepositorySession(MavenExecutionRequest request) {
151 return newRepositorySessionBuilder(request).build();
152 }
153
154 @Override
155 @SuppressWarnings({"checkstyle:methodLength"})
156 public SessionBuilder newRepositorySessionBuilder(MavenExecutionRequest request) {
157 requireNonNull(request, "request");
158
159
160 Map<String, String> mergedProps = createMergedProperties(request);
161
162 boolean mavenMaven3Personality = Features.mavenMaven3Personality(mergedProps);
163 MavenSessionBuilderSupplier supplier = new MavenSessionBuilderSupplier(repoSystem, mavenMaven3Personality);
164 SessionBuilder sessionBuilder = supplier.get();
165 sessionBuilder.setArtifactTypeRegistry(new TypeRegistryAdapter(typeRegistry));
166 sessionBuilder.setCache(request.getRepositoryCache());
167
168
169 Map<String, Object> configProps = new LinkedHashMap<>();
170 configProps.put(ConfigurationProperties.USER_AGENT, getUserAgent());
171 configProps.put(ConfigurationProperties.INTERACTIVE, request.isInteractiveMode());
172 configProps.put("maven.startTime", request.getStartTime());
173 configProps.put(Constants.MAVEN_START_INSTANT, request.getStartInstant());
174
175 sessionBuilder.setOffline(request.isOffline());
176 sessionBuilder.setChecksumPolicy(request.getGlobalChecksumPolicy());
177 sessionBuilder.setUpdatePolicy(
178 request.isNoSnapshotUpdates()
179 ? RepositoryPolicy.UPDATE_POLICY_NEVER
180 : request.isUpdateSnapshots() ? RepositoryPolicy.UPDATE_POLICY_ALWAYS : null);
181
182 int errorPolicy = 0;
183 errorPolicy |= request.isCacheNotFound()
184 ? ResolutionErrorPolicy.CACHE_NOT_FOUND
185 : ResolutionErrorPolicy.CACHE_DISABLED;
186 errorPolicy |= request.isCacheTransferError()
187 ? ResolutionErrorPolicy.CACHE_TRANSFER_ERROR
188 : ResolutionErrorPolicy.CACHE_DISABLED;
189 sessionBuilder.setResolutionErrorPolicy(
190 new SimpleResolutionErrorPolicy(errorPolicy, errorPolicy | ResolutionErrorPolicy.CACHE_NOT_FOUND));
191
192 sessionBuilder.setArtifactDescriptorPolicy(new SimpleArtifactDescriptorPolicy(
193 request.isIgnoreMissingArtifactDescriptor(), request.isIgnoreInvalidArtifactDescriptor()));
194
195 VersionFilter versionFilter = buildVersionFilter(mergedProps.get(Constants.MAVEN_VERSION_FILTER));
196 if (versionFilter != null) {
197 sessionBuilder.setVersionFilter(versionFilter);
198 }
199
200 DefaultMirrorSelector mirrorSelector = new DefaultMirrorSelector();
201 for (Mirror mirror : request.getMirrors()) {
202 mirrorSelector.add(
203 mirror.getId(),
204 mirror.getUrl(),
205 mirror.getLayout(),
206 false,
207 mirror.isBlocked(),
208 mirror.getMirrorOf(),
209 mirror.getMirrorOfLayouts());
210 }
211 sessionBuilder.setMirrorSelector(mirrorSelector);
212
213 DefaultProxySelector proxySelector = new DefaultProxySelector();
214 for (Proxy proxy : request.getProxies()) {
215 AuthenticationBuilder authBuilder = new AuthenticationBuilder();
216 authBuilder.addUsername(proxy.getUsername()).addPassword(proxy.getPassword());
217 proxySelector.add(
218 new org.eclipse.aether.repository.Proxy(
219 proxy.getProtocol(), proxy.getHost(), proxy.getPort(), authBuilder.build()),
220 proxy.getNonProxyHosts());
221 }
222 sessionBuilder.setProxySelector(proxySelector);
223
224
225
226 DefaultAuthenticationSelector authSelector = new DefaultAuthenticationSelector();
227 for (Server server : request.getServers()) {
228 AuthenticationBuilder authBuilder = new AuthenticationBuilder();
229 authBuilder.addUsername(server.getUsername()).addPassword(server.getPassword());
230 authBuilder.addPrivateKey(server.getPrivateKey(), server.getPassphrase());
231 authSelector.add(server.getId(), authBuilder.build());
232
233 if (server.getConfiguration() != null) {
234 XmlNode dom = server.getDelegate().getConfiguration();
235 List<XmlNode> children = dom.children().stream()
236 .filter(c -> !"wagonProvider".equals(c.name()))
237 .collect(Collectors.toList());
238 dom = XmlNode.newInstance(dom.name(), children);
239 PlexusConfiguration config = XmlPlexusConfiguration.toPlexusConfiguration(dom);
240 configProps.put("aether.transport.wagon.config." + server.getId(), config);
241
242
243
244
245 Map<String, String> headers = null;
246 Integer connectTimeout = null;
247 Integer requestTimeout = null;
248
249 PlexusConfiguration httpHeaders = config.getChild("httpHeaders", false);
250 if (httpHeaders != null) {
251 PlexusConfiguration[] properties = httpHeaders.getChildren("property");
252 if (properties != null && properties.length > 0) {
253 headers = new HashMap<>();
254 for (PlexusConfiguration property : properties) {
255 headers.put(
256 property.getChild("name").getValue(),
257 property.getChild("value").getValue());
258 }
259 }
260 }
261
262 PlexusConfiguration connectTimeoutXml = config.getChild("connectTimeout", false);
263 if (connectTimeoutXml != null) {
264 connectTimeout = Integer.parseInt(connectTimeoutXml.getValue());
265 } else {
266
267 PlexusConfiguration httpConfiguration = config.getChild("httpConfiguration", false);
268 if (httpConfiguration != null) {
269 PlexusConfiguration httpConfigurationAll = httpConfiguration.getChild("all", false);
270 if (httpConfigurationAll != null) {
271 connectTimeoutXml = httpConfigurationAll.getChild("connectionTimeout", false);
272 if (connectTimeoutXml != null) {
273 connectTimeout = Integer.parseInt(connectTimeoutXml.getValue());
274 logger.warn("Settings for server {} uses legacy format", server.getId());
275 }
276 }
277 }
278 }
279
280 PlexusConfiguration requestTimeoutXml = config.getChild("requestTimeout", false);
281 if (requestTimeoutXml != null) {
282 requestTimeout = Integer.parseInt(requestTimeoutXml.getValue());
283 } else {
284
285 PlexusConfiguration httpConfiguration = config.getChild("httpConfiguration", false);
286 if (httpConfiguration != null) {
287 PlexusConfiguration httpConfigurationAll = httpConfiguration.getChild("all", false);
288 if (httpConfigurationAll != null) {
289 requestTimeoutXml = httpConfigurationAll.getChild("readTimeout", false);
290 if (requestTimeoutXml != null) {
291 requestTimeout = Integer.parseInt(requestTimeoutXml.getValue());
292 logger.warn("Settings for server {} uses legacy format", server.getId());
293 }
294 }
295 }
296 }
297
298
299 if (headers != null) {
300 configProps.put(ConfigurationProperties.HTTP_HEADERS + "." + server.getId(), headers);
301 }
302
303 if (connectTimeout != null) {
304 configProps.put(ConfigurationProperties.CONNECT_TIMEOUT + "." + server.getId(), connectTimeout);
305 }
306
307 if (requestTimeout != null) {
308 configProps.put(ConfigurationProperties.REQUEST_TIMEOUT + "." + server.getId(), requestTimeout);
309 }
310 }
311
312 configProps.put("aether.transport.wagon.perms.fileMode." + server.getId(), server.getFilePermissions());
313 configProps.put("aether.transport.wagon.perms.dirMode." + server.getId(), server.getDirectoryPermissions());
314 }
315 sessionBuilder.setAuthenticationSelector(authSelector);
316
317 Object transport =
318 mergedProps.getOrDefault(Constants.MAVEN_RESOLVER_TRANSPORT, MAVEN_RESOLVER_TRANSPORT_DEFAULT);
319 if (MAVEN_RESOLVER_TRANSPORT_DEFAULT.equals(transport)) {
320
321 } else if (MAVEN_RESOLVER_TRANSPORT_JDK.equals(transport)) {
322
323 configProps.put(FILE_TRANSPORTER_PRIORITY_KEY, RESOLVER_MAX_PRIORITY);
324 configProps.put(JDK_HTTP_TRANSPORTER_PRIORITY_KEY, RESOLVER_MAX_PRIORITY);
325 } else if (MAVEN_RESOLVER_TRANSPORT_APACHE.equals(transport)
326 || MAVEN_RESOLVER_TRANSPORT_NATIVE.equals(transport)) {
327 if (MAVEN_RESOLVER_TRANSPORT_NATIVE.equals(transport)) {
328 logger.warn(
329 "Transport name '{}' is DEPRECATED/RENAMED, use '{}' instead",
330 MAVEN_RESOLVER_TRANSPORT_NATIVE,
331 MAVEN_RESOLVER_TRANSPORT_APACHE);
332 }
333
334 configProps.put(FILE_TRANSPORTER_PRIORITY_KEY, RESOLVER_MAX_PRIORITY);
335 configProps.put(APACHE_HTTP_TRANSPORTER_PRIORITY_KEY, RESOLVER_MAX_PRIORITY);
336 } else if (MAVEN_RESOLVER_TRANSPORT_WAGON.equals(transport)) {
337
338 configProps.put(WAGON_TRANSPORTER_PRIORITY_KEY, RESOLVER_MAX_PRIORITY);
339 } else if (!MAVEN_RESOLVER_TRANSPORT_AUTO.equals(transport)) {
340 throw new IllegalArgumentException("Unknown resolver transport '" + transport
341 + "'. Supported transports are: " + MAVEN_RESOLVER_TRANSPORT_WAGON + ", "
342 + MAVEN_RESOLVER_TRANSPORT_APACHE + ", " + MAVEN_RESOLVER_TRANSPORT_JDK + ", "
343 + MAVEN_RESOLVER_TRANSPORT_AUTO);
344 }
345
346 sessionBuilder.setIgnoreArtifactDescriptorRepositories(request.isIgnoreTransitiveRepositories());
347
348 sessionBuilder.setTransferListener(request.getTransferListener());
349
350 RepositoryListener repositoryListener = eventSpyDispatcher.chainListener(new LoggingRepositoryListener(logger));
351
352 boolean recordReverseTree = Boolean.parseBoolean(
353 mergedProps.getOrDefault(Constants.MAVEN_REPO_LOCAL_RECORD_REVERSE_TREE, Boolean.FALSE.toString()));
354 if (recordReverseTree) {
355 repositoryListener = new ChainedRepositoryListener(repositoryListener, new ReverseTreeRepositoryListener());
356 }
357 sessionBuilder.setRepositoryListener(repositoryListener);
358
359
360 String resolverDependencyManagerTransitivity = mergedProps.getOrDefault(
361 Constants.MAVEN_RESOLVER_DEPENDENCY_MANAGER_TRANSITIVITY, Boolean.toString(!mavenMaven3Personality));
362 sessionBuilder.setDependencyManager(
363 supplier.getDependencyManager(Boolean.parseBoolean(resolverDependencyManagerTransitivity)));
364
365 ArrayList<Path> paths = new ArrayList<>();
366 String localRepoHead = mergedProps.get(Constants.MAVEN_REPO_LOCAL_HEAD);
367 if (localRepoHead != null) {
368 Arrays.stream(localRepoHead.split(","))
369 .filter(p -> p != null && !p.trim().isEmpty())
370 .map(this::resolve)
371 .forEach(paths::add);
372 }
373 paths.add(Paths.get(request.getLocalRepository().getBasedir()));
374 String localRepoTail = mergedProps.get(Constants.MAVEN_REPO_LOCAL_TAIL);
375 if (localRepoTail != null) {
376 Arrays.stream(localRepoTail.split(","))
377 .filter(p -> p != null && !p.trim().isEmpty())
378 .map(this::resolve)
379 .forEach(paths::add);
380 }
381 sessionBuilder.withLocalRepositoryBaseDirectories(paths);
382
383 if (mergedProps.containsKey(Constants.MAVEN_REPO_LOCAL_TAIL_IGNORE_AVAILABILITY)) {
384 configProps.put(
385 ChainedLocalRepositoryManager.CONFIG_PROP_IGNORE_TAIL_AVAILABILITY,
386 mergedProps.get(Constants.MAVEN_REPO_LOCAL_TAIL_IGNORE_AVAILABILITY));
387 }
388
389 for (RepositorySystemSessionExtender extender : sessionExtenders.values()) {
390 extender.extend(request, configProps, mirrorSelector, proxySelector, authSelector);
391 }
392
393
394
395 HashMap<String, Object> finalConfigProperties = new HashMap<>();
396 finalConfigProperties.putAll(mergedProps);
397 finalConfigProperties.putAll(configProps);
398
399 sessionBuilder.setUserProperties(request.getUserProperties());
400 sessionBuilder.setSystemProperties(request.getSystemProperties());
401 sessionBuilder.setConfigProperties(finalConfigProperties);
402
403 return sessionBuilder;
404 }
405
406 private Path resolve(String string) {
407 if (string.startsWith("~/") || string.startsWith("~\\")) {
408
409 return Paths.get(System.getProperty("user.home"))
410 .resolve(string.substring(2))
411 .normalize()
412 .toAbsolutePath();
413 } else {
414
415 return Paths.get(string).normalize().toAbsolutePath();
416 }
417 }
418
419 private VersionFilter buildVersionFilter(String filterExpression) {
420 ArrayList<VersionFilter> filters = new ArrayList<>();
421 if (filterExpression != null) {
422 List<String> expressions = Arrays.stream(filterExpression.split(";"))
423 .filter(s -> s != null && !s.trim().isEmpty())
424 .toList();
425 for (String expression : expressions) {
426 if ("h".equals(expression)) {
427 filters.add(new HighestVersionFilter());
428 } else if (expression.startsWith("h(") && expression.endsWith(")")) {
429 int num = Integer.parseInt(expression.substring(2, expression.length() - 1));
430 filters.add(new HighestVersionFilter(num));
431 } else if ("l".equals(expression)) {
432 filters.add(new LowestVersionFilter());
433 } else if (expression.startsWith("l(") && expression.endsWith(")")) {
434 int num = Integer.parseInt(expression.substring(2, expression.length() - 1));
435 filters.add(new LowestVersionFilter(num));
436 } else if ("s".equals(expression)) {
437 filters.add(new ContextualSnapshotVersionFilter());
438 } else if (expression.startsWith("e(") && expression.endsWith(")")) {
439 Artifact artifact = new DefaultArtifact(expression.substring(2, expression.length() - 1));
440 VersionRange versionRange =
441 artifact.getVersion().contains(",") ? parseVersionRange(artifact.getVersion()) : null;
442 Predicate<Artifact> predicate = a -> {
443 if (artifact.getGroupId().equals(a.getGroupId())
444 && artifact.getArtifactId().equals(a.getArtifactId())) {
445 if (versionRange != null) {
446 Version v = parseVersion(a.getVersion());
447 return !versionRange.containsVersion(v);
448 } else {
449 return !artifact.getVersion().equals(a.getVersion());
450 }
451 }
452 return true;
453 };
454 filters.add(new PredicateVersionFilter(predicate));
455 } else {
456 throw new IllegalArgumentException("Unsupported filter expression: " + expression);
457 }
458 }
459 }
460 if (filters.isEmpty()) {
461 return null;
462 } else if (filters.size() == 1) {
463 return filters.get(0);
464 } else {
465 return ChainedVersionFilter.newInstance(filters);
466 }
467 }
468
469 private Version parseVersion(String spec) {
470 try {
471 return versionScheme.parseVersion(spec);
472 } catch (InvalidVersionSpecificationException e) {
473 throw new RuntimeException(e);
474 }
475 }
476
477 private VersionRange parseVersionRange(String spec) {
478 try {
479 return versionScheme.parseVersionRange(spec);
480 } catch (InvalidVersionSpecificationException e) {
481 throw new RuntimeException(e);
482 }
483 }
484
485 @SuppressWarnings({"unchecked", "rawtypes"})
486 private Map<String, String> createMergedProperties(MavenExecutionRequest request) {
487
488 Map<String, String> mergedProps = new HashMap<>();
489 mergedProps.putAll(getPropertiesFromRequestedProfiles(request));
490 mergedProps.putAll(new HashMap<String, String>((Map) request.getSystemProperties()));
491 mergedProps.putAll(new HashMap<String, String>((Map) request.getUserProperties()));
492 return mergedProps;
493 }
494
495 private Map<String, String> getPropertiesFromRequestedProfiles(MavenExecutionRequest request) {
496 HashSet<String> activeProfileId =
497 new HashSet<>(request.getProfileActivation().getRequiredActiveProfileIds());
498 activeProfileId.addAll(request.getProfileActivation().getOptionalActiveProfileIds());
499
500 return request.getProfiles().stream()
501 .filter(profile -> activeProfileId.contains(profile.getId()))
502 .map(ModelBase::getProperties)
503 .flatMap(properties -> properties.entrySet().stream())
504 .filter(e -> e.getValue() != null)
505 .collect(Collectors.toMap(
506 e -> String.valueOf(e.getKey()), e -> String.valueOf(e.getValue()), (k1, k2) -> k2));
507 }
508
509 private String getUserAgent() {
510 String version = runtimeInformation.getMavenVersion();
511 version = version.isEmpty() ? version : "/" + version;
512 return "Apache-Maven" + version + " (Java " + System.getProperty("java.version") + "; "
513 + System.getProperty("os.name") + " " + System.getProperty("os.version") + ")";
514 }
515 }