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.ArtifactUtils;
35  import org.apache.maven.artifact.repository.metadata.Versioning;
36  import org.apache.maven.artifact.repository.metadata.io.MetadataStaxReader;
37  import org.eclipse.aether.RepositoryEvent;
38  import org.eclipse.aether.RepositoryEvent.EventType;
39  import org.eclipse.aether.RepositorySystemSession;
40  import org.eclipse.aether.RequestTrace;
41  import org.eclipse.aether.SyncContext;
42  import org.eclipse.aether.impl.MetadataResolver;
43  import org.eclipse.aether.impl.RepositoryEventDispatcher;
44  import org.eclipse.aether.impl.VersionRangeResolver;
45  import org.eclipse.aether.metadata.DefaultMetadata;
46  import org.eclipse.aether.metadata.Metadata;
47  import org.eclipse.aether.repository.ArtifactRepository;
48  import org.eclipse.aether.repository.RemoteRepository;
49  import org.eclipse.aether.repository.WorkspaceReader;
50  import org.eclipse.aether.resolution.MetadataRequest;
51  import org.eclipse.aether.resolution.MetadataResult;
52  import org.eclipse.aether.resolution.VersionRangeRequest;
53  import org.eclipse.aether.resolution.VersionRangeResolutionException;
54  import org.eclipse.aether.resolution.VersionRangeResult;
55  import org.eclipse.aether.spi.synccontext.SyncContextFactory;
56  import org.eclipse.aether.version.InvalidVersionSpecificationException;
57  import org.eclipse.aether.version.Version;
58  import org.eclipse.aether.version.VersionConstraint;
59  import org.eclipse.aether.version.VersionRange;
60  import org.eclipse.aether.version.VersionScheme;
61  
62  /**
63   */
64  @Named
65  @Singleton
66  public class DefaultVersionRangeResolver implements VersionRangeResolver {
67  
68      private static final String MAVEN_METADATA_XML = "maven-metadata.xml";
69  
70      private final MetadataResolver metadataResolver;
71      private final SyncContextFactory syncContextFactory;
72      private final RepositoryEventDispatcher repositoryEventDispatcher;
73      private final VersionScheme versionScheme;
74  
75      @Inject
76      public DefaultVersionRangeResolver(
77              MetadataResolver metadataResolver,
78              SyncContextFactory syncContextFactory,
79              RepositoryEventDispatcher repositoryEventDispatcher,
80              VersionScheme versionScheme) {
81          this.metadataResolver = Objects.requireNonNull(metadataResolver, "metadataResolver cannot be null");
82          this.syncContextFactory = Objects.requireNonNull(syncContextFactory, "syncContextFactory cannot be null");
83          this.repositoryEventDispatcher =
84                  Objects.requireNonNull(repositoryEventDispatcher, "repositoryEventDispatcher cannot be null");
85          this.versionScheme = Objects.requireNonNull(versionScheme, "versionScheme cannot be null");
86      }
87  
88      @Override
89      public VersionRangeResult resolveVersionRange(RepositorySystemSession session, VersionRangeRequest request)
90              throws VersionRangeResolutionException {
91          VersionRangeResult result = new VersionRangeResult(request);
92  
93          VersionConstraint versionConstraint;
94          try {
95              versionConstraint =
96                      versionScheme.parseVersionConstraint(request.getArtifact().getVersion());
97          } catch (InvalidVersionSpecificationException e) {
98              result.addException(e);
99              throw new VersionRangeResolutionException(result);
100         }
101 
102         result.setVersionConstraint(versionConstraint);
103 
104         if (versionConstraint.getRange() == null) {
105             result.addVersion(versionConstraint.getVersion());
106         } else {
107             VersionRange.Bound lowerBound = versionConstraint.getRange().getLowerBound();
108             if (lowerBound != null
109                     && lowerBound.equals(versionConstraint.getRange().getUpperBound())) {
110                 result.addVersion(lowerBound.getVersion());
111             } else {
112                 Map<String, ArtifactRepository> versionIndex = getVersions(session, result, request);
113 
114                 List<Version> versions = new ArrayList<>();
115                 for (Map.Entry<String, ArtifactRepository> v : versionIndex.entrySet()) {
116                     try {
117                         Version ver = versionScheme.parseVersion(v.getKey());
118                         if (versionConstraint.containsVersion(ver)) {
119                             versions.add(ver);
120                             result.setRepository(ver, v.getValue());
121                         }
122                     } catch (InvalidVersionSpecificationException e) {
123                         result.addException(e);
124                     }
125                 }
126 
127                 Collections.sort(versions);
128                 result.setVersions(versions);
129             }
130         }
131 
132         return result;
133     }
134 
135     private Map<String, ArtifactRepository> getVersions(
136             RepositorySystemSession session, VersionRangeResult result, VersionRangeRequest request) {
137         RequestTrace trace = RequestTrace.newChild(request.getTrace(), request);
138 
139         Map<String, ArtifactRepository> versionIndex = new HashMap<>();
140 
141         Metadata metadata = new DefaultMetadata(
142                 request.getArtifact().getGroupId(),
143                 request.getArtifact().getArtifactId(),
144                 MAVEN_METADATA_XML,
145                 Metadata.Nature.RELEASE_OR_SNAPSHOT);
146 
147         List<MetadataRequest> metadataRequests =
148                 new ArrayList<>(request.getRepositories().size());
149 
150         metadataRequests.add(new MetadataRequest(metadata, null, request.getRequestContext()));
151 
152         for (RemoteRepository repository : request.getRepositories()) {
153             MetadataRequest metadataRequest = new MetadataRequest(metadata, repository, request.getRequestContext());
154             metadataRequest.setDeleteLocalCopyIfMissing(true);
155             metadataRequest.setTrace(trace);
156             metadataRequests.add(metadataRequest);
157         }
158 
159         List<MetadataResult> metadataResults = metadataResolver.resolveMetadata(session, metadataRequests);
160 
161         WorkspaceReader workspace = session.getWorkspaceReader();
162         if (workspace != null) {
163             List<String> versions = workspace.findVersions(request.getArtifact());
164             for (String version : versions) {
165                 versionIndex.put(version, workspace.getRepository());
166             }
167         }
168 
169         for (MetadataResult metadataResult : metadataResults) {
170             result.addException(metadataResult.getException());
171 
172             ArtifactRepository repository = metadataResult.getRequest().getRepository();
173             if (repository == null) {
174                 repository = session.getLocalRepository();
175             }
176 
177             Versioning versioning = readVersions(session, trace, metadataResult.getMetadata(), repository, result);
178 
179             versioning = filterVersionsByRepositoryType(
180                     versioning, metadataResult.getRequest().getRepository());
181 
182             for (String version : versioning.getVersions()) {
183                 if (!versionIndex.containsKey(version)) {
184                     versionIndex.put(version, repository);
185                 }
186             }
187         }
188 
189         return versionIndex;
190     }
191 
192     private Versioning readVersions(
193             RepositorySystemSession session,
194             RequestTrace trace,
195             Metadata metadata,
196             ArtifactRepository repository,
197             VersionRangeResult result) {
198         Versioning versioning = null;
199         try {
200             if (metadata != null) {
201                 try (SyncContext syncContext = syncContextFactory.newInstance(session, true)) {
202                     syncContext.acquire(null, Collections.singleton(metadata));
203 
204                     if (metadata.getFile() != null && metadata.getFile().exists()) {
205                         try (InputStream in =
206                                 Files.newInputStream(metadata.getFile().toPath())) {
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.getPolicy(ArtifactUtils.isSnapshot(version)).isEnabled()) {
230                 filteredVersions.removeVersion(version);
231             }
232         }
233 
234         return filteredVersions;
235     }
236 
237     private void invalidMetadata(
238             RepositorySystemSession session,
239             RequestTrace trace,
240             Metadata metadata,
241             ArtifactRepository repository,
242             Exception exception) {
243         RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_INVALID);
244         event.setTrace(trace);
245         event.setMetadata(metadata);
246         event.setException(exception);
247         event.setRepository(repository);
248 
249         repositoryEventDispatcher.dispatch(event.build());
250     }
251 }