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