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