1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.internal.impl.resolver;
20
21 import java.io.InputStream;
22 import java.nio.file.Files;
23 import java.util.ArrayList;
24 import java.util.Collections;
25 import java.util.HashMap;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Objects;
29
30 import org.apache.maven.api.di.Inject;
31 import org.apache.maven.api.di.Named;
32 import org.apache.maven.api.di.Singleton;
33 import org.apache.maven.api.metadata.Versioning;
34 import org.apache.maven.internal.impl.DefaultModelVersionParser;
35 import org.apache.maven.metadata.v4.MetadataStaxReader;
36 import org.eclipse.aether.RepositoryEvent;
37 import org.eclipse.aether.RepositoryEvent.EventType;
38 import org.eclipse.aether.RepositorySystemSession;
39 import org.eclipse.aether.RequestTrace;
40 import org.eclipse.aether.SyncContext;
41 import org.eclipse.aether.impl.MetadataResolver;
42 import org.eclipse.aether.impl.RepositoryEventDispatcher;
43 import org.eclipse.aether.impl.VersionRangeResolver;
44 import org.eclipse.aether.metadata.DefaultMetadata;
45 import org.eclipse.aether.metadata.Metadata;
46 import org.eclipse.aether.repository.ArtifactRepository;
47 import org.eclipse.aether.repository.RemoteRepository;
48 import org.eclipse.aether.repository.WorkspaceReader;
49 import org.eclipse.aether.resolution.MetadataRequest;
50 import org.eclipse.aether.resolution.MetadataResult;
51 import org.eclipse.aether.resolution.VersionRangeRequest;
52 import org.eclipse.aether.resolution.VersionRangeResolutionException;
53 import org.eclipse.aether.resolution.VersionRangeResult;
54 import org.eclipse.aether.spi.synccontext.SyncContextFactory;
55 import org.eclipse.aether.version.InvalidVersionSpecificationException;
56 import org.eclipse.aether.version.Version;
57 import org.eclipse.aether.version.VersionConstraint;
58 import org.eclipse.aether.version.VersionRange;
59 import org.eclipse.aether.version.VersionScheme;
60
61
62
63 @Named
64 @Singleton
65 public class DefaultVersionRangeResolver implements VersionRangeResolver {
66
67 private static final String MAVEN_METADATA_XML = "maven-metadata.xml";
68
69 private final MetadataResolver metadataResolver;
70 private final SyncContextFactory syncContextFactory;
71 private final RepositoryEventDispatcher repositoryEventDispatcher;
72 private final VersionScheme versionScheme;
73
74 @Inject
75 public DefaultVersionRangeResolver(
76 MetadataResolver metadataResolver,
77 SyncContextFactory syncContextFactory,
78 RepositoryEventDispatcher repositoryEventDispatcher,
79 VersionScheme versionScheme) {
80 this.metadataResolver = Objects.requireNonNull(metadataResolver, "metadataResolver cannot be null");
81 this.syncContextFactory = Objects.requireNonNull(syncContextFactory, "syncContextFactory cannot be null");
82 this.repositoryEventDispatcher =
83 Objects.requireNonNull(repositoryEventDispatcher, "repositoryEventDispatcher cannot be null");
84 this.versionScheme = Objects.requireNonNull(versionScheme, "versionScheme cannot be null");
85 }
86
87 @Override
88 public VersionRangeResult resolveVersionRange(RepositorySystemSession session, VersionRangeRequest request)
89 throws VersionRangeResolutionException {
90 VersionRangeResult result = new VersionRangeResult(request);
91
92 VersionConstraint versionConstraint;
93 try {
94 versionConstraint =
95 versionScheme.parseVersionConstraint(request.getArtifact().getVersion());
96 } catch (InvalidVersionSpecificationException e) {
97 result.addException(e);
98 throw new VersionRangeResolutionException(result);
99 }
100
101 result.setVersionConstraint(versionConstraint);
102
103 if (versionConstraint.getRange() == null) {
104 result.addVersion(versionConstraint.getVersion());
105 } else {
106 VersionRange.Bound lowerBound = versionConstraint.getRange().getLowerBound();
107 if (lowerBound != null
108 && lowerBound.equals(versionConstraint.getRange().getUpperBound())) {
109 result.addVersion(lowerBound.getVersion());
110 } else {
111 Map<String, ArtifactRepository> versionIndex = getVersions(session, result, request);
112
113 List<Version> versions = new ArrayList<>();
114 for (Map.Entry<String, ArtifactRepository> v : versionIndex.entrySet()) {
115 try {
116 Version ver = versionScheme.parseVersion(v.getKey());
117 if (versionConstraint.containsVersion(ver)) {
118 versions.add(ver);
119 result.setRepository(ver, v.getValue());
120 }
121 } catch (InvalidVersionSpecificationException e) {
122 result.addException(e);
123 }
124 }
125
126 Collections.sort(versions);
127 result.setVersions(versions);
128 }
129 }
130
131 return result;
132 }
133
134 private Map<String, ArtifactRepository> getVersions(
135 RepositorySystemSession session, VersionRangeResult result, VersionRangeRequest request) {
136 RequestTrace trace = RequestTrace.newChild(request.getTrace(), request);
137
138 Map<String, ArtifactRepository> versionIndex = new HashMap<>();
139
140 Metadata metadata = new DefaultMetadata(
141 request.getArtifact().getGroupId(),
142 request.getArtifact().getArtifactId(),
143 MAVEN_METADATA_XML,
144 Metadata.Nature.RELEASE_OR_SNAPSHOT);
145
146 List<MetadataRequest> metadataRequests =
147 new ArrayList<>(request.getRepositories().size());
148
149 metadataRequests.add(new MetadataRequest(metadata, null, request.getRequestContext()));
150
151 for (RemoteRepository repository : request.getRepositories()) {
152 MetadataRequest metadataRequest = new MetadataRequest(metadata, repository, request.getRequestContext());
153 metadataRequest.setDeleteLocalCopyIfMissing(true);
154 metadataRequest.setTrace(trace);
155 metadataRequests.add(metadataRequest);
156 }
157
158 List<MetadataResult> metadataResults = metadataResolver.resolveMetadata(session, metadataRequests);
159
160 WorkspaceReader workspace = session.getWorkspaceReader();
161 if (workspace != null) {
162 List<String> versions = workspace.findVersions(request.getArtifact());
163 for (String version : versions) {
164 versionIndex.put(version, workspace.getRepository());
165 }
166 }
167
168 for (MetadataResult metadataResult : metadataResults) {
169 result.addException(metadataResult.getException());
170
171 ArtifactRepository repository = metadataResult.getRequest().getRepository();
172 if (repository == null) {
173 repository = session.getLocalRepository();
174 }
175
176 Versioning versioning = readVersions(session, trace, metadataResult.getMetadata(), repository, result);
177
178 versioning = filterVersionsByRepositoryType(
179 versioning, metadataResult.getRequest().getRepository());
180
181 for (String version : versioning.getVersions()) {
182 if (!versionIndex.containsKey(version)) {
183 versionIndex.put(version, repository);
184 }
185 }
186 }
187
188 return versionIndex;
189 }
190
191 private Versioning readVersions(
192 RepositorySystemSession session,
193 RequestTrace trace,
194 Metadata metadata,
195 ArtifactRepository repository,
196 VersionRangeResult result) {
197 Versioning versioning = null;
198 try {
199 if (metadata != null) {
200 try (SyncContext syncContext = syncContextFactory.newInstance(session, true)) {
201 syncContext.acquire(null, Collections.singleton(metadata));
202
203 if (metadata.getPath() != null && Files.exists(metadata.getPath())) {
204 try (InputStream in = Files.newInputStream(metadata.getPath())) {
205 versioning =
206 new MetadataStaxReader().read(in, false).getVersioning();
207 }
208 }
209 }
210 }
211 } catch (Exception e) {
212 invalidMetadata(session, trace, metadata, repository, e);
213 result.addException(e);
214 }
215
216 return (versioning != null) ? versioning : Versioning.newInstance();
217 }
218
219 private Versioning filterVersionsByRepositoryType(Versioning versioning, RemoteRepository remoteRepository) {
220 if (remoteRepository == null) {
221 return versioning;
222 }
223 return versioning.withVersions(versioning.getVersions().stream()
224 .filter(version -> remoteRepository
225 .getPolicy(DefaultModelVersionParser.checkSnapshot(version))
226 .isEnabled())
227 .toList());
228 }
229
230 private void invalidMetadata(
231 RepositorySystemSession session,
232 RequestTrace trace,
233 Metadata metadata,
234 ArtifactRepository repository,
235 Exception exception) {
236 RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_INVALID);
237 event.setTrace(trace);
238 event.setMetadata(metadata);
239 event.setException(exception);
240 event.setRepository(repository);
241
242 repositoryEventDispatcher.dispatch(event.build());
243 }
244 }