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