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