View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.repository.internal;
20  
21  import javax.inject.Inject;
22  import javax.inject.Named;
23  import javax.inject.Singleton;
24  
25  import java.io.InputStream;
26  import java.nio.file.Files;
27  import java.util.ArrayList;
28  import java.util.Collections;
29  import java.util.HashMap;
30  import java.util.List;
31  import java.util.Map;
32  import java.util.Objects;
33  
34  import org.apache.maven.artifact.repository.metadata.Versioning;
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   * @deprecated since 4.0.0, use {@code maven-api-impl} jar instead/
63   */
64  @Named
65  @Singleton
66  @Deprecated(since = "4.0.0")
67  public class DefaultVersionRangeResolver implements VersionRangeResolver {
68  
69      private static final String MAVEN_METADATA_XML = "maven-metadata.xml";
70  
71      private final MetadataResolver metadataResolver;
72      private final SyncContextFactory syncContextFactory;
73      private final RepositoryEventDispatcher repositoryEventDispatcher;
74      private final VersionScheme versionScheme;
75  
76      @Inject
77      public DefaultVersionRangeResolver(
78              MetadataResolver metadataResolver,
79              SyncContextFactory syncContextFactory,
80              RepositoryEventDispatcher repositoryEventDispatcher,
81              VersionScheme versionScheme) {
82          this.metadataResolver = Objects.requireNonNull(metadataResolver, "metadataResolver cannot be null");
83          this.syncContextFactory = Objects.requireNonNull(syncContextFactory, "syncContextFactory cannot be null");
84          this.repositoryEventDispatcher =
85                  Objects.requireNonNull(repositoryEventDispatcher, "repositoryEventDispatcher cannot be null");
86          this.versionScheme = Objects.requireNonNull(versionScheme, "versionScheme cannot be null");
87      }
88  
89      @Override
90      public VersionRangeResult resolveVersionRange(RepositorySystemSession session, VersionRangeRequest request)
91              throws VersionRangeResolutionException {
92          VersionRangeResult result = new VersionRangeResult(request);
93  
94          VersionConstraint versionConstraint;
95          try {
96              versionConstraint =
97                      versionScheme.parseVersionConstraint(request.getArtifact().getVersion());
98          } catch (InvalidVersionSpecificationException e) {
99              result.addException(e);
100             throw new VersionRangeResolutionException(result);
101         }
102 
103         result.setVersionConstraint(versionConstraint);
104 
105         if (versionConstraint.getRange() == null) {
106             result.addVersion(versionConstraint.getVersion());
107         } else {
108             VersionRange.Bound lowerBound = versionConstraint.getRange().getLowerBound();
109             if (lowerBound != null
110                     && lowerBound.equals(versionConstraint.getRange().getUpperBound())) {
111                 result.addVersion(lowerBound.getVersion());
112             } else {
113                 Map<String, ArtifactRepository> versionIndex = getVersions(session, result, request);
114 
115                 List<Version> versions = new ArrayList<>();
116                 for (Map.Entry<String, ArtifactRepository> v : versionIndex.entrySet()) {
117                     try {
118                         Version ver = versionScheme.parseVersion(v.getKey());
119                         if (versionConstraint.containsVersion(ver)) {
120                             versions.add(ver);
121                             result.setRepository(ver, v.getValue());
122                         }
123                     } catch (InvalidVersionSpecificationException e) {
124                         result.addException(e);
125                     }
126                 }
127 
128                 Collections.sort(versions);
129                 result.setVersions(versions);
130             }
131         }
132 
133         return result;
134     }
135 
136     private Map<String, ArtifactRepository> getVersions(
137             RepositorySystemSession session, VersionRangeResult result, VersionRangeRequest request) {
138         RequestTrace trace = RequestTrace.newChild(request.getTrace(), request);
139 
140         Map<String, ArtifactRepository> versionIndex = new HashMap<>();
141 
142         Metadata metadata = new DefaultMetadata(
143                 request.getArtifact().getGroupId(),
144                 request.getArtifact().getArtifactId(),
145                 MAVEN_METADATA_XML,
146                 Metadata.Nature.RELEASE_OR_SNAPSHOT);
147 
148         List<MetadataRequest> metadataRequests =
149                 new ArrayList<>(request.getRepositories().size());
150 
151         metadataRequests.add(new MetadataRequest(metadata, null, request.getRequestContext()));
152 
153         for (RemoteRepository repository : request.getRepositories()) {
154             MetadataRequest metadataRequest = new MetadataRequest(metadata, repository, request.getRequestContext());
155             metadataRequest.setDeleteLocalCopyIfMissing(true);
156             metadataRequest.setTrace(trace);
157             metadataRequests.add(metadataRequest);
158         }
159 
160         List<MetadataResult> metadataResults = metadataResolver.resolveMetadata(session, metadataRequests);
161 
162         WorkspaceReader workspace = session.getWorkspaceReader();
163         if (workspace != null) {
164             List<String> versions = workspace.findVersions(request.getArtifact());
165             for (String version : versions) {
166                 versionIndex.put(version, workspace.getRepository());
167             }
168         }
169 
170         for (MetadataResult metadataResult : metadataResults) {
171             result.addException(metadataResult.getException());
172 
173             ArtifactRepository repository = metadataResult.getRequest().getRepository();
174             if (repository == null) {
175                 repository = session.getLocalRepository();
176             }
177 
178             Versioning versioning = readVersions(session, trace, metadataResult.getMetadata(), repository, result);
179 
180             versioning = filterVersionsByRepositoryType(
181                     versioning, metadataResult.getRequest().getRepository());
182 
183             for (String version : versioning.getVersions()) {
184                 if (!versionIndex.containsKey(version)) {
185                     versionIndex.put(version, repository);
186                 }
187             }
188         }
189 
190         return versionIndex;
191     }
192 
193     private Versioning readVersions(
194             RepositorySystemSession session,
195             RequestTrace trace,
196             Metadata metadata,
197             ArtifactRepository repository,
198             VersionRangeResult result) {
199         Versioning versioning = null;
200         try {
201             if (metadata != null) {
202                 try (SyncContext syncContext = syncContextFactory.newInstance(session, true)) {
203                     syncContext.acquire(null, Collections.singleton(metadata));
204 
205                     if (metadata.getPath() != null && Files.exists(metadata.getPath())) {
206                         try (InputStream in = Files.newInputStream(metadata.getPath())) {
207                             versioning = new Versioning(
208                                     new MetadataStaxReader().read(in, false).getVersioning());
209                         }
210                     }
211                 }
212             }
213         } catch (Exception e) {
214             invalidMetadata(session, trace, metadata, repository, e);
215             result.addException(e);
216         }
217 
218         return (versioning != null) ? versioning : new Versioning();
219     }
220 
221     private Versioning filterVersionsByRepositoryType(Versioning versioning, RemoteRepository remoteRepository) {
222         if (remoteRepository == null) {
223             return versioning;
224         }
225 
226         Versioning filteredVersions = versioning.clone();
227 
228         for (String version : versioning.getVersions()) {
229             if (!remoteRepository
230                     .getPolicy(DefaultModelVersionParser.checkSnapshot(version))
231                     .isEnabled()) {
232                 filteredVersions.removeVersion(version);
233             }
234         }
235 
236         return filteredVersions;
237     }
238 
239     private void invalidMetadata(
240             RepositorySystemSession session,
241             RequestTrace trace,
242             Metadata metadata,
243             ArtifactRepository repository,
244             Exception exception) {
245         RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_INVALID);
246         event.setTrace(trace);
247         event.setMetadata(metadata);
248         event.setException(exception);
249         event.setRepository(repository);
250 
251         repositoryEventDispatcher.dispatch(event.build());
252     }
253 }