001package org.apache.maven.repository.internal;
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.util.LinkedHashSet;
023import java.util.Map;
024import java.util.Properties;
025import java.util.Set;
026
027import javax.inject.Inject;
028import javax.inject.Named;
029
030import org.apache.commons.lang3.Validate;
031import org.apache.maven.model.DistributionManagement;
032import org.apache.maven.model.Model;
033import org.apache.maven.model.Relocation;
034import org.apache.maven.model.building.DefaultModelBuilderFactory;
035import org.apache.maven.model.building.DefaultModelBuildingRequest;
036import org.apache.maven.model.building.FileModelSource;
037import org.apache.maven.model.building.ModelBuilder;
038import org.apache.maven.model.building.ModelBuildingException;
039import org.apache.maven.model.building.ModelBuildingRequest;
040import org.apache.maven.model.building.ModelProblem;
041import org.apache.maven.model.resolution.UnresolvableModelException;
042import org.codehaus.plexus.component.annotations.Component;
043import org.codehaus.plexus.component.annotations.Requirement;
044import org.eclipse.aether.RepositoryEvent;
045import org.eclipse.aether.RepositoryEvent.EventType;
046import org.eclipse.aether.RepositoryException;
047import org.eclipse.aether.RepositorySystemSession;
048import org.eclipse.aether.RequestTrace;
049import org.eclipse.aether.artifact.Artifact;
050import org.eclipse.aether.impl.ArtifactDescriptorReader;
051import org.eclipse.aether.impl.ArtifactResolver;
052import org.eclipse.aether.impl.RemoteRepositoryManager;
053import org.eclipse.aether.impl.RepositoryEventDispatcher;
054import org.eclipse.aether.impl.VersionRangeResolver;
055import org.eclipse.aether.impl.VersionResolver;
056import org.eclipse.aether.repository.WorkspaceReader;
057import org.eclipse.aether.repository.WorkspaceRepository;
058import org.eclipse.aether.resolution.ArtifactDescriptorException;
059import org.eclipse.aether.resolution.ArtifactDescriptorPolicy;
060import org.eclipse.aether.resolution.ArtifactDescriptorPolicyRequest;
061import org.eclipse.aether.resolution.ArtifactDescriptorRequest;
062import org.eclipse.aether.resolution.ArtifactDescriptorResult;
063import org.eclipse.aether.resolution.ArtifactRequest;
064import org.eclipse.aether.resolution.ArtifactResolutionException;
065import org.eclipse.aether.resolution.ArtifactResult;
066import org.eclipse.aether.resolution.VersionRequest;
067import org.eclipse.aether.resolution.VersionResolutionException;
068import org.eclipse.aether.resolution.VersionResult;
069import org.eclipse.aether.spi.locator.Service;
070import org.eclipse.aether.spi.locator.ServiceLocator;
071import org.eclipse.aether.spi.log.Logger;
072import org.eclipse.aether.spi.log.LoggerFactory;
073import org.eclipse.aether.spi.log.NullLoggerFactory;
074import org.eclipse.aether.transfer.ArtifactNotFoundException;
075
076/**
077 * @author Benjamin Bentmann
078 */
079@Named
080@Component( role = ArtifactDescriptorReader.class )
081public class DefaultArtifactDescriptorReader
082    implements ArtifactDescriptorReader, Service
083{
084
085    @SuppressWarnings( "unused" )
086    @Requirement( role = LoggerFactory.class )
087    private Logger logger = NullLoggerFactory.LOGGER;
088
089    @Requirement
090    private RemoteRepositoryManager remoteRepositoryManager;
091
092    @Requirement
093    private VersionResolver versionResolver;
094
095    @Requirement
096    private VersionRangeResolver versionRangeResolver;
097
098    @Requirement
099    private ArtifactResolver artifactResolver;
100
101    @Requirement
102    private RepositoryEventDispatcher repositoryEventDispatcher;
103
104    @Requirement
105    private ModelBuilder modelBuilder;
106
107    public DefaultArtifactDescriptorReader()
108    {
109        // enable no-arg constructor
110    }
111
112    @Inject
113    DefaultArtifactDescriptorReader( RemoteRepositoryManager remoteRepositoryManager, VersionResolver versionResolver,
114                                     ArtifactResolver artifactResolver, ModelBuilder modelBuilder,
115                                     RepositoryEventDispatcher repositoryEventDispatcher, LoggerFactory loggerFactory )
116    {
117        setRemoteRepositoryManager( remoteRepositoryManager );
118        setVersionResolver( versionResolver );
119        setArtifactResolver( artifactResolver );
120        setModelBuilder( modelBuilder );
121        setLoggerFactory( loggerFactory );
122        setRepositoryEventDispatcher( repositoryEventDispatcher );
123    }
124
125    public void initService( ServiceLocator locator )
126    {
127        setLoggerFactory( locator.getService( LoggerFactory.class ) );
128        setRemoteRepositoryManager( locator.getService( RemoteRepositoryManager.class ) );
129        setVersionResolver( locator.getService( VersionResolver.class ) );
130        setVersionRangeResolver( locator.getService( VersionRangeResolver.class ) );
131        setArtifactResolver( locator.getService( ArtifactResolver.class ) );
132        setRepositoryEventDispatcher( locator.getService( RepositoryEventDispatcher.class ) );
133        modelBuilder = locator.getService( ModelBuilder.class );
134        if ( modelBuilder == null )
135        {
136            setModelBuilder( new DefaultModelBuilderFactory().newInstance() );
137        }
138    }
139
140    public DefaultArtifactDescriptorReader setLoggerFactory( LoggerFactory loggerFactory )
141    {
142        this.logger = NullLoggerFactory.getSafeLogger( loggerFactory, getClass() );
143        return this;
144    }
145
146    void setLogger( LoggerFactory loggerFactory )
147    {
148        // plexus support
149        setLoggerFactory( loggerFactory );
150    }
151
152    public DefaultArtifactDescriptorReader setRemoteRepositoryManager( RemoteRepositoryManager remoteRepositoryManager )
153    {
154        this.remoteRepositoryManager = Validate.notNull( remoteRepositoryManager,
155            "remoteRepositoryManager cannot be null" );
156        return this;
157    }
158
159    public DefaultArtifactDescriptorReader setVersionResolver( VersionResolver versionResolver )
160    {
161        this.versionResolver = Validate.notNull( versionResolver, "versionResolver cannot be null" );
162        return this;
163    }
164
165    /** @since 3.2.2 */
166    public DefaultArtifactDescriptorReader setVersionRangeResolver( VersionRangeResolver versionRangeResolver )
167    {
168        this.versionRangeResolver = Validate.notNull( versionRangeResolver, "versionRangeResolver cannot be null" );
169        return this;
170    }
171
172    public DefaultArtifactDescriptorReader setArtifactResolver( ArtifactResolver artifactResolver )
173    {
174        this.artifactResolver = Validate.notNull( artifactResolver, "artifactResolver cannot be null" );
175        return this;
176    }
177
178    public DefaultArtifactDescriptorReader setRepositoryEventDispatcher(
179        RepositoryEventDispatcher repositoryEventDispatcher )
180    {
181        this.repositoryEventDispatcher = Validate.notNull( repositoryEventDispatcher,
182            "repositoryEventDispatcher cannot be null" );
183        return this;
184    }
185
186    public DefaultArtifactDescriptorReader setModelBuilder( ModelBuilder modelBuilder )
187    {
188        this.modelBuilder = Validate.notNull( modelBuilder, "modelBuilder cannot be null" );
189        return this;
190    }
191
192    public ArtifactDescriptorResult readArtifactDescriptor( RepositorySystemSession session,
193                                                            ArtifactDescriptorRequest request )
194        throws ArtifactDescriptorException
195    {
196        ArtifactDescriptorResult result = new ArtifactDescriptorResult( request );
197
198        Model model = loadPom( session, request, result );
199        if ( model != null )
200        {
201            Map<String, Object> config = session.getConfigProperties();
202            ArtifactDescriptorReaderDelegate delegate =
203                (ArtifactDescriptorReaderDelegate) config.get( ArtifactDescriptorReaderDelegate.class.getName() );
204
205            if ( delegate == null )
206            {
207                delegate = new ArtifactDescriptorReaderDelegate();
208            }
209
210            delegate.populateResult( session, result, model );
211        }
212
213        return result;
214    }
215
216    private Model loadPom( RepositorySystemSession session, ArtifactDescriptorRequest request,
217                           ArtifactDescriptorResult result )
218        throws ArtifactDescriptorException
219    {
220        RequestTrace trace = RequestTrace.newChild( request.getTrace(), request );
221
222        Set<String> visited = new LinkedHashSet<>();
223        for ( Artifact a = request.getArtifact();; )
224        {
225            Artifact pomArtifact = ArtifactDescriptorUtils.toPomArtifact( a );
226            try
227            {
228                VersionRequest versionRequest =
229                    new VersionRequest( a, request.getRepositories(), request.getRequestContext() );
230                versionRequest.setTrace( trace );
231                VersionResult versionResult = versionResolver.resolveVersion( session, versionRequest );
232
233                a = a.setVersion( versionResult.getVersion() );
234
235                versionRequest =
236                    new VersionRequest( pomArtifact, request.getRepositories(), request.getRequestContext() );
237                versionRequest.setTrace( trace );
238                versionResult = versionResolver.resolveVersion( session, versionRequest );
239
240                pomArtifact = pomArtifact.setVersion( versionResult.getVersion() );
241            }
242            catch ( VersionResolutionException e )
243            {
244                result.addException( e );
245                throw new ArtifactDescriptorException( result );
246            }
247
248            if ( !visited.add( a.getGroupId() + ':' + a.getArtifactId() + ':' + a.getBaseVersion() ) )
249            {
250                RepositoryException exception =
251                    new RepositoryException( "Artifact relocations form a cycle: " + visited );
252                invalidDescriptor( session, trace, a, exception );
253                if ( ( getPolicy( session, a, request ) & ArtifactDescriptorPolicy.IGNORE_INVALID ) != 0 )
254                {
255                    return null;
256                }
257                result.addException( exception );
258                throw new ArtifactDescriptorException( result );
259            }
260
261            ArtifactResult resolveResult;
262            try
263            {
264                ArtifactRequest resolveRequest =
265                    new ArtifactRequest( pomArtifact, request.getRepositories(), request.getRequestContext() );
266                resolveRequest.setTrace( trace );
267                resolveResult = artifactResolver.resolveArtifact( session, resolveRequest );
268                pomArtifact = resolveResult.getArtifact();
269                result.setRepository( resolveResult.getRepository() );
270            }
271            catch ( ArtifactResolutionException e )
272            {
273                if ( e.getCause() instanceof ArtifactNotFoundException )
274                {
275                    missingDescriptor( session, trace, a, (Exception) e.getCause() );
276                    if ( ( getPolicy( session, a, request ) & ArtifactDescriptorPolicy.IGNORE_MISSING ) != 0 )
277                    {
278                        return null;
279                    }
280                }
281                result.addException( e );
282                throw new ArtifactDescriptorException( result );
283            }
284
285            Model model;
286
287            // hack: don't rebuild model if it was already loaded during reactor resolution
288            final WorkspaceReader workspace = session.getWorkspaceReader();
289            if ( workspace instanceof MavenWorkspaceReader )
290            {
291                model = ( (MavenWorkspaceReader) workspace ).findModel( pomArtifact );
292                if ( model != null )
293                {
294                    return model;
295                }
296            }
297
298            try
299            {
300                ModelBuildingRequest modelRequest = new DefaultModelBuildingRequest();
301                modelRequest.setValidationLevel( ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL );
302                modelRequest.setProcessPlugins( false );
303                modelRequest.setTwoPhaseBuilding( false );
304                modelRequest.setSystemProperties( toProperties( session.getUserProperties(),
305                                                                session.getSystemProperties() ) );
306                modelRequest.setModelCache( DefaultModelCache.newInstance( session ) );
307                modelRequest.setModelResolver( new DefaultModelResolver( session, trace.newChild( modelRequest ),
308                                                                         request.getRequestContext(), artifactResolver,
309                                                                         versionRangeResolver, remoteRepositoryManager,
310                                                                         request.getRepositories() ) );
311                if ( resolveResult.getRepository() instanceof WorkspaceRepository )
312                {
313                    modelRequest.setPomFile( pomArtifact.getFile() );
314                }
315                else
316                {
317                    modelRequest.setModelSource( new FileModelSource( pomArtifact.getFile() ) );
318                }
319
320                model = modelBuilder.build( modelRequest ).getEffectiveModel();
321            }
322            catch ( ModelBuildingException e )
323            {
324                for ( ModelProblem problem : e.getProblems() )
325                {
326                    if ( problem.getException() instanceof UnresolvableModelException )
327                    {
328                        result.addException( problem.getException() );
329                        throw new ArtifactDescriptorException( result );
330                    }
331                }
332                invalidDescriptor( session, trace, a, e );
333                if ( ( getPolicy( session, a, request ) & ArtifactDescriptorPolicy.IGNORE_INVALID ) != 0 )
334                {
335                    return null;
336                }
337                result.addException( e );
338                throw new ArtifactDescriptorException( result );
339            }
340
341            Relocation relocation = getRelocation( model );
342
343            if ( relocation != null )
344            {
345                result.addRelocation( a );
346                a =
347                    new RelocatedArtifact( a, relocation.getGroupId(), relocation.getArtifactId(),
348                                           relocation.getVersion() );
349                result.setArtifact( a );
350            }
351            else
352            {
353                return model;
354            }
355        }
356    }
357
358    private Properties toProperties( Map<String, String> dominant, Map<String, String> recessive )
359    {
360        Properties props = new Properties();
361        if ( recessive != null )
362        {
363            props.putAll( recessive );
364        }
365        if ( dominant != null )
366        {
367            props.putAll( dominant );
368        }
369        return props;
370    }
371
372    private Relocation getRelocation( Model model )
373    {
374        Relocation relocation = null;
375        DistributionManagement distMngt = model.getDistributionManagement();
376        if ( distMngt != null )
377        {
378            relocation = distMngt.getRelocation();
379        }
380        return relocation;
381    }
382
383    private void missingDescriptor( RepositorySystemSession session, RequestTrace trace, Artifact artifact,
384                                    Exception exception )
385    {
386        RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.ARTIFACT_DESCRIPTOR_MISSING );
387        event.setTrace( trace );
388        event.setArtifact( artifact );
389        event.setException( exception );
390
391        repositoryEventDispatcher.dispatch( event.build() );
392    }
393
394    private void invalidDescriptor( RepositorySystemSession session, RequestTrace trace, Artifact artifact,
395                                    Exception exception )
396    {
397        RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.ARTIFACT_DESCRIPTOR_INVALID );
398        event.setTrace( trace );
399        event.setArtifact( artifact );
400        event.setException( exception );
401
402        repositoryEventDispatcher.dispatch( event.build() );
403    }
404
405    private int getPolicy( RepositorySystemSession session, Artifact a, ArtifactDescriptorRequest request )
406    {
407        ArtifactDescriptorPolicy policy = session.getArtifactDescriptorPolicy();
408        if ( policy == null )
409        {
410            return ArtifactDescriptorPolicy.STRICT;
411        }
412        return policy.getPolicy( session, new ArtifactDescriptorPolicyRequest( a, request.getRequestContext() ) );
413    }
414
415}