View Javadoc
1   package org.apache.maven.repository.internal;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.File;
23  import java.util.ArrayList;
24  import java.util.Collections;
25  import java.util.HashSet;
26  import java.util.Iterator;
27  import java.util.List;
28  import java.util.Set;
29  
30  import org.apache.maven.model.Dependency;
31  import org.apache.maven.model.Parent;
32  import org.apache.maven.model.Repository;
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.eclipse.aether.RepositorySystemSession;
39  import org.eclipse.aether.RequestTrace;
40  import org.eclipse.aether.artifact.Artifact;
41  import org.eclipse.aether.artifact.DefaultArtifact;
42  import org.eclipse.aether.impl.ArtifactResolver;
43  import org.eclipse.aether.impl.RemoteRepositoryManager;
44  import org.eclipse.aether.impl.VersionRangeResolver;
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 dependency POMs. This resolver gives priority to those repositories that have
54   * been initially specified and repositories discovered in dependency POMs are recessively merged into the search chain.
55   *
56   * @author Benjamin Bentmann
57   * @see DefaultArtifactDescriptorReader
58   */
59  class DefaultModelResolver
60      implements ModelResolver
61  {
62  
63      private final RepositorySystemSession session;
64  
65      private final RequestTrace trace;
66  
67      private final String context;
68  
69      private List<RemoteRepository> repositories;
70  
71      private final List<RemoteRepository> externalRepositories;
72  
73      private final ArtifactResolver resolver;
74  
75      private final VersionRangeResolver versionRangeResolver;
76  
77      private final RemoteRepositoryManager remoteRepositoryManager;
78  
79      private final Set<String> repositoryIds;
80  
81      DefaultModelResolver( RepositorySystemSession session, RequestTrace trace, String context,
82                            ArtifactResolver resolver, VersionRangeResolver versionRangeResolver,
83                            RemoteRepositoryManager remoteRepositoryManager, List<RemoteRepository> repositories )
84      {
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      {
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 )
112         throws InvalidRepositoryException
113     {
114         addRepository( repository, false );
115     }
116 
117     @Override
118     public void addRepository( final Repository repository, boolean replace )
119         throws InvalidRepositoryException
120     {
121         if ( session.isIgnoreArtifactDescriptorRepositories() )
122         {
123             return;
124         }
125 
126         if ( !repositoryIds.add( repository.getId() ) )
127         {
128             if ( !replace )
129             {
130                 return;
131             }
132 
133             removeMatchingRepository( repositories, repository.getId() );
134         }
135 
136         List<RemoteRepository> newRepositories =
137             Collections.singletonList( ArtifactDescriptorUtils.toRemoteRepository( repository ) );
138 
139         this.repositories =
140             remoteRepositoryManager.aggregateRepositories( session, repositories, newRepositories, true );
141     }
142 
143     private static void removeMatchingRepository( Iterable<RemoteRepository> repositories, final String id )
144     {
145         Iterator<RemoteRepository> iterator = repositories.iterator();
146         while ( iterator.hasNext() )
147         {
148             RemoteRepository remoteRepository = iterator.next();
149             if ( remoteRepository.getId().equals( id ) )
150             {
151                 iterator.remove();
152             }
153         }
154     }
155 
156     @Override
157     public ModelResolver newCopy()
158     {
159         return new DefaultModelResolver( this );
160     }
161 
162     @Override
163     public ModelSource resolveModel( String groupId, String artifactId, String version )
164         throws UnresolvableModelException
165     {
166         Artifact pomArtifact = new DefaultArtifact( groupId, artifactId, "", "pom", version );
167 
168         try
169         {
170             ArtifactRequest request = new ArtifactRequest( pomArtifact, repositories, context );
171             request.setTrace( trace );
172             pomArtifact = resolver.resolveArtifact( session, request ).getArtifact();
173         }
174         catch ( ArtifactResolutionException e )
175         {
176             throw new UnresolvableModelException( e.getMessage(), groupId, artifactId, version, e );
177         }
178 
179         File pomFile = pomArtifact.getFile();
180 
181         return new FileModelSource( pomFile );
182     }
183 
184     @Override
185     public ModelSource resolveModel( final Parent parent )
186         throws UnresolvableModelException
187     {
188         try
189         {
190             final Artifact artifact = new DefaultArtifact( parent.getGroupId(), parent.getArtifactId(), "", "pom",
191                                                            parent.getVersion() );
192 
193             final VersionRangeRequest versionRangeRequest = new VersionRangeRequest( artifact, repositories, context );
194             versionRangeRequest.setTrace( trace );
195 
196             final VersionRangeResult versionRangeResult =
197                 versionRangeResolver.resolveVersionRange( session, versionRangeRequest );
198 
199             if ( versionRangeResult.getHighestVersion() == null )
200             {
201                 throw new UnresolvableModelException(
202                     String.format( "No versions matched the requested parent version range '%s'",
203                                    parent.getVersion() ),
204                     parent.getGroupId(), parent.getArtifactId(), parent.getVersion() );
205 
206             }
207 
208             if ( versionRangeResult.getVersionConstraint() != null
209                      && versionRangeResult.getVersionConstraint().getRange() != null
210                      && versionRangeResult.getVersionConstraint().getRange().getUpperBound() == null )
211             {
212                 // Message below is checked for in the MNG-2199 core IT.
213                 throw new UnresolvableModelException(
214                     String.format( "The requested parent version range '%s' does not specify an upper bound",
215                                    parent.getVersion() ),
216                     parent.getGroupId(), parent.getArtifactId(), parent.getVersion() );
217 
218             }
219 
220             parent.setVersion( versionRangeResult.getHighestVersion().toString() );
221 
222             return resolveModel( parent.getGroupId(), parent.getArtifactId(), parent.getVersion() );
223         }
224         catch ( final VersionRangeResolutionException e )
225         {
226             throw new UnresolvableModelException( e.getMessage(), parent.getGroupId(), parent.getArtifactId(),
227                                                   parent.getVersion(), e );
228 
229         }
230     }
231 
232     @Override
233     public ModelSource resolveModel( final Dependency dependency )
234         throws UnresolvableModelException
235     {
236         try
237         {
238             final Artifact artifact = new DefaultArtifact( dependency.getGroupId(), dependency.getArtifactId(), "",
239                                                            "pom", dependency.getVersion() );
240 
241             final VersionRangeRequest versionRangeRequest = new VersionRangeRequest( artifact, repositories, context );
242             versionRangeRequest.setTrace( trace );
243 
244             final VersionRangeResult versionRangeResult =
245                 versionRangeResolver.resolveVersionRange( session, versionRangeRequest );
246 
247             if ( versionRangeResult.getHighestVersion() == null )
248             {
249                 throw new UnresolvableModelException(
250                     String.format( "No versions matched the requested dependency version range '%s'",
251                                    dependency.getVersion() ),
252                     dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion() );
253 
254             }
255 
256             if ( versionRangeResult.getVersionConstraint() != null
257                      && versionRangeResult.getVersionConstraint().getRange() != null
258                      && versionRangeResult.getVersionConstraint().getRange().getUpperBound() == null )
259             {
260                 // Message below is checked for in the MNG-4463 core IT.
261                 throw new UnresolvableModelException(
262                     String.format( "The requested dependency version range '%s' does not specify an upper bound",
263                                    dependency.getVersion() ),
264                     dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion() );
265 
266             }
267 
268             dependency.setVersion( versionRangeResult.getHighestVersion().toString() );
269 
270             return resolveModel( dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion() );
271         }
272         catch ( VersionRangeResolutionException e )
273         {
274             throw new UnresolvableModelException( e.getMessage(), dependency.getGroupId(), dependency.getArtifactId(),
275                                                   dependency.getVersion(), e );
276 
277         }
278     }
279 }