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 java.util.ArrayList;
22  import java.util.Collections;
23  import java.util.HashSet;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.Set;
27  import java.util.concurrent.atomic.AtomicReference;
28  
29  import org.apache.maven.api.model.Dependency;
30  import org.apache.maven.api.model.Parent;
31  import org.apache.maven.api.model.Repository;
32  import org.apache.maven.model.building.ArtifactModelSource;
33  import org.apache.maven.model.building.ModelSource;
34  import org.apache.maven.model.resolution.InvalidRepositoryException;
35  import org.apache.maven.model.resolution.ModelResolver;
36  import org.apache.maven.model.resolution.UnresolvableModelException;
37  import org.eclipse.aether.RepositorySystemSession;
38  import org.eclipse.aether.RequestTrace;
39  import org.eclipse.aether.artifact.Artifact;
40  import org.eclipse.aether.artifact.DefaultArtifact;
41  import org.eclipse.aether.impl.ArtifactResolver;
42  import org.eclipse.aether.impl.RemoteRepositoryManager;
43  import org.eclipse.aether.impl.VersionRangeResolver;
44  import org.eclipse.aether.repository.RemoteRepository;
45  import org.eclipse.aether.resolution.ArtifactRequest;
46  import org.eclipse.aether.resolution.ArtifactResolutionException;
47  import org.eclipse.aether.resolution.VersionRangeRequest;
48  import org.eclipse.aether.resolution.VersionRangeResolutionException;
49  import org.eclipse.aether.resolution.VersionRangeResult;
50  
51  /**
52   * A model resolver to assist building of dependency POMs. This resolver gives priority to those repositories that have
53   * been initially specified and repositories discovered in dependency POMs are recessively merged into the search chain.
54   *
55   * @author Benjamin Bentmann
56   * @see DefaultArtifactDescriptorReader
57   */
58  class DefaultModelResolver implements ModelResolver {
59  
60      private final RepositorySystemSession session;
61  
62      private final RequestTrace trace;
63  
64      private final String context;
65  
66      private List<RemoteRepository> repositories;
67  
68      private final List<RemoteRepository> externalRepositories;
69  
70      private final ArtifactResolver resolver;
71  
72      private final VersionRangeResolver versionRangeResolver;
73  
74      private final RemoteRepositoryManager remoteRepositoryManager;
75  
76      private final Set<String> repositoryIds;
77  
78      DefaultModelResolver(
79              RepositorySystemSession session,
80              RequestTrace trace,
81              String context,
82              ArtifactResolver resolver,
83              VersionRangeResolver versionRangeResolver,
84              RemoteRepositoryManager remoteRepositoryManager,
85              List<RemoteRepository> repositories) {
86          this.session = session;
87          this.trace = trace;
88          this.context = context;
89          this.resolver = resolver;
90          this.versionRangeResolver = versionRangeResolver;
91          this.remoteRepositoryManager = remoteRepositoryManager;
92          this.repositories = repositories;
93          this.externalRepositories = Collections.unmodifiableList(new ArrayList<>(repositories));
94  
95          this.repositoryIds = new HashSet<>();
96      }
97  
98      private DefaultModelResolver(DefaultModelResolver original) {
99          this.session = original.session;
100         this.trace = original.trace;
101         this.context = original.context;
102         this.resolver = original.resolver;
103         this.versionRangeResolver = original.versionRangeResolver;
104         this.remoteRepositoryManager = original.remoteRepositoryManager;
105         this.repositories = new ArrayList<>(original.repositories);
106         this.externalRepositories = original.externalRepositories;
107         this.repositoryIds = new HashSet<>(original.repositoryIds);
108     }
109 
110     @Override
111     public void addRepository(Repository repository) throws InvalidRepositoryException {
112         addRepository(repository, false);
113     }
114 
115     @Override
116     public void addRepository(final Repository repository, boolean replace) throws InvalidRepositoryException {
117         if (session.isIgnoreArtifactDescriptorRepositories()) {
118             return;
119         }
120 
121         if (!repositoryIds.add(repository.getId())) {
122             if (!replace) {
123                 return;
124             }
125 
126             removeMatchingRepository(repositories, repository.getId());
127         }
128 
129         List<RemoteRepository> newRepositories = Collections.singletonList(
130                 ArtifactDescriptorUtils.toRemoteRepository(new org.apache.maven.model.Repository(repository)));
131 
132         this.repositories = remoteRepositoryManager.aggregateRepositories(session, repositories, newRepositories, true);
133     }
134 
135     private static void removeMatchingRepository(Iterable<RemoteRepository> repositories, final String id) {
136         Iterator<RemoteRepository> iterator = repositories.iterator();
137         while (iterator.hasNext()) {
138             RemoteRepository remoteRepository = iterator.next();
139             if (remoteRepository.getId().equals(id)) {
140                 iterator.remove();
141             }
142         }
143     }
144 
145     @Override
146     public ModelResolver newCopy() {
147         return new DefaultModelResolver(this);
148     }
149 
150     @Override
151     public ModelSource resolveModel(String groupId, String artifactId, String version)
152             throws UnresolvableModelException {
153         Artifact pomArtifact = new DefaultArtifact(groupId, artifactId, "", "pom", version);
154 
155         try {
156             ArtifactRequest request = new ArtifactRequest(pomArtifact, repositories, context);
157             request.setTrace(trace);
158             pomArtifact = resolver.resolveArtifact(session, request).getArtifact();
159         } catch (ArtifactResolutionException e) {
160             throw new UnresolvableModelException(e.getMessage(), groupId, artifactId, version, e);
161         }
162 
163         return new ArtifactModelSource(pomArtifact.getFile(), groupId, artifactId, version);
164     }
165 
166     @Override
167     public ModelSource resolveModel(final Parent parent, final AtomicReference<Parent> modified)
168             throws UnresolvableModelException {
169         try {
170             final Artifact artifact =
171                     new DefaultArtifact(parent.getGroupId(), parent.getArtifactId(), "", "pom", parent.getVersion());
172 
173             final VersionRangeRequest versionRangeRequest = new VersionRangeRequest(artifact, repositories, context);
174             versionRangeRequest.setTrace(trace);
175 
176             final VersionRangeResult versionRangeResult =
177                     versionRangeResolver.resolveVersionRange(session, versionRangeRequest);
178 
179             if (versionRangeResult.getHighestVersion() == null) {
180                 throw new UnresolvableModelException(
181                         String.format(
182                                 "No versions matched the requested parent version range '%s'", parent.getVersion()),
183                         parent.getGroupId(),
184                         parent.getArtifactId(),
185                         parent.getVersion());
186             }
187 
188             if (versionRangeResult.getVersionConstraint() != null
189                     && versionRangeResult.getVersionConstraint().getRange() != null
190                     && versionRangeResult.getVersionConstraint().getRange().getUpperBound() == null) {
191                 // Message below is checked for in the MNG-2199 core IT.
192                 throw new UnresolvableModelException(
193                         String.format(
194                                 "The requested parent version range '%s' does not specify an upper bound",
195                                 parent.getVersion()),
196                         parent.getGroupId(),
197                         parent.getArtifactId(),
198                         parent.getVersion());
199             }
200 
201             String newVersion = versionRangeResult.getHighestVersion().toString();
202             if (!parent.getVersion().equals(newVersion)) {
203                 modified.set(parent.withVersion(newVersion));
204             }
205 
206             return resolveModel(parent.getGroupId(), parent.getArtifactId(), newVersion);
207         } catch (final VersionRangeResolutionException e) {
208             throw new UnresolvableModelException(
209                     e.getMessage(), parent.getGroupId(), parent.getArtifactId(), parent.getVersion(), e);
210         }
211     }
212 
213     @Override
214     public ModelSource resolveModel(final Dependency dependency, AtomicReference<Dependency> modified)
215             throws UnresolvableModelException {
216         try {
217             final Artifact artifact = new DefaultArtifact(
218                     dependency.getGroupId(), dependency.getArtifactId(), "", "pom", dependency.getVersion());
219 
220             final VersionRangeRequest versionRangeRequest = new VersionRangeRequest(artifact, repositories, context);
221             versionRangeRequest.setTrace(trace);
222 
223             final VersionRangeResult versionRangeResult =
224                     versionRangeResolver.resolveVersionRange(session, versionRangeRequest);
225 
226             if (versionRangeResult.getHighestVersion() == null) {
227                 throw new UnresolvableModelException(
228                         String.format(
229                                 "No versions matched the requested dependency version range '%s'",
230                                 dependency.getVersion()),
231                         dependency.getGroupId(),
232                         dependency.getArtifactId(),
233                         dependency.getVersion());
234             }
235 
236             if (versionRangeResult.getVersionConstraint() != null
237                     && versionRangeResult.getVersionConstraint().getRange() != null
238                     && versionRangeResult.getVersionConstraint().getRange().getUpperBound() == null) {
239                 // Message below is checked for in the MNG-4463 core IT.
240                 throw new UnresolvableModelException(
241                         String.format(
242                                 "The requested dependency version range '%s' does not specify an upper bound",
243                                 dependency.getVersion()),
244                         dependency.getGroupId(),
245                         dependency.getArtifactId(),
246                         dependency.getVersion());
247             }
248 
249             String newVersion = versionRangeResult.getHighestVersion().toString();
250             if (!dependency.getVersion().equals(newVersion)) {
251                 modified.set(dependency.withVersion(newVersion));
252             }
253 
254             return resolveModel(dependency.getGroupId(), dependency.getArtifactId(), newVersion);
255         } catch (VersionRangeResolutionException e) {
256             throw new UnresolvableModelException(
257                     e.getMessage(), dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion(), e);
258         }
259     }
260 
261     @Override
262     public ModelSource resolveModel(org.apache.maven.model.Parent parent) throws UnresolvableModelException {
263         AtomicReference<org.apache.maven.api.model.Parent> resolvedParent = new AtomicReference<>();
264         ModelSource result = resolveModel(parent.getDelegate(), resolvedParent);
265         if (resolvedParent.get() != null) {
266             parent.setVersion(resolvedParent.get().getVersion());
267         }
268         return result;
269     }
270 
271     @Override
272     public ModelSource resolveModel(org.apache.maven.model.Dependency dependency) throws UnresolvableModelException {
273         AtomicReference<org.apache.maven.api.model.Dependency> resolvedDependency = new AtomicReference<>();
274         ModelSource result = resolveModel(dependency.getDelegate(), resolvedDependency);
275         if (resolvedDependency.get() != null) {
276             dependency.setVersion(resolvedDependency.get().getVersion());
277         }
278         return result;
279     }
280 
281     @Override
282     public void addRepository(org.apache.maven.model.Repository repository) throws InvalidRepositoryException {
283         addRepository(repository.getDelegate());
284     }
285 
286     @Override
287     public void addRepository(org.apache.maven.model.Repository repository, boolean replace)
288             throws InvalidRepositoryException {
289         addRepository(repository.getDelegate(), replace);
290     }
291 }