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.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.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 =
130                 Collections.singletonList(ArtifactDescriptorUtils.toRemoteRepository(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         File pomFile = pomArtifact.getFile();
164 
165         return new FileModelSource(pomFile);
166     }
167 
168     @Override
169     public ModelSource resolveModel(final Parent parent) throws UnresolvableModelException {
170         try {
171             final Artifact artifact =
172                     new DefaultArtifact(parent.getGroupId(), parent.getArtifactId(), "", "pom", parent.getVersion());
173 
174             final VersionRangeRequest versionRangeRequest = new VersionRangeRequest(artifact, repositories, context);
175             versionRangeRequest.setTrace(trace);
176 
177             final VersionRangeResult versionRangeResult =
178                     versionRangeResolver.resolveVersionRange(session, versionRangeRequest);
179 
180             if (versionRangeResult.getHighestVersion() == null) {
181                 throw new UnresolvableModelException(
182                         String.format(
183                                 "No versions matched the requested parent version range '%s'", parent.getVersion()),
184                         parent.getGroupId(),
185                         parent.getArtifactId(),
186                         parent.getVersion());
187             }
188 
189             if (versionRangeResult.getVersionConstraint() != null
190                     && versionRangeResult.getVersionConstraint().getRange() != null
191                     && versionRangeResult.getVersionConstraint().getRange().getUpperBound() == null) {
192                 // Message below is checked for in the MNG-2199 core IT.
193                 throw new UnresolvableModelException(
194                         String.format(
195                                 "The requested parent version range '%s' does not specify an upper bound",
196                                 parent.getVersion()),
197                         parent.getGroupId(),
198                         parent.getArtifactId(),
199                         parent.getVersion());
200             }
201 
202             parent.setVersion(versionRangeResult.getHighestVersion().toString());
203 
204             return resolveModel(parent.getGroupId(), parent.getArtifactId(), parent.getVersion());
205         } catch (final VersionRangeResolutionException e) {
206             throw new UnresolvableModelException(
207                     e.getMessage(), parent.getGroupId(), parent.getArtifactId(), parent.getVersion(), e);
208         }
209     }
210 
211     @Override
212     public ModelSource resolveModel(final Dependency dependency) throws UnresolvableModelException {
213         try {
214             final Artifact artifact = new DefaultArtifact(
215                     dependency.getGroupId(), dependency.getArtifactId(), "", "pom", dependency.getVersion());
216 
217             final VersionRangeRequest versionRangeRequest = new VersionRangeRequest(artifact, repositories, context);
218             versionRangeRequest.setTrace(trace);
219 
220             final VersionRangeResult versionRangeResult =
221                     versionRangeResolver.resolveVersionRange(session, versionRangeRequest);
222 
223             if (versionRangeResult.getHighestVersion() == null) {
224                 throw new UnresolvableModelException(
225                         String.format(
226                                 "No versions matched the requested dependency version range '%s'",
227                                 dependency.getVersion()),
228                         dependency.getGroupId(),
229                         dependency.getArtifactId(),
230                         dependency.getVersion());
231             }
232 
233             if (versionRangeResult.getVersionConstraint() != null
234                     && versionRangeResult.getVersionConstraint().getRange() != null
235                     && versionRangeResult.getVersionConstraint().getRange().getUpperBound() == null) {
236                 // Message below is checked for in the MNG-4463 core IT.
237                 throw new UnresolvableModelException(
238                         String.format(
239                                 "The requested dependency version range '%s' does not specify an upper bound",
240                                 dependency.getVersion()),
241                         dependency.getGroupId(),
242                         dependency.getArtifactId(),
243                         dependency.getVersion());
244             }
245 
246             dependency.setVersion(versionRangeResult.getHighestVersion().toString());
247 
248             return resolveModel(dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion());
249         } catch (VersionRangeResolutionException e) {
250             throw new UnresolvableModelException(
251                     e.getMessage(), dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion(), e);
252         }
253     }
254 }