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.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  import java.util.concurrent.atomic.AtomicReference;
29  
30  import org.apache.maven.api.model.Dependency;
31  import org.apache.maven.api.model.Parent;
32  import org.apache.maven.api.model.Repository;
33  import org.apache.maven.model.building.ArtifactModelSource;
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(
138                     new org.apache.maven.model.Repository( repository ) ) );
139 
140         this.repositories =
141             remoteRepositoryManager.aggregateRepositories( session, repositories, newRepositories, true );
142     }
143 
144     private static void removeMatchingRepository( Iterable<RemoteRepository> repositories, final String id )
145     {
146         Iterator<RemoteRepository> iterator = repositories.iterator();
147         while ( iterator.hasNext() )
148         {
149             RemoteRepository remoteRepository = iterator.next();
150             if ( remoteRepository.getId().equals( id ) )
151             {
152                 iterator.remove();
153             }
154         }
155     }
156 
157     @Override
158     public ModelResolver newCopy()
159     {
160         return new DefaultModelResolver( this );
161     }
162 
163     @Override
164     public ModelSource resolveModel( String groupId, String artifactId, String version )
165         throws UnresolvableModelException
166     {
167         Artifact pomArtifact = new DefaultArtifact( groupId, artifactId, "", "pom", version );
168 
169         try
170         {
171             ArtifactRequest request = new ArtifactRequest( pomArtifact, repositories, context );
172             request.setTrace( trace );
173             pomArtifact = resolver.resolveArtifact( session, request ).getArtifact();
174         }
175         catch ( ArtifactResolutionException e )
176         {
177             throw new UnresolvableModelException( e.getMessage(), groupId, artifactId, version, e );
178         }
179 
180         return new ArtifactModelSource( pomArtifact.getFile(), groupId, artifactId, version );
181     }
182 
183     @Override
184     public ModelSource resolveModel( final Parent parent, final AtomicReference<Parent> modified )
185         throws UnresolvableModelException
186     {
187         try
188         {
189             final Artifact artifact = new DefaultArtifact( parent.getGroupId(), parent.getArtifactId(), "", "pom",
190                                                            parent.getVersion() );
191 
192             final VersionRangeRequest versionRangeRequest = new VersionRangeRequest( artifact, repositories, context );
193             versionRangeRequest.setTrace( trace );
194 
195             final VersionRangeResult versionRangeResult =
196                 versionRangeResolver.resolveVersionRange( session, versionRangeRequest );
197 
198             if ( versionRangeResult.getHighestVersion() == null )
199             {
200                 throw new UnresolvableModelException(
201                     String.format( "No versions matched the requested parent version range '%s'",
202                                    parent.getVersion() ),
203                     parent.getGroupId(), parent.getArtifactId(), parent.getVersion() );
204 
205             }
206 
207             if ( versionRangeResult.getVersionConstraint() != null
208                      && versionRangeResult.getVersionConstraint().getRange() != null
209                      && versionRangeResult.getVersionConstraint().getRange().getUpperBound() == null )
210             {
211                 // Message below is checked for in the MNG-2199 core IT.
212                 throw new UnresolvableModelException(
213                     String.format( "The requested parent version range '%s' does not specify an upper bound",
214                                    parent.getVersion() ),
215                     parent.getGroupId(), parent.getArtifactId(), parent.getVersion() );
216 
217             }
218 
219             String newVersion = versionRangeResult.getHighestVersion().toString();
220             if ( !parent.getVersion().equals( newVersion ) )
221             {
222                 modified.set( parent.withVersion( newVersion ) );
223             }
224 
225             return resolveModel( parent.getGroupId(), parent.getArtifactId(), newVersion );
226         }
227         catch ( final VersionRangeResolutionException e )
228         {
229             throw new UnresolvableModelException( e.getMessage(), parent.getGroupId(), parent.getArtifactId(),
230                                                   parent.getVersion(), e );
231 
232         }
233     }
234 
235     @Override
236     public ModelSource resolveModel( final Dependency dependency, AtomicReference<Dependency> modified )
237         throws UnresolvableModelException
238     {
239         try
240         {
241             final Artifact artifact = new DefaultArtifact( dependency.getGroupId(), dependency.getArtifactId(), "",
242                                                            "pom", dependency.getVersion() );
243 
244             final VersionRangeRequest versionRangeRequest = new VersionRangeRequest( artifact, repositories, context );
245             versionRangeRequest.setTrace( trace );
246 
247             final VersionRangeResult versionRangeResult =
248                 versionRangeResolver.resolveVersionRange( session, versionRangeRequest );
249 
250             if ( versionRangeResult.getHighestVersion() == null )
251             {
252                 throw new UnresolvableModelException(
253                     String.format( "No versions matched the requested dependency version range '%s'",
254                                    dependency.getVersion() ),
255                     dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion() );
256 
257             }
258 
259             if ( versionRangeResult.getVersionConstraint() != null
260                      && versionRangeResult.getVersionConstraint().getRange() != null
261                      && versionRangeResult.getVersionConstraint().getRange().getUpperBound() == null )
262             {
263                 // Message below is checked for in the MNG-4463 core IT.
264                 throw new UnresolvableModelException(
265                     String.format( "The requested dependency version range '%s' does not specify an upper bound",
266                                    dependency.getVersion() ),
267                     dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion() );
268 
269             }
270 
271             String newVersion = versionRangeResult.getHighestVersion().toString();
272             if ( !dependency.getVersion().equals( newVersion ) )
273             {
274                 modified.set( dependency.withVersion( newVersion ) );
275             }
276 
277             return resolveModel( dependency.getGroupId(), dependency.getArtifactId(), newVersion );
278         }
279         catch ( VersionRangeResolutionException e )
280         {
281             throw new UnresolvableModelException( e.getMessage(), dependency.getGroupId(), dependency.getArtifactId(),
282                                                   dependency.getVersion(), e );
283 
284         }
285     }
286 
287     @Override
288     public ModelSource resolveModel( org.apache.maven.model.Parent parent ) throws UnresolvableModelException
289     {
290         AtomicReference<org.apache.maven.api.model.Parent> resolvedParent = new AtomicReference<>();
291         ModelSource result = resolveModel( parent.getDelegate(), resolvedParent );
292         if ( resolvedParent.get() != null )
293         {
294             parent.setVersion( resolvedParent.get().getVersion() );
295         }
296         return result;
297     }
298 
299     @Override
300     public ModelSource resolveModel( org.apache.maven.model.Dependency dependency ) throws UnresolvableModelException
301     {
302         AtomicReference<org.apache.maven.api.model.Dependency> resolvedDependency = new AtomicReference<>();
303         ModelSource result = resolveModel( dependency.getDelegate(), resolvedDependency );
304         if ( resolvedDependency.get() != null )
305         {
306             dependency.setVersion( resolvedDependency.get().getVersion() );
307         }
308         return result;
309     }
310 
311     @Override
312     public void addRepository( org.apache.maven.model.Repository repository ) throws InvalidRepositoryException
313     {
314         addRepository( repository.getDelegate() );
315     }
316 
317     @Override
318     public void addRepository( org.apache.maven.model.Repository repository, boolean replace )
319             throws InvalidRepositoryException
320     {
321         addRepository( repository.getDelegate(), replace );
322     }
323 }