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