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