]> source.dussan.org Git - archiva.git/blob
3336ab1efa9b648702139c96d5aaf47687e2008b
[archiva.git] /
1 package org.apache.archiva.dependency.tree.maven2;
2
3 /*
4  * Licensed to the Apache Software Foundation (ASF) under one
5  * or more contributor license agreements.  See the NOTICE file
6  * distributed with this work for additional information
7  * regarding copyright ownership.  The ASF licenses this file
8  * to you under the Apache License, Version 2.0 (the
9  * "License"); you may not use this file except in compliance
10  * with the License.  You may obtain a copy of the License at
11  *
12  *  http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing,
15  * software distributed under the License is distributed on an
16  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17  * KIND, either express or implied.  See the License for the
18  * specific language governing permissions and limitations
19  * under the License.
20  */
21
22 import org.apache.archiva.common.plexusbridge.PlexusSisuBridge;
23 import org.apache.archiva.common.plexusbridge.PlexusSisuBridgeException;
24 import org.apache.archiva.metadata.repository.MetadataResolutionException;
25 import org.apache.archiva.metadata.repository.MetadataResolver;
26 import org.apache.archiva.metadata.repository.RepositorySession;
27 import org.apache.archiva.metadata.repository.RepositorySessionFactory;
28 import org.apache.archiva.metadata.repository.storage.RepositoryPathTranslator;
29 import org.apache.archiva.metadata.repository.storage.maven2.RepositoryModelResolver;
30 import org.apache.commons.lang.StringUtils;
31 import org.apache.maven.archiva.common.utils.Slf4JPlexusLogger;
32 import org.apache.maven.archiva.configuration.ArchivaConfiguration;
33 import org.apache.maven.archiva.configuration.ManagedRepositoryConfiguration;
34 import org.apache.maven.artifact.Artifact;
35 import org.apache.maven.artifact.factory.ArtifactFactory;
36 import org.apache.maven.artifact.metadata.ArtifactMetadataRetrievalException;
37 import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
38 import org.apache.maven.artifact.metadata.ResolutionGroup;
39 import org.apache.maven.artifact.repository.ArtifactRepository;
40 import org.apache.maven.artifact.resolver.ArtifactCollector;
41 import org.apache.maven.artifact.resolver.ArtifactResolutionException;
42 import org.apache.maven.artifact.resolver.ResolutionListener;
43 import org.apache.maven.artifact.resolver.filter.AndArtifactFilter;
44 import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
45 import org.apache.maven.artifact.resolver.filter.ExcludesArtifactFilter;
46 import org.apache.maven.artifact.versioning.ArtifactVersion;
47 import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
48 import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
49 import org.apache.maven.artifact.versioning.ManagedVersionMap;
50 import org.apache.maven.artifact.versioning.VersionRange;
51 import org.apache.maven.model.Dependency;
52 import org.apache.maven.model.DependencyManagement;
53 import org.apache.maven.model.Exclusion;
54 import org.apache.maven.model.Model;
55 import org.apache.maven.model.building.DefaultModelBuilderFactory;
56 import org.apache.maven.model.building.DefaultModelBuildingRequest;
57 import org.apache.maven.model.building.ModelBuilder;
58 import org.apache.maven.model.building.ModelBuildingException;
59 import org.apache.maven.model.building.ModelBuildingRequest;
60 import org.apache.maven.model.resolution.UnresolvableModelException;
61 import org.apache.maven.repository.legacy.metadata.MetadataResolutionRequest;
62 import org.apache.maven.shared.dependency.tree.DependencyNode;
63 import org.apache.maven.shared.dependency.tree.DependencyTreeBuilderException;
64 import org.apache.maven.shared.dependency.tree.DependencyTreeResolutionListener;
65 import org.apache.maven.shared.dependency.tree.filter.AncestorOrSelfDependencyNodeFilter;
66 import org.apache.maven.shared.dependency.tree.filter.DependencyNodeFilter;
67 import org.apache.maven.shared.dependency.tree.filter.StateDependencyNodeFilter;
68 import org.apache.maven.shared.dependency.tree.traversal.BuildingDependencyNodeVisitor;
69 import org.apache.maven.shared.dependency.tree.traversal.CollectingDependencyNodeVisitor;
70 import org.apache.maven.shared.dependency.tree.traversal.DependencyNodeVisitor;
71 import org.apache.maven.shared.dependency.tree.traversal.FilteringDependencyNodeVisitor;
72 import org.slf4j.Logger;
73 import org.slf4j.LoggerFactory;
74 import org.springframework.stereotype.Service;
75
76 import javax.annotation.PostConstruct;
77 import javax.inject.Inject;
78 import javax.inject.Named;
79 import java.io.File;
80 import java.util.ArrayList;
81 import java.util.Collection;
82 import java.util.Collections;
83 import java.util.HashSet;
84 import java.util.LinkedHashSet;
85 import java.util.List;
86 import java.util.Map;
87 import java.util.Set;
88
89 /**
90  * Default implementation of <code>DependencyTreeBuilder</code>. Customized wrapper for maven-dependency-tree to use
91  * maven-model-builder instead of maven-project. Note that the role must differ to avoid conflicting with the
92  * maven-shared implementation.
93  * <p/>
94  * plexus.component role="org.apache.archiva.dependency.tree.maven2.DependencyTreeBuilder" role-hint="maven2"
95  */
96 @Service( "dependencyTreeBuilder#maven2" )
97 public class DefaultDependencyTreeBuilder
98     implements DependencyTreeBuilder
99 {
100
101     private Logger log = LoggerFactory.getLogger( getClass() );
102
103     /**
104      * plexus.requirement
105      */
106     private ArtifactFactory factory;
107
108     /**
109      * plexus.requirement
110      */
111     private ArtifactCollector collector;
112
113     /**
114      * plexus.requirement
115      */
116     private ModelBuilder builder;
117
118     /**
119      * TODO: can have other types, and this might eventually come through from the main request
120      * <p/>
121      * plexus.requirement
122      */
123     @Inject
124     private RepositorySessionFactory repositorySessionFactory;
125
126     /**
127      * plexus.requirement role-hint="maven2"
128      */
129     @Inject
130     @Named( value = "repositoryPathTranslator#maven2" )
131     private RepositoryPathTranslator pathTranslator;
132
133     /**
134      * plexus.requirement
135      */
136     @Inject
137     @Named( value = "archivaConfiguration#default" )
138     private ArchivaConfiguration archivaConfiguration;
139
140     @Inject
141     private PlexusSisuBridge plexusSisuBridge;
142
143     @PostConstruct
144     public void initialize()
145         throws PlexusSisuBridgeException
146     {
147         factory = plexusSisuBridge.lookup( ArtifactFactory.class , "default" );
148         collector = plexusSisuBridge.lookup( ArtifactCollector.class , "default" );
149
150
151         DefaultModelBuilderFactory defaultModelBuilderFactory = new DefaultModelBuilderFactory();
152         builder = defaultModelBuilderFactory.newInstance();
153     }
154
155     public void buildDependencyTree( List<String> repositoryIds, String groupId, String artifactId, String version,
156                                      DependencyNodeVisitor nodeVisitor )
157         throws DependencyTreeBuilderException
158     {
159         DependencyTreeResolutionListener listener =
160             new DependencyTreeResolutionListener( new Slf4JPlexusLogger( getClass() ) );
161
162         Artifact projectArtifact = factory.createProjectArtifact( groupId, artifactId, version );
163         File basedir = findArtifactInRepositories( repositoryIds, projectArtifact );
164
165         if ( basedir == null )
166         {
167             // metadata could not be resolved
168             return;
169         }
170
171         try
172         {
173             Model model =
174                 buildProject( new RepositoryModelResolver( basedir, pathTranslator ), groupId, artifactId, version );
175
176             Map managedVersions = createManagedVersionMap( model );
177
178             Set<Artifact> dependencyArtifacts = createArtifacts( model, null );
179
180             RepositorySession repositorySession = repositorySessionFactory.createSession();
181             try
182             {
183                 ArtifactMetadataSource metadataSource =
184                     new MetadataArtifactMetadataSource( repositoryIds, repositorySession );
185
186                 // Note that we don't permit going to external repositories. We don't need to pass in a local and remote
187                 // since our metadata source has control over them
188                 //collector.collect( dependencyArtifacts, projectArtifact, managedVersions, null, null, metadataSource,
189                 //                   null, Collections.singletonList( listener ) );
190
191                 collector.collect( dependencyArtifacts, projectArtifact, null, Collections.<ArtifactRepository>emptyList(),
192                                    metadataSource, null,  Collections.singletonList( (ResolutionListener) listener ) );
193
194                 /*
195                 Set<Artifact> artifacts, Artifact originatingArtifact,
196                                       ArtifactRepository localRepository, List<ArtifactRepository> remoteRepositories,
197                                       ArtifactMetadataSource source, ArtifactFilter filter,
198                                       List< ResolutionListener > listeners
199                 */
200             }
201             finally
202             {
203                 repositorySession.close();
204             }
205
206             DependencyNode rootNode = listener.getRootNode();
207
208             // TODO: remove the need for this when the serializer can calculate last nodes from visitor calls only
209             DependencyNodeVisitor visitor = new BuildingDependencyNodeVisitor( nodeVisitor );
210
211             CollectingDependencyNodeVisitor collectingVisitor = new CollectingDependencyNodeVisitor();
212             DependencyNodeVisitor firstPassVisitor =
213                 new FilteringDependencyNodeVisitor( collectingVisitor, StateDependencyNodeFilter.INCLUDED );
214             rootNode.accept( firstPassVisitor );
215
216             DependencyNodeFilter secondPassFilter =
217                 new AncestorOrSelfDependencyNodeFilter( collectingVisitor.getNodes() );
218             visitor = new FilteringDependencyNodeVisitor( visitor, secondPassFilter );
219
220             rootNode.accept( visitor );
221         }
222         catch ( ArtifactResolutionException e )
223         {
224             throw new DependencyTreeBuilderException( "Cannot build project dependency tree " + e.getMessage(), e );
225         }
226         catch ( InvalidVersionSpecificationException e )
227         {
228             throw new DependencyTreeBuilderException( "Invalid dependency version for artifact " + projectArtifact );
229         }
230         catch ( ModelBuildingException e )
231         {
232             throw new DependencyTreeBuilderException( "Cannot build project dependency tree " + e.getMessage(), e );
233         }
234         catch ( UnresolvableModelException e )
235         {
236             throw new DependencyTreeBuilderException( "Cannot build project dependency tree " + e.getMessage(), e );
237         }
238     }
239
240     private File findArtifactInRepositories( List<String> repositoryIds, Artifact projectArtifact )
241     {
242         for ( String repoId : repositoryIds )
243         {
244             ManagedRepositoryConfiguration repositoryConfiguration =
245                 archivaConfiguration.getConfiguration().findManagedRepositoryById( repoId );
246
247             File repoDir = new File( repositoryConfiguration.getLocation() );
248             File file = pathTranslator.toFile( repoDir, projectArtifact.getGroupId(), projectArtifact.getArtifactId(),
249                                                projectArtifact.getBaseVersion(),
250                                                projectArtifact.getArtifactId() + "-" + projectArtifact.getVersion()
251                                                    + ".pom" );
252
253             if ( file.exists() )
254             {
255                 return repoDir;
256             }
257         }
258         return null;
259     }
260
261     private Model buildProject( RepositoryModelResolver modelResolver, String groupId, String artifactId,
262                                 String version )
263         throws ModelBuildingException, UnresolvableModelException
264     {
265         ModelBuildingRequest req = new DefaultModelBuildingRequest();
266         req.setProcessPlugins( false );
267         req.setModelSource( modelResolver.resolveModel( groupId, artifactId, version ) );
268         req.setModelResolver( modelResolver );
269         req.setValidationLevel( ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL );
270
271         return builder.build( req ).getEffectiveModel();
272     }
273
274     // from maven-project to avoid the dependency on it
275     private Set<Artifact> createArtifacts( Model model, ArtifactFilter dependencyFilter )
276         throws InvalidVersionSpecificationException
277     {
278         Collection<Dependency> dependencies = model.getDependencies();
279         Set<Artifact> projectArtifacts = new LinkedHashSet<Artifact>( dependencies.size() );
280
281         for ( Dependency dependency : dependencies )
282         {
283             String scope = dependency.getScope();
284
285             if ( StringUtils.isEmpty( scope ) )
286             {
287                 scope = Artifact.SCOPE_COMPILE;
288
289                 dependency.setScope( scope );
290             }
291
292             VersionRange versionRange = VersionRange.createFromVersionSpec( dependency.getVersion() );
293             Artifact artifact =
294                 factory.createDependencyArtifact( dependency.getGroupId(), dependency.getArtifactId(), versionRange,
295                                                   dependency.getType(), dependency.getClassifier(), scope, null,
296                                                   dependency.isOptional() );
297
298             if ( Artifact.SCOPE_SYSTEM.equals( scope ) )
299             {
300                 artifact.setFile( new File( dependency.getSystemPath() ) );
301             }
302
303             ArtifactFilter artifactFilter = dependencyFilter;
304
305             // MNG-3769: It would be nice to be able to process relocations here,
306             // so we could have this filtering step apply to post-relocated dependencies.
307             // HOWEVER, this would require a much more invasive POM resolution process
308             // in order to look for relocations, which would make the early steps in
309             // a Maven build way too heavy.
310             if ( artifact != null && ( artifactFilter == null || artifactFilter.include( artifact ) ) )
311             {
312                 if ( dependency.getExclusions() != null && !dependency.getExclusions().isEmpty() )
313                 {
314                     List<String> exclusions = new ArrayList<String>();
315                     for ( Object o : dependency.getExclusions() )
316                     {
317                         Exclusion e = (Exclusion) o;
318                         exclusions.add( e.getGroupId() + ":" + e.getArtifactId() );
319                     }
320
321                     ArtifactFilter newFilter = new ExcludesArtifactFilter( exclusions );
322
323                     if ( artifactFilter != null )
324                     {
325                         AndArtifactFilter filter = new AndArtifactFilter();
326                         filter.add( artifactFilter );
327                         filter.add( newFilter );
328                         artifactFilter = filter;
329                     }
330                     else
331                     {
332                         artifactFilter = newFilter;
333                     }
334                 }
335
336                 artifact.setDependencyFilter( artifactFilter );
337
338                 projectArtifacts.add( artifact );
339             }
340         }
341
342         return projectArtifacts;
343
344     }
345
346     // from maven-project to avoid the dependency on it
347
348     private Map createManagedVersionMap( Model model )
349         throws InvalidVersionSpecificationException
350     {
351         DependencyManagement dependencyManagement = model.getDependencyManagement();
352
353         Map<String, Artifact> map = null;
354         List<Dependency> deps;
355         if ( ( dependencyManagement != null ) && ( ( deps = dependencyManagement.getDependencies() ) != null ) && (
356             deps.size() > 0 ) )
357         {
358             map = new ManagedVersionMap( map );
359
360             for ( Dependency dependency : dependencyManagement.getDependencies() )
361             {
362
363                 VersionRange versionRange = VersionRange.createFromVersionSpec( dependency.getVersion() );
364
365                 Artifact artifact =
366                     factory.createDependencyArtifact( dependency.getGroupId(), dependency.getArtifactId(), versionRange,
367                                                       dependency.getType(), dependency.getClassifier(),
368                                                       dependency.getScope(), dependency.isOptional() );
369
370                 log.debug( "artifact {}", artifact );
371
372                 // If the dependencyManagement section listed exclusions,
373                 // add them to the managed artifacts here so that transitive
374                 // dependencies will be excluded if necessary.
375                 if ( ( null != dependency.getExclusions() ) && !dependency.getExclusions().isEmpty() )
376                 {
377                     List<String> exclusions = new ArrayList<String>();
378
379                     for ( Exclusion exclusion : dependency.getExclusions() )
380                     {
381                         exclusions.add( exclusion.getGroupId() + ":" + exclusion.getArtifactId() );
382                     }
383                     ExcludesArtifactFilter eaf = new ExcludesArtifactFilter( exclusions );
384                     artifact.setDependencyFilter( eaf );
385                 }
386                 else
387                 {
388                     artifact.setDependencyFilter( null );
389                 }
390                 map.put( dependency.getManagementKey(), artifact );
391             }
392         }
393         else
394         {
395             map = Collections.emptyMap();
396         }
397
398         return map;
399     }
400
401     private class MetadataArtifactMetadataSource
402         implements ArtifactMetadataSource
403     {
404         private final List<String> repositoryIds;
405
406         private final RepositorySession session;
407
408         private final MetadataResolver resolver;
409
410         public MetadataArtifactMetadataSource( List<String> repositoryIds, RepositorySession session )
411         {
412             this.repositoryIds = repositoryIds;
413             this.session = session;
414             resolver = this.session.getResolver();
415         }
416
417         // modified version from MavenMetadataSource to work with the simpler environment
418         public ResolutionGroup retrieve( Artifact artifact, ArtifactRepository localRepository,
419                                          List remoteRepositories )
420             throws ArtifactMetadataRetrievalException
421         {
422             // TODO: we removed relocation support here. This is something that might need to be generically handled
423             //       throughout this module
424
425             Artifact pomArtifact =
426                 factory.createProjectArtifact( artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion(),
427                                                artifact.getScope() );
428
429             File basedir = findArtifactInRepositories( repositoryIds, pomArtifact );
430
431             Model project = null;
432             if ( !Artifact.SCOPE_SYSTEM.equals( artifact.getScope() ) && basedir != null )
433             {
434                 try
435                 {
436                     project =
437                         buildProject( new RepositoryModelResolver( basedir, pathTranslator ), artifact.getGroupId(),
438                                       artifact.getArtifactId(), artifact.getVersion() );
439                 }
440                 catch ( ModelBuildingException e )
441                 {
442                     throw new ArtifactMetadataRetrievalException( e.getMessage(), e, artifact );
443                 }
444                 catch ( UnresolvableModelException e )
445                 {
446                     throw new ArtifactMetadataRetrievalException( e.getMessage(), e, artifact );
447                 }
448             }
449
450             ResolutionGroup result;
451
452             if ( project == null )
453             {
454                 // TODO: we could record this so that it is displayed in the dependency tree as (...) or similar
455
456                 // if the project is null, we encountered an invalid model (read: m1 POM)
457                 // we'll just return an empty resolution group.
458                 // or used the inherited scope (should that be passed to the buildFromRepository method above?)
459                 result = new ResolutionGroup( pomArtifact, Collections.<Artifact>emptySet(), Collections.<ArtifactRepository>emptyList() );
460             }
461             else
462             {
463                 Set<Artifact> artifacts = Collections.emptySet();
464                 if ( !artifact.getArtifactHandler().isIncludesDependencies() )
465                 {
466                     try
467                     {
468                         artifacts = createArtifacts( project, artifact.getDependencyFilter() );
469                     }
470                     catch ( InvalidVersionSpecificationException e )
471                     {
472                         throw new ArtifactMetadataRetrievalException( e.getMessage(), e, artifact );
473                     }
474                 }
475
476                 result = new ResolutionGroup( pomArtifact, artifacts, Collections.<ArtifactRepository>emptyList() );
477             }
478
479             return result;
480         }
481
482         public List retrieveAvailableVersions( Artifact artifact, ArtifactRepository localRepository,
483                                                List remoteRepositories )
484             throws ArtifactMetadataRetrievalException
485         {
486             Set<ArtifactVersion> versions = new HashSet<ArtifactVersion>();
487             for ( String repoId : repositoryIds )
488             {
489                 Collection<String> projectVersions;
490                 try
491                 {
492                     projectVersions = resolver.resolveProjectVersions( session, repoId, artifact.getGroupId(),
493                                                                        artifact.getArtifactId() );
494                 }
495                 catch ( MetadataResolutionException e )
496                 {
497                     throw new ArtifactMetadataRetrievalException( e.getMessage(), e, artifact );
498                 }
499                 for ( String version : projectVersions )
500                 {
501                     versions.add( new DefaultArtifactVersion( version ) );
502                 }
503             }
504
505             return new ArrayList<ArtifactVersion>( versions );
506         }
507
508         public ResolutionGroup retrieve( MetadataResolutionRequest metadataResolutionRequest )
509             throws ArtifactMetadataRetrievalException
510         {
511             //TODO
512             return null;
513         }
514
515         public List<ArtifactVersion> retrieveAvailableVersions( MetadataResolutionRequest metadataResolutionRequest )
516             throws ArtifactMetadataRetrievalException
517         {
518             //TODO
519             return null;
520         }
521
522         public List<ArtifactVersion> retrieveAvailableVersionsFromDeploymentRepository( Artifact artifact,
523                                                                                         ArtifactRepository artifactRepository,
524                                                                                         ArtifactRepository artifactRepository1 )
525             throws ArtifactMetadataRetrievalException
526         {
527             // TODO
528             return null;
529         }
530     }
531
532     public ArtifactFactory getFactory()
533     {
534         return factory;
535     }
536 }