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