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