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.WorkspaceRepository;
056import org.eclipse.aether.resolution.ArtifactDescriptorException;
057import org.eclipse.aether.resolution.ArtifactDescriptorPolicy;
058import org.eclipse.aether.resolution.ArtifactDescriptorPolicyRequest;
059import org.eclipse.aether.resolution.ArtifactDescriptorRequest;
060import org.eclipse.aether.resolution.ArtifactDescriptorResult;
061import org.eclipse.aether.resolution.ArtifactRequest;
062import org.eclipse.aether.resolution.ArtifactResolutionException;
063import org.eclipse.aether.resolution.ArtifactResult;
064import org.eclipse.aether.resolution.VersionRequest;
065import org.eclipse.aether.resolution.VersionResolutionException;
066import org.eclipse.aether.resolution.VersionResult;
067import org.eclipse.aether.spi.locator.Service;
068import org.eclipse.aether.spi.locator.ServiceLocator;
069import org.eclipse.aether.spi.log.Logger;
070import org.eclipse.aether.spi.log.LoggerFactory;
071import org.eclipse.aether.spi.log.NullLoggerFactory;
072import org.eclipse.aether.transfer.ArtifactNotFoundException;
073
074/**
075 * @author Benjamin Bentmann
076 */
077@Named
078@Component( role = ArtifactDescriptorReader.class )
079public class DefaultArtifactDescriptorReader
080    implements ArtifactDescriptorReader, Service
081{
082
083    @SuppressWarnings( "unused" )
084    @Requirement( role = LoggerFactory.class )
085    private Logger logger = NullLoggerFactory.LOGGER;
086
087    @Requirement
088    private RemoteRepositoryManager remoteRepositoryManager;
089
090    @Requirement
091    private VersionResolver versionResolver;
092
093    @Requirement
094    private VersionRangeResolver versionRangeResolver;
095
096    @Requirement
097    private ArtifactResolver artifactResolver;
098
099    @Requirement
100    private RepositoryEventDispatcher repositoryEventDispatcher;
101
102    @Requirement
103    private ModelBuilder modelBuilder;
104
105    public DefaultArtifactDescriptorReader()
106    {
107        // enable no-arg constructor
108    }
109
110    @Inject
111    DefaultArtifactDescriptorReader( RemoteRepositoryManager remoteRepositoryManager, VersionResolver versionResolver,
112                                     ArtifactResolver artifactResolver, ModelBuilder modelBuilder,
113                                     RepositoryEventDispatcher repositoryEventDispatcher, LoggerFactory loggerFactory )
114    {
115        setRemoteRepositoryManager( remoteRepositoryManager );
116        setVersionResolver( versionResolver );
117        setArtifactResolver( artifactResolver );
118        setModelBuilder( modelBuilder );
119        setLoggerFactory( loggerFactory );
120        setRepositoryEventDispatcher( repositoryEventDispatcher );
121    }
122
123    public void initService( ServiceLocator locator )
124    {
125        setLoggerFactory( locator.getService( LoggerFactory.class ) );
126        setRemoteRepositoryManager( locator.getService( RemoteRepositoryManager.class ) );
127        setVersionResolver( locator.getService( VersionResolver.class ) );
128        setVersionRangeResolver( locator.getService( VersionRangeResolver.class ) );
129        setArtifactResolver( locator.getService( ArtifactResolver.class ) );
130        setRepositoryEventDispatcher( locator.getService( RepositoryEventDispatcher.class ) );
131        modelBuilder = locator.getService( ModelBuilder.class );
132        if ( modelBuilder == null )
133        {
134            setModelBuilder( new DefaultModelBuilderFactory().newInstance() );
135        }
136    }
137
138    public DefaultArtifactDescriptorReader setLoggerFactory( LoggerFactory loggerFactory )
139    {
140        this.logger = NullLoggerFactory.getSafeLogger( loggerFactory, getClass() );
141        return this;
142    }
143
144    void setLogger( LoggerFactory loggerFactory )
145    {
146        // plexus support
147        setLoggerFactory( loggerFactory );
148    }
149
150    public DefaultArtifactDescriptorReader setRemoteRepositoryManager( RemoteRepositoryManager remoteRepositoryManager )
151    {
152        if ( remoteRepositoryManager == null )
153        {
154            throw new IllegalArgumentException( "remote repository manager has not been specified" );
155        }
156        this.remoteRepositoryManager = remoteRepositoryManager;
157        return this;
158    }
159
160    public DefaultArtifactDescriptorReader setVersionResolver( VersionResolver versionResolver )
161    {
162        if ( versionResolver == null )
163        {
164            throw new IllegalArgumentException( "version resolver has not been specified" );
165        }
166        this.versionResolver = versionResolver;
167        return this;
168    }
169
170    /** @since 3.2.2 */
171    public DefaultArtifactDescriptorReader setVersionRangeResolver( VersionRangeResolver versionRangeResolver )
172    {
173        if ( versionRangeResolver == null )
174        {
175            throw new IllegalArgumentException( "version range resolver has not been specified" );
176        }
177        this.versionRangeResolver = versionRangeResolver;
178        return this;
179    }
180
181    public DefaultArtifactDescriptorReader setArtifactResolver( ArtifactResolver artifactResolver )
182    {
183        if ( artifactResolver == null )
184        {
185            throw new IllegalArgumentException( "artifact resolver has not been specified" );
186        }
187        this.artifactResolver = artifactResolver;
188        return this;
189    }
190
191    public DefaultArtifactDescriptorReader setRepositoryEventDispatcher( RepositoryEventDispatcher red )
192    {
193        if ( red == null )
194        {
195            throw new IllegalArgumentException( "repository event dispatcher has not been specified" );
196        }
197        this.repositoryEventDispatcher = red;
198        return this;
199    }
200
201    public DefaultArtifactDescriptorReader setModelBuilder( ModelBuilder modelBuilder )
202    {
203        if ( modelBuilder == null )
204        {
205            throw new IllegalArgumentException( "model builder has not been specified" );
206        }
207        this.modelBuilder = modelBuilder;
208        return this;
209    }
210
211    public ArtifactDescriptorResult readArtifactDescriptor( RepositorySystemSession session,
212                                                            ArtifactDescriptorRequest request )
213        throws ArtifactDescriptorException
214    {
215        ArtifactDescriptorResult result = new ArtifactDescriptorResult( request );
216
217        Model model = loadPom( session, request, result );
218
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            try
307            {
308                ModelBuildingRequest modelRequest = new DefaultModelBuildingRequest();
309                modelRequest.setValidationLevel( ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL );
310                modelRequest.setProcessPlugins( false );
311                modelRequest.setTwoPhaseBuilding( false );
312                modelRequest.setSystemProperties( toProperties( session.getUserProperties(),
313                                                                session.getSystemProperties() ) );
314                modelRequest.setModelCache( DefaultModelCache.newInstance( session ) );
315                modelRequest.setModelResolver( new DefaultModelResolver( session, trace.newChild( modelRequest ),
316                                                                         request.getRequestContext(), artifactResolver,
317                                                                         versionRangeResolver, remoteRepositoryManager,
318                                                                         request.getRepositories() ) );
319                if ( resolveResult.getRepository() instanceof WorkspaceRepository )
320                {
321                    modelRequest.setPomFile( pomArtifact.getFile() );
322                }
323                else
324                {
325                    modelRequest.setModelSource( new FileModelSource( pomArtifact.getFile() ) );
326                }
327
328                model = modelBuilder.build( modelRequest ).getEffectiveModel();
329            }
330            catch ( ModelBuildingException e )
331            {
332                for ( ModelProblem problem : e.getProblems() )
333                {
334                    if ( problem.getException() instanceof UnresolvableModelException )
335                    {
336                        result.addException( problem.getException() );
337                        throw new ArtifactDescriptorException( result );
338                    }
339                }
340                invalidDescriptor( session, trace, a, e );
341                if ( ( getPolicy( session, a, request ) & ArtifactDescriptorPolicy.IGNORE_INVALID ) != 0 )
342                {
343                    return null;
344                }
345                result.addException( e );
346                throw new ArtifactDescriptorException( result );
347            }
348
349            Relocation relocation = getRelocation( model );
350
351            if ( relocation != null )
352            {
353                result.addRelocation( a );
354                a =
355                    new RelocatedArtifact( a, relocation.getGroupId(), relocation.getArtifactId(),
356                                           relocation.getVersion() );
357                result.setArtifact( a );
358            }
359            else
360            {
361                return model;
362            }
363        }
364    }
365
366    private Properties toProperties( Map<String, String> dominant, Map<String, String> recessive )
367    {
368        Properties props = new Properties();
369        if ( recessive != null )
370        {
371            props.putAll( recessive );
372        }
373        if ( dominant != null )
374        {
375            props.putAll( dominant );
376        }
377        return props;
378    }
379
380    private Relocation getRelocation( Model model )
381    {
382        Relocation relocation = null;
383        DistributionManagement distMngt = model.getDistributionManagement();
384        if ( distMngt != null )
385        {
386            relocation = distMngt.getRelocation();
387        }
388        return relocation;
389    }
390
391    private void missingDescriptor( RepositorySystemSession session, RequestTrace trace, Artifact artifact,
392                                    Exception exception )
393    {
394        RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.ARTIFACT_DESCRIPTOR_MISSING );
395        event.setTrace( trace );
396        event.setArtifact( artifact );
397        event.setException( exception );
398
399        repositoryEventDispatcher.dispatch( event.build() );
400    }
401
402    private void invalidDescriptor( RepositorySystemSession session, RequestTrace trace, Artifact artifact,
403                                    Exception exception )
404    {
405        RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.ARTIFACT_DESCRIPTOR_INVALID );
406        event.setTrace( trace );
407        event.setArtifact( artifact );
408        event.setException( exception );
409
410        repositoryEventDispatcher.dispatch( event.build() );
411    }
412
413    private int getPolicy( RepositorySystemSession session, Artifact a, ArtifactDescriptorRequest request )
414    {
415        ArtifactDescriptorPolicy policy = session.getArtifactDescriptorPolicy();
416        if ( policy == null )
417        {
418            return ArtifactDescriptorPolicy.STRICT;
419        }
420        return policy.getPolicy( session, new ArtifactDescriptorPolicyRequest( a, request.getRequestContext() ) );
421    }
422
423}