001package org.apache.maven.project;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *   http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.io.File;
023import java.util.ArrayList;
024import java.util.Collections;
025import java.util.HashSet;
026import java.util.List;
027import java.util.Set;
028
029import com.google.common.base.Predicate;
030import com.google.common.collect.Iterables;
031
032import org.apache.maven.model.Parent;
033import org.apache.maven.model.Repository;
034import org.apache.maven.model.building.FileModelSource;
035import org.apache.maven.model.building.ModelSource;
036import org.apache.maven.model.resolution.InvalidRepositoryException;
037import org.apache.maven.model.resolution.ModelResolver;
038import org.apache.maven.model.resolution.UnresolvableModelException;
039import org.apache.maven.repository.internal.ArtifactDescriptorUtils;
040import org.eclipse.aether.RepositorySystem;
041import org.eclipse.aether.RepositorySystemSession;
042import org.eclipse.aether.RequestTrace;
043import org.eclipse.aether.artifact.Artifact;
044import org.eclipse.aether.artifact.DefaultArtifact;
045import org.eclipse.aether.impl.RemoteRepositoryManager;
046import org.eclipse.aether.repository.RemoteRepository;
047import org.eclipse.aether.resolution.ArtifactRequest;
048import org.eclipse.aether.resolution.ArtifactResolutionException;
049import org.eclipse.aether.resolution.VersionRangeRequest;
050import org.eclipse.aether.resolution.VersionRangeResolutionException;
051import org.eclipse.aether.resolution.VersionRangeResult;
052
053/**
054 * A model resolver to assist building of projects. This resolver gives priority to those repositories that have been
055 * declared in the POM.
056 *
057 * @author Benjamin Bentmann
058 */
059public class ProjectModelResolver
060    implements ModelResolver
061{
062
063    private final RepositorySystemSession session;
064
065    private final RequestTrace trace;
066
067    private final String context = "project";
068
069    private List<RemoteRepository> repositories;
070
071    private List<RemoteRepository> pomRepositories;
072
073    private final List<RemoteRepository> externalRepositories;
074
075    private final RepositorySystem resolver;
076
077    private final RemoteRepositoryManager remoteRepositoryManager;
078
079    private final Set<String> repositoryIds;
080
081    private final ReactorModelPool modelPool;
082
083    private final ProjectBuildingRequest.RepositoryMerging repositoryMerging;
084
085    public ProjectModelResolver( RepositorySystemSession session, RequestTrace trace, RepositorySystem resolver,
086                                 RemoteRepositoryManager remoteRepositoryManager, List<RemoteRepository> repositories,
087                                 ProjectBuildingRequest.RepositoryMerging repositoryMerging,
088                                 ReactorModelPool modelPool )
089    {
090        this.session = session;
091        this.trace = trace;
092        this.resolver = resolver;
093        this.remoteRepositoryManager = remoteRepositoryManager;
094        this.pomRepositories = new ArrayList<>();
095        List<RemoteRepository> externalRepositories = new ArrayList<>();
096        externalRepositories.addAll( repositories );
097        this.externalRepositories = Collections.unmodifiableList( externalRepositories );
098        this.repositories = new ArrayList<>();
099        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( repository ) );
143
144        if ( ProjectBuildingRequest.RepositoryMerging.REQUEST_DOMINANT.equals( repositoryMerging ) )
145        {
146            repositories = remoteRepositoryManager.aggregateRepositories( session, repositories, newRepositories,
147                                                                          true );
148        }
149        else
150        {
151            pomRepositories =
152                remoteRepositoryManager.aggregateRepositories( session, pomRepositories, newRepositories, true );
153            repositories =
154                remoteRepositoryManager.aggregateRepositories( session, pomRepositories, externalRepositories, false );
155        }
156    }
157
158    private static void removeMatchingRepository( Iterable<RemoteRepository> repositories, final String id )
159    {
160        Iterables.removeIf( repositories, new Predicate<RemoteRepository>()
161        {
162            @Override
163            public boolean apply( RemoteRepository remoteRepository )
164            {
165                return remoteRepository.getId().equals( id );
166            }
167        } );
168    }
169
170    public ModelResolver newCopy()
171    {
172        return new ProjectModelResolver( this );
173    }
174
175    public ModelSource resolveModel( String groupId, String artifactId, String version )
176        throws UnresolvableModelException
177    {
178        File pomFile = null;
179
180        if ( modelPool != null )
181        {
182            pomFile = modelPool.get( groupId, artifactId, version );
183        }
184
185        if ( pomFile == null )
186        {
187            Artifact pomArtifact = new DefaultArtifact( groupId, artifactId, "", "pom", version );
188
189            try
190            {
191                ArtifactRequest request = new ArtifactRequest( pomArtifact, repositories, context );
192                request.setTrace( trace );
193                pomArtifact = resolver.resolveArtifact( session, request ).getArtifact();
194            }
195            catch ( ArtifactResolutionException e )
196            {
197                throw new UnresolvableModelException( e.getMessage(), groupId, artifactId, version, e );
198            }
199
200            pomFile = pomArtifact.getFile();
201        }
202
203        return new FileModelSource( pomFile );
204    }
205
206    public ModelSource resolveModel( Parent parent )
207        throws UnresolvableModelException
208    {
209        Artifact artifact = new DefaultArtifact( parent.getGroupId(), parent.getArtifactId(), "", "pom",
210                                                 parent.getVersion() );
211
212        VersionRangeRequest versionRangeRequest = new VersionRangeRequest( artifact, repositories, context );
213        versionRangeRequest.setTrace( trace );
214
215        try
216        {
217            VersionRangeResult versionRangeResult = resolver.resolveVersionRange( session, versionRangeRequest );
218
219            if ( versionRangeResult.getHighestVersion() == null )
220            {
221                throw new UnresolvableModelException( "No versions matched the requested range '" + parent.getVersion()
222                                                          + "'", parent.getGroupId(), parent.getArtifactId(),
223                                                      parent.getVersion() );
224
225            }
226
227            if ( versionRangeResult.getVersionConstraint() != null
228                     && versionRangeResult.getVersionConstraint().getRange() != null
229                     && versionRangeResult.getVersionConstraint().getRange().getUpperBound() == null )
230            {
231                throw new UnresolvableModelException( "The requested version range '" + parent.getVersion()
232                                                          + "' does not specify an upper bound", parent.getGroupId(),
233                                                      parent.getArtifactId(), parent.getVersion() );
234
235            }
236
237            parent.setVersion( versionRangeResult.getHighestVersion().toString() );
238        }
239        catch ( VersionRangeResolutionException e )
240        {
241            throw new UnresolvableModelException( e.getMessage(), parent.getGroupId(), parent.getArtifactId(),
242                                                  parent.getVersion(), e );
243
244        }
245
246        return resolveModel( parent.getGroupId(), parent.getArtifactId(), parent.getVersion() );
247    }
248}