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