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