001    package 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    
022    import java.util.ArrayList;
023    import java.util.Collections;
024    import java.util.HashMap;
025    import java.util.LinkedHashMap;
026    import java.util.LinkedHashSet;
027    import java.util.List;
028    import java.util.Map;
029    import java.util.Properties;
030    import java.util.Set;
031    
032    import javax.inject.Inject;
033    import javax.inject.Named;
034    
035    import org.apache.maven.model.DependencyManagement;
036    import org.apache.maven.model.DistributionManagement;
037    import org.apache.maven.model.License;
038    import org.apache.maven.model.Model;
039    import org.apache.maven.model.Prerequisites;
040    import org.apache.maven.model.Relocation;
041    import org.apache.maven.model.Repository;
042    import org.apache.maven.model.building.DefaultModelBuilderFactory;
043    import org.apache.maven.model.building.DefaultModelBuildingRequest;
044    import org.apache.maven.model.building.FileModelSource;
045    import org.apache.maven.model.building.ModelBuilder;
046    import org.apache.maven.model.building.ModelBuildingException;
047    import org.apache.maven.model.building.ModelBuildingRequest;
048    import org.apache.maven.model.building.ModelProblem;
049    import org.apache.maven.model.resolution.UnresolvableModelException;
050    import org.codehaus.plexus.component.annotations.Component;
051    import org.codehaus.plexus.component.annotations.Requirement;
052    import org.eclipse.aether.RepositoryException;
053    import org.eclipse.aether.RepositoryEvent.EventType;
054    import org.eclipse.aether.RepositoryEvent;
055    import org.eclipse.aether.RepositorySystemSession;
056    import org.eclipse.aether.RequestTrace;
057    import org.eclipse.aether.artifact.Artifact;
058    import org.eclipse.aether.artifact.ArtifactProperties;
059    import org.eclipse.aether.artifact.ArtifactType;
060    import org.eclipse.aether.artifact.ArtifactTypeRegistry;
061    import org.eclipse.aether.artifact.DefaultArtifact;
062    import org.eclipse.aether.artifact.DefaultArtifactType;
063    import org.eclipse.aether.graph.Dependency;
064    import org.eclipse.aether.graph.Exclusion;
065    import org.eclipse.aether.impl.ArtifactDescriptorReader;
066    import org.eclipse.aether.impl.ArtifactResolver;
067    import org.eclipse.aether.impl.RemoteRepositoryManager;
068    import org.eclipse.aether.impl.RepositoryEventDispatcher;
069    import org.eclipse.aether.impl.VersionResolver;
070    import org.eclipse.aether.repository.WorkspaceRepository;
071    import org.eclipse.aether.resolution.ArtifactDescriptorException;
072    import org.eclipse.aether.resolution.ArtifactDescriptorPolicy;
073    import org.eclipse.aether.resolution.ArtifactDescriptorPolicyRequest;
074    import org.eclipse.aether.resolution.ArtifactDescriptorRequest;
075    import org.eclipse.aether.resolution.ArtifactDescriptorResult;
076    import org.eclipse.aether.resolution.ArtifactRequest;
077    import org.eclipse.aether.resolution.ArtifactResolutionException;
078    import org.eclipse.aether.resolution.ArtifactResult;
079    import org.eclipse.aether.resolution.VersionRequest;
080    import org.eclipse.aether.resolution.VersionResolutionException;
081    import org.eclipse.aether.resolution.VersionResult;
082    import org.eclipse.aether.spi.locator.Service;
083    import org.eclipse.aether.spi.locator.ServiceLocator;
084    import org.eclipse.aether.spi.log.Logger;
085    import org.eclipse.aether.spi.log.LoggerFactory;
086    import org.eclipse.aether.spi.log.NullLoggerFactory;
087    import org.eclipse.aether.transfer.ArtifactNotFoundException;
088    
089    /**
090     * @author Benjamin Bentmann
091     */
092    @Named
093    @Component( role = ArtifactDescriptorReader.class )
094    public class DefaultArtifactDescriptorReader
095        implements ArtifactDescriptorReader, Service
096    {
097    
098        @SuppressWarnings( "unused" )
099        @Requirement( role = LoggerFactory.class )
100        private Logger logger = NullLoggerFactory.LOGGER;
101    
102        @Requirement
103        private RemoteRepositoryManager remoteRepositoryManager;
104    
105        @Requirement
106        private VersionResolver versionResolver;
107    
108        @Requirement
109        private ArtifactResolver artifactResolver;
110    
111        @Requirement
112        private RepositoryEventDispatcher repositoryEventDispatcher;
113    
114        @Requirement
115        private ModelBuilder modelBuilder;
116    
117        public DefaultArtifactDescriptorReader()
118        {
119            // enable no-arg constructor
120        }
121    
122        @Inject
123        DefaultArtifactDescriptorReader( RemoteRepositoryManager remoteRepositoryManager, VersionResolver versionResolver,
124                                         ArtifactResolver artifactResolver, ModelBuilder modelBuilder,
125                                         RepositoryEventDispatcher repositoryEventDispatcher, LoggerFactory loggerFactory )
126        {
127            setRemoteRepositoryManager( remoteRepositoryManager );
128            setVersionResolver( versionResolver );
129            setArtifactResolver( artifactResolver );
130            setModelBuilder( modelBuilder );
131            setLoggerFactory( loggerFactory );
132            setRepositoryEventDispatcher( repositoryEventDispatcher );
133        }
134    
135        public void initService( ServiceLocator locator )
136        {
137            setLoggerFactory( locator.getService( LoggerFactory.class ) );
138            setRemoteRepositoryManager( locator.getService( RemoteRepositoryManager.class ) );
139            setVersionResolver( locator.getService( VersionResolver.class ) );
140            setArtifactResolver( locator.getService( ArtifactResolver.class ) );
141            setRepositoryEventDispatcher( locator.getService( RepositoryEventDispatcher.class ) );
142            modelBuilder = locator.getService( ModelBuilder.class );
143            if ( modelBuilder == null )
144            {
145                setModelBuilder( new DefaultModelBuilderFactory().newInstance() );
146            }
147        }
148    
149        public DefaultArtifactDescriptorReader setLoggerFactory( LoggerFactory loggerFactory )
150        {
151            this.logger = NullLoggerFactory.getSafeLogger( loggerFactory, getClass() );
152            return this;
153        }
154    
155        void setLogger( LoggerFactory loggerFactory )
156        {
157            // plexus support
158            setLoggerFactory( loggerFactory );
159        }
160    
161        public DefaultArtifactDescriptorReader setRemoteRepositoryManager( RemoteRepositoryManager remoteRepositoryManager )
162        {
163            if ( remoteRepositoryManager == null )
164            {
165                throw new IllegalArgumentException( "remote repository manager has not been specified" );
166            }
167            this.remoteRepositoryManager = remoteRepositoryManager;
168            return this;
169        }
170    
171        public DefaultArtifactDescriptorReader setVersionResolver( VersionResolver versionResolver )
172        {
173            if ( versionResolver == null )
174            {
175                throw new IllegalArgumentException( "version resolver has not been specified" );
176            }
177            this.versionResolver = versionResolver;
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 repositoryEventDispatcher )
192        {
193            if ( repositoryEventDispatcher == null )
194            {
195                throw new IllegalArgumentException( "repository event dispatcher has not been specified" );
196            }
197            this.repositoryEventDispatcher = repositoryEventDispatcher;
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                ArtifactTypeRegistry stereotypes = session.getArtifactTypeRegistry();
222    
223                for ( Repository r : model.getRepositories() )
224                {
225                    result.addRepository( ArtifactDescriptorUtils.toRemoteRepository( r ) );
226                }
227    
228                for ( org.apache.maven.model.Dependency dependency : model.getDependencies() )
229                {
230                    result.addDependency( convert( dependency, stereotypes ) );
231                }
232    
233                DependencyManagement mngt = model.getDependencyManagement();
234                if ( mngt != null )
235                {
236                    for ( org.apache.maven.model.Dependency dependency : mngt.getDependencies() )
237                    {
238                        result.addManagedDependency( convert( dependency, stereotypes ) );
239                    }
240                }
241    
242                Map<String, Object> properties = new LinkedHashMap<String, Object>();
243    
244                Prerequisites prerequisites = model.getPrerequisites();
245                if ( prerequisites != null )
246                {
247                    properties.put( "prerequisites.maven", prerequisites.getMaven() );
248                }
249    
250                List<License> licenses = model.getLicenses();
251                properties.put( "license.count", licenses.size() );
252                for ( int i = 0; i < licenses.size(); i++ )
253                {
254                    License license = licenses.get( i );
255                    properties.put( "license." + i + ".name", license.getName() );
256                    properties.put( "license." + i + ".url", license.getUrl() );
257                    properties.put( "license." + i + ".comments", license.getComments() );
258                    properties.put( "license." + i + ".distribution", license.getDistribution() );
259                }
260    
261                result.setProperties( properties );
262    
263                setArtifactProperties( result, model );
264            }
265    
266            return result;
267        }
268    
269        private Model loadPom( RepositorySystemSession session, ArtifactDescriptorRequest request,
270                               ArtifactDescriptorResult result )
271            throws ArtifactDescriptorException
272        {
273            RequestTrace trace = RequestTrace.newChild( request.getTrace(), request );
274    
275            Set<String> visited = new LinkedHashSet<String>();
276            for ( Artifact artifact = request.getArtifact();; )
277            {
278                try
279                {
280                    VersionRequest versionRequest =
281                        new VersionRequest( artifact, request.getRepositories(), request.getRequestContext() );
282                    versionRequest.setTrace( trace );
283                    VersionResult versionResult = versionResolver.resolveVersion( session, versionRequest );
284    
285                    artifact = artifact.setVersion( versionResult.getVersion() );
286                }
287                catch ( VersionResolutionException e )
288                {
289                    result.addException( e );
290                    throw new ArtifactDescriptorException( result );
291                }
292    
293                if ( !visited.add( artifact.getGroupId() + ':' + artifact.getArtifactId() + ':' + artifact.getBaseVersion() ) )
294                {
295                    RepositoryException exception =
296                        new RepositoryException( "Artifact relocations form a cycle: " + visited );
297                    invalidDescriptor( session, trace, artifact, exception );
298                    if ( ( getPolicy( session, artifact, request ) & ArtifactDescriptorPolicy.IGNORE_INVALID ) != 0 )
299                    {
300                        return null;
301                    }
302                    result.addException( exception );
303                    throw new ArtifactDescriptorException( result );
304                }
305    
306                Artifact pomArtifact = ArtifactDescriptorUtils.toPomArtifact( artifact );
307    
308                ArtifactResult resolveResult;
309                try
310                {
311                    ArtifactRequest resolveRequest =
312                        new ArtifactRequest( pomArtifact, request.getRepositories(), request.getRequestContext() );
313                    resolveRequest.setTrace( trace );
314                    resolveResult = artifactResolver.resolveArtifact( session, resolveRequest );
315                    pomArtifact = resolveResult.getArtifact();
316                    result.setRepository( resolveResult.getRepository() );
317                }
318                catch ( ArtifactResolutionException e )
319                {
320                    if ( e.getCause() instanceof ArtifactNotFoundException )
321                    {
322                        missingDescriptor( session, trace, artifact, (Exception) e.getCause() );
323                        if ( ( getPolicy( session, artifact, request ) & ArtifactDescriptorPolicy.IGNORE_MISSING ) != 0 )
324                        {
325                            return null;
326                        }
327                    }
328                    result.addException( e );
329                    throw new ArtifactDescriptorException( result );
330                }
331    
332                Model model;
333                try
334                {
335                    ModelBuildingRequest modelRequest = new DefaultModelBuildingRequest();
336                    modelRequest.setValidationLevel( ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL );
337                    modelRequest.setProcessPlugins( false );
338                    modelRequest.setTwoPhaseBuilding( false );
339                    modelRequest.setSystemProperties( toProperties( session.getUserProperties(),
340                                                                    session.getSystemProperties() ) );
341                    modelRequest.setModelCache( DefaultModelCache.newInstance( session ) );
342                    modelRequest.setModelResolver( new DefaultModelResolver( session, trace.newChild( modelRequest ),
343                                                                             request.getRequestContext(), artifactResolver,
344                                                                             remoteRepositoryManager,
345                                                                             request.getRepositories() ) );
346                    if ( resolveResult.getRepository() instanceof WorkspaceRepository )
347                    {
348                        modelRequest.setPomFile( pomArtifact.getFile() );
349                    }
350                    else
351                    {
352                        modelRequest.setModelSource( new FileModelSource( pomArtifact.getFile() ) );
353                    }
354    
355                    model = modelBuilder.build( modelRequest ).getEffectiveModel();
356                }
357                catch ( ModelBuildingException e )
358                {
359                    for ( ModelProblem problem : e.getProblems() )
360                    {
361                        if ( problem.getException() instanceof UnresolvableModelException )
362                        {
363                            result.addException( problem.getException() );
364                            throw new ArtifactDescriptorException( result );
365                        }
366                    }
367                    invalidDescriptor( session, trace, artifact, e );
368                    if ( ( getPolicy( session, artifact, request ) & ArtifactDescriptorPolicy.IGNORE_INVALID ) != 0 )
369                    {
370                        return null;
371                    }
372                    result.addException( e );
373                    throw new ArtifactDescriptorException( result );
374                }
375    
376                Relocation relocation = getRelocation( model );
377    
378                if ( relocation != null )
379                {
380                    result.addRelocation( artifact );
381                    artifact =
382                        new RelocatedArtifact( artifact, relocation.getGroupId(), relocation.getArtifactId(),
383                                               relocation.getVersion() );
384                    result.setArtifact( artifact );
385                }
386                else
387                {
388                    return model;
389                }
390            }
391        }
392    
393        private Properties toProperties( Map<String, String> dominant, Map<String, String> recessive )
394        {
395            Properties props = new Properties();
396            if ( recessive != null )
397            {
398                props.putAll( recessive );
399            }
400            if ( dominant != null )
401            {
402                props.putAll( dominant );
403            }
404            return props;
405        }
406    
407        private Relocation getRelocation( Model model )
408        {
409            Relocation relocation = null;
410            DistributionManagement distMngt = model.getDistributionManagement();
411            if ( distMngt != null )
412            {
413                relocation = distMngt.getRelocation();
414            }
415            return relocation;
416        }
417    
418        private void setArtifactProperties( ArtifactDescriptorResult result, Model model )
419        {
420            String downloadUrl = null;
421            DistributionManagement distMngt = model.getDistributionManagement();
422            if ( distMngt != null )
423            {
424                downloadUrl = distMngt.getDownloadUrl();
425            }
426            if ( downloadUrl != null && downloadUrl.length() > 0 )
427            {
428                Artifact artifact = result.getArtifact();
429                Map<String, String> props = new HashMap<String, String>( artifact.getProperties() );
430                props.put( ArtifactProperties.DOWNLOAD_URL, downloadUrl );
431                result.setArtifact( artifact.setProperties( props ) );
432            }
433        }
434    
435        private Dependency convert( org.apache.maven.model.Dependency dependency, ArtifactTypeRegistry stereotypes )
436        {
437            ArtifactType stereotype = stereotypes.get( dependency.getType() );
438            if ( stereotype == null )
439            {
440                stereotype = new DefaultArtifactType( dependency.getType() );
441            }
442    
443            boolean system = dependency.getSystemPath() != null && dependency.getSystemPath().length() > 0;
444    
445            Map<String, String> props = null;
446            if ( system )
447            {
448                props = Collections.singletonMap( ArtifactProperties.LOCAL_PATH, dependency.getSystemPath() );
449            }
450    
451            Artifact artifact =
452                new DefaultArtifact( dependency.getGroupId(), dependency.getArtifactId(), dependency.getClassifier(), null,
453                                     dependency.getVersion(), props, stereotype );
454    
455            List<Exclusion> exclusions = new ArrayList<Exclusion>( dependency.getExclusions().size() );
456            for ( org.apache.maven.model.Exclusion exclusion : dependency.getExclusions() )
457            {
458                exclusions.add( convert( exclusion ) );
459            }
460    
461            Dependency result = new Dependency( artifact, dependency.getScope(), dependency.isOptional(), exclusions );
462    
463            return result;
464        }
465    
466        private Exclusion convert( org.apache.maven.model.Exclusion exclusion )
467        {
468            return new Exclusion( exclusion.getGroupId(), exclusion.getArtifactId(), "*", "*" );
469        }
470    
471        private void missingDescriptor( RepositorySystemSession session, RequestTrace trace, Artifact artifact,
472                                        Exception exception )
473        {
474            RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.ARTIFACT_DESCRIPTOR_MISSING );
475            event.setTrace( trace );
476            event.setArtifact( artifact );
477            event.setException( exception );
478    
479            repositoryEventDispatcher.dispatch( event.build() );
480        }
481    
482        private void invalidDescriptor( RepositorySystemSession session, RequestTrace trace, Artifact artifact,
483                                        Exception exception )
484        {
485            RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.ARTIFACT_DESCRIPTOR_INVALID );
486            event.setTrace( trace );
487            event.setArtifact( artifact );
488            event.setException( exception );
489    
490            repositoryEventDispatcher.dispatch( event.build() );
491        }
492    
493        private int getPolicy( RepositorySystemSession session, Artifact artifact, ArtifactDescriptorRequest request )
494        {
495            ArtifactDescriptorPolicy policy = session.getArtifactDescriptorPolicy();
496            if ( policy == null )
497            {
498                return ArtifactDescriptorPolicy.STRICT;
499            }
500            return policy.getPolicy( session, new ArtifactDescriptorPolicyRequest( artifact, request.getRequestContext() ) );
501        }
502    
503    }