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