]> source.dussan.org Git - archiva.git/blob
0b99194d2dd46bda1799fc32f42861028a784e3e
[archiva.git] /
1 package org.apache.archiva.dependency.tree.maven2;
2 /*
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  */
20
21
22 import org.apache.archiva.admin.model.RepositoryAdminException;
23 import org.apache.archiva.admin.model.beans.ManagedRepository;
24 import org.apache.archiva.admin.model.beans.NetworkProxy;
25 import org.apache.archiva.admin.model.beans.ProxyConnector;
26 import org.apache.archiva.admin.model.beans.RemoteRepository;
27 import org.apache.archiva.admin.model.managed.ManagedRepositoryAdmin;
28 import org.apache.archiva.admin.model.networkproxy.NetworkProxyAdmin;
29 import org.apache.archiva.admin.model.proxyconnector.ProxyConnectorAdmin;
30 import org.apache.archiva.admin.model.remote.RemoteRepositoryAdmin;
31 import org.apache.archiva.common.plexusbridge.PlexusSisuBridge;
32 import org.apache.archiva.common.plexusbridge.PlexusSisuBridgeException;
33 import org.apache.archiva.metadata.repository.storage.RepositoryPathTranslator;
34 import org.apache.archiva.metadata.repository.storage.maven2.RepositoryModelResolver;
35 import org.apache.archiva.proxy.common.WagonFactory;
36 import org.apache.maven.RepositoryUtils;
37 import org.apache.maven.artifact.Artifact;
38 import org.apache.maven.artifact.factory.ArtifactFactory;
39 import org.apache.maven.artifact.versioning.VersionRange;
40 import org.apache.maven.model.DependencyManagement;
41 import org.apache.maven.model.Exclusion;
42 import org.apache.maven.model.Model;
43 import org.apache.maven.model.building.DefaultModelBuilderFactory;
44 import org.apache.maven.model.building.DefaultModelBuildingRequest;
45 import org.apache.maven.model.building.ModelBuilder;
46 import org.apache.maven.model.building.ModelBuildingException;
47 import org.apache.maven.model.building.ModelBuildingRequest;
48 import org.apache.maven.model.resolution.UnresolvableModelException;
49 import org.apache.maven.project.DefaultDependencyResolutionRequest;
50 import org.apache.maven.project.DefaultProjectBuildingRequest;
51 import org.apache.maven.project.DependencyResolutionException;
52 import org.apache.maven.project.DependencyResolutionResult;
53 import org.apache.maven.project.MavenProject;
54 import org.apache.maven.repository.internal.DefaultArtifactDescriptorReader;
55 import org.apache.maven.repository.internal.DefaultVersionRangeResolver;
56 import org.apache.maven.repository.internal.DefaultVersionResolver;
57 import org.apache.maven.repository.internal.MavenRepositorySystemSession;
58 import org.codehaus.plexus.util.StringUtils;
59 import org.slf4j.Logger;
60 import org.slf4j.LoggerFactory;
61 import org.sonatype.aether.RepositorySystem;
62 import org.sonatype.aether.RepositorySystemSession;
63 import org.sonatype.aether.RequestTrace;
64 import org.sonatype.aether.artifact.ArtifactType;
65 import org.sonatype.aether.artifact.ArtifactTypeRegistry;
66 import org.sonatype.aether.collection.CollectRequest;
67 import org.sonatype.aether.collection.CollectResult;
68 import org.sonatype.aether.collection.DependencyCollectionException;
69 import org.sonatype.aether.connector.file.FileRepositoryConnectorFactory;
70 import org.sonatype.aether.graph.Dependency;
71 import org.sonatype.aether.graph.DependencyFilter;
72 import org.sonatype.aether.graph.DependencyVisitor;
73 import org.sonatype.aether.impl.ArtifactDescriptorReader;
74 import org.sonatype.aether.impl.VersionRangeResolver;
75 import org.sonatype.aether.impl.VersionResolver;
76 import org.sonatype.aether.impl.internal.DefaultServiceLocator;
77 import org.sonatype.aether.impl.internal.SimpleLocalRepositoryManager;
78 import org.sonatype.aether.repository.LocalRepository;
79 import org.sonatype.aether.resolution.DependencyRequest;
80 import org.sonatype.aether.spi.connector.ArtifactDownload;
81 import org.sonatype.aether.spi.connector.ArtifactUpload;
82 import org.sonatype.aether.spi.connector.MetadataDownload;
83 import org.sonatype.aether.spi.connector.MetadataUpload;
84 import org.sonatype.aether.spi.connector.RepositoryConnector;
85 import org.sonatype.aether.spi.connector.RepositoryConnectorFactory;
86 import org.sonatype.aether.transfer.NoRepositoryConnectorException;
87 import org.sonatype.aether.util.DefaultRepositorySystemSession;
88 import org.sonatype.aether.util.DefaultRequestTrace;
89 import org.sonatype.aether.util.artifact.ArtifacIdUtils;
90 import org.sonatype.aether.util.artifact.DefaultArtifact;
91 import org.sonatype.aether.util.artifact.JavaScopes;
92 import org.sonatype.aether.version.VersionConstraint;
93 import org.springframework.stereotype.Service;
94
95 import javax.annotation.PostConstruct;
96 import javax.inject.Inject;
97 import javax.inject.Named;
98 import java.io.File;
99 import java.util.ArrayList;
100 import java.util.Collection;
101 import java.util.Collections;
102 import java.util.HashMap;
103 import java.util.IdentityHashMap;
104 import java.util.List;
105 import java.util.Map;
106
107 /**
108  * @author Olivier Lamy
109  */
110 @Service( "dependencyTreeBuilder#maven3" )
111 public class Maven3DependencyTreeBuilder
112 {
113     private Logger log = LoggerFactory.getLogger( getClass() );
114
115     @Inject
116     private PlexusSisuBridge plexusSisuBridge;
117
118     @Inject
119     @Named( value = "repositoryPathTranslator#maven2" )
120     private RepositoryPathTranslator pathTranslator;
121
122     @Inject
123     private WagonFactory wagonFactory;
124
125     @Inject
126     private ManagedRepositoryAdmin managedRepositoryAdmin;
127
128     @Inject
129     private ProxyConnectorAdmin proxyConnectorAdmin;
130
131     @Inject
132     private NetworkProxyAdmin networkProxyAdmin;
133
134     @Inject
135     private RemoteRepositoryAdmin remoteRepositoryAdmin;
136
137     private ArtifactFactory factory;
138
139     private ModelBuilder builder;
140
141
142     private RepositorySystem repoSystem;
143
144     @PostConstruct
145     public void initialize()
146         throws PlexusSisuBridgeException
147     {
148         factory = plexusSisuBridge.lookup( ArtifactFactory.class, "default" );
149
150         repoSystem = plexusSisuBridge.lookup( RepositorySystem.class );
151         DefaultModelBuilderFactory defaultModelBuilderFactory = new DefaultModelBuilderFactory();
152         builder = defaultModelBuilderFactory.newInstance();
153     }
154
155     public DependencyResolutionResult buildDependencyTree( List<String> repositoryIds, String groupId,
156                                                            String artifactId, String version,
157                                                            DependencyVisitor dependencyVisitor )
158         throws Exception
159     {
160         Artifact projectArtifact = factory.createProjectArtifact( groupId, artifactId, version );
161         ManagedRepository repository = null;
162         try
163         {
164             repository = findArtifactInRepositories( repositoryIds, projectArtifact );
165         }
166         catch ( RepositoryAdminException e )
167         {
168             // FIXME better exception
169             throw new Exception( "Cannot build project dependency tree " + e.getMessage(), e );
170         }
171
172         if ( repository == null )
173         {
174             // metadata could not be resolved
175             return new DefaultDependencyResolutionResult();
176         }
177
178         // MRM-1411
179         // TODO: this is a workaround for a lack of proxy capability in the resolvers - replace when it can all be
180         //       handled there. It doesn't cache anything locally!
181         List<RemoteRepository> remoteRepositories = new ArrayList<RemoteRepository>();
182         Map<String, NetworkProxy> networkProxies = new HashMap<String, NetworkProxy>();
183
184         Map<String, List<ProxyConnector>> proxyConnectorsMap = proxyConnectorAdmin.getProxyConnectorAsMap();
185         List<ProxyConnector> proxyConnectors = proxyConnectorsMap.get( repository.getId() );
186         if ( proxyConnectors != null )
187         {
188             for ( ProxyConnector proxyConnector : proxyConnectors )
189             {
190                 remoteRepositories.add( remoteRepositoryAdmin.getRemoteRepository( proxyConnector.getTargetRepoId() ) );
191
192                 NetworkProxy networkProxyConfig = networkProxyAdmin.getNetworkProxy( proxyConnector.getProxyId() );
193
194                 if ( networkProxyConfig != null )
195                 {
196                     // key/value: remote repo ID/proxy info
197                     networkProxies.put( proxyConnector.getTargetRepoId(), networkProxyConfig );
198                 }
199             }
200         }
201
202         Model model = buildProject(
203             new RepositoryModelResolver( repository, pathTranslator, wagonFactory, remoteRepositories, networkProxies,
204                                          repository ), groupId, artifactId, version );
205
206         MavenProject project = new MavenProject( model );
207
208         DefaultRepositorySystemSession repositorySystemSession = new DefaultRepositorySystemSession();
209
210         // FIXME take care of relative path for getLocation
211         repositorySystemSession.setLocalRepositoryManager(
212             new SimpleLocalRepositoryManager( new File( repository.getLocation() ) ) );
213
214         DefaultProjectBuildingRequest projectBuildingRequest = new DefaultProjectBuildingRequest();
215
216         project.setProjectBuildingRequest( projectBuildingRequest );
217
218         projectBuildingRequest.setRepositorySession( repositorySystemSession );
219
220         DefaultDependencyResolutionRequest request =
221             new DefaultDependencyResolutionRequest( project, projectBuildingRequest.getRepositorySession() );
222
223         //DependencyFilter dependencyFilter
224         //request.setResolutionFilter(  )
225
226         //DependencyResolutionResult result = projectDependenciesResolver.resolve( request );
227
228         //DependencyNode dependencyNode = buildDependencyNode( null, result.getDependencyGraph(), projectArtifact, null );
229         /*DependencyNode dependencyNode = dependencyGraphBuilder.buildDependencyGraph( project, new ArtifactFilter()
230         {
231             public boolean include( Artifact artifact )
232             {
233                 return true;
234             }
235         } );*/
236
237         DependencyResolutionResult resolutionResult = resolve( request );
238
239         log.debug( "dependency graph build" );
240
241         // FIXME take care of relative path
242         test( repository.getLocation(), groupId, artifactId, version, dependencyVisitor );
243
244         return resolutionResult;
245     }
246
247     private DependencyResolutionResult resolve( DefaultDependencyResolutionRequest request )
248         throws DependencyResolutionException
249     {
250
251         RequestTrace trace = DefaultRequestTrace.newChild( null, request );
252
253         DefaultDependencyResolutionResult result = new DefaultDependencyResolutionResult();
254
255         MavenProject project = request.getMavenProject();
256         RepositorySystemSession session = request.getRepositorySession();
257         DependencyFilter filter = request.getResolutionFilter();
258
259         ArtifactTypeRegistry stereotypes = session.getArtifactTypeRegistry();
260
261         CollectRequest collect = new CollectRequest();
262         collect.setRequestContext( "project" );
263         collect.setRepositories( project.getRemoteProjectRepositories() );
264
265         if ( project.getDependencyArtifacts() == null )
266         {
267             for ( org.apache.maven.model.Dependency dependency : project.getDependencies() )
268             {
269                 if ( StringUtils.isEmpty( dependency.getGroupId() ) || StringUtils.isEmpty( dependency.getArtifactId() )
270                     || StringUtils.isEmpty( dependency.getVersion() ) )
271                 {
272                     // guard against case where best-effort resolution for invalid models is requested
273                     continue;
274                 }
275                 collect.addDependency( RepositoryUtils.toDependency( dependency, stereotypes ) );
276             }
277         }
278         else
279         {
280             Map<String, org.apache.maven.model.Dependency> dependencies =
281                 new HashMap<String, org.apache.maven.model.Dependency>();
282             for ( org.apache.maven.model.Dependency dependency : project.getDependencies() )
283             {
284                 String classifier = dependency.getClassifier();
285                 if ( classifier == null )
286                 {
287                     ArtifactType type = stereotypes.get( dependency.getType() );
288                     if ( type != null )
289                     {
290                         classifier = type.getClassifier();
291                     }
292                 }
293                 String key = ArtifacIdUtils.toVersionlessId( dependency.getGroupId(), dependency.getArtifactId(),
294                                                              dependency.getType(), classifier );
295                 dependencies.put( key, dependency );
296             }
297             for ( Artifact artifact : project.getDependencyArtifacts() )
298             {
299                 String key = artifact.getDependencyConflictId();
300                 org.apache.maven.model.Dependency dependency = dependencies.get( key );
301                 Collection<Exclusion> exclusions = dependency != null ? dependency.getExclusions() : null;
302                 org.sonatype.aether.graph.Dependency dep = RepositoryUtils.toDependency( artifact, exclusions );
303                 if ( !JavaScopes.SYSTEM.equals( dep.getScope() ) && dep.getArtifact().getFile() != null )
304                 {
305                     // enable re-resolution
306                     org.sonatype.aether.artifact.Artifact art = dep.getArtifact();
307                     art = art.setFile( null ).setVersion( art.getBaseVersion() );
308                     dep = dep.setArtifact( art );
309                 }
310                 collect.addDependency( dep );
311             }
312         }
313
314         DependencyManagement depMngt = project.getDependencyManagement();
315         if ( depMngt != null )
316         {
317             for ( org.apache.maven.model.Dependency dependency : depMngt.getDependencies() )
318             {
319                 collect.addManagedDependency( RepositoryUtils.toDependency( dependency, stereotypes ) );
320             }
321         }
322
323         collect.setRoot( new org.sonatype.aether.graph.Dependency(
324             new org.sonatype.aether.util.artifact.DefaultArtifact( project.getGroupId(), project.getArtifactId(), null,
325                                                                    project.getVersion() ), "compile" ) );
326
327         DependencyRequest depRequest = new DependencyRequest( collect, filter );
328         depRequest.setTrace( trace );
329
330         org.sonatype.aether.graph.DependencyNode node;
331         try
332         {
333             collect.setTrace( DefaultRequestTrace.newChild( trace, depRequest ) );
334             node = repoSystem.collectDependencies( session, collect ).getRoot();
335             result.setDependencyGraph( node );
336         }
337         catch ( DependencyCollectionException e )
338         {
339             result.setDependencyGraph( e.getResult().getRoot() );
340             result.setCollectionErrors( e.getResult().getExceptions() );
341
342             throw new DependencyResolutionException( result,
343                                                      "Could not resolve dependencies for project " + project.getId()
344                                                          + ": " + e.getMessage(), e );
345         }
346
347         depRequest.setRoot( node );
348
349         return result;
350     }
351
352     private void test( String localRepoDir, String groupId, String artifactId, String version,
353                        DependencyVisitor dependencyVisitor )
354     {
355
356         RepositorySystem system = newRepositorySystem();
357
358         RepositorySystemSession session = newRepositorySystemSession( system, localRepoDir );
359
360         org.sonatype.aether.artifact.Artifact artifact =
361             new DefaultArtifact( groupId + ":" + artifactId + ":" + version );
362
363         //RemoteRepository repo = Booter.newCentralRepository();
364
365         CollectRequest collectRequest = new CollectRequest();
366         collectRequest.setRoot( new Dependency( artifact, "" ) );
367         //collectRequest.addRepository( repo );
368
369         try
370         {
371             CollectResult collectResult = system.collectDependencies( session, collectRequest );
372             collectResult.getRoot().accept( dependencyVisitor );
373             log.debug( "test" );
374         }
375         catch ( DependencyCollectionException e )
376         {
377             log.error( e.getMessage(), e );
378         }
379
380
381     }
382
383     public static class MyFileRepositoryConnectorFactory
384         extends FileRepositoryConnectorFactory
385     {
386
387         public MyFileRepositoryConnectorFactory()
388         {
389
390         }
391
392         public RepositoryConnector newInstance( RepositorySystemSession session,
393                                                 org.sonatype.aether.repository.RemoteRepository repository )
394             throws NoRepositoryConnectorException
395         {
396
397             try
398             {
399                 return super.newInstance( session, repository );
400             }
401             catch ( NoRepositoryConnectorException e )
402             {
403
404             }
405
406             return new RepositoryConnector()
407             {
408
409                 private Logger log = LoggerFactory.getLogger( getClass() );
410
411                 public void get( Collection<? extends ArtifactDownload> artifactDownloads,
412                                  Collection<? extends MetadataDownload> metadataDownloads )
413                 {
414                     log.debug( "get" );
415                 }
416
417                 public void put( Collection<? extends ArtifactUpload> artifactUploads,
418                                  Collection<? extends MetadataUpload> metadataUploads )
419                 {
420                     log.debug( "put" );
421                 }
422
423                 public void close()
424                 {
425                     log.debug( "close" );
426                 }
427             };
428         }
429     }
430
431     public static RepositorySystem newRepositorySystem()
432     {
433         DefaultServiceLocator locator = new DefaultServiceLocator();
434         locator.addService( RepositoryConnectorFactory.class,
435                             MyFileRepositoryConnectorFactory.class );// FileRepositoryConnectorFactory.class );
436         locator.addService( VersionResolver.class, DefaultVersionResolver.class );
437         locator.addService( VersionRangeResolver.class, DefaultVersionRangeResolver.class );
438         locator.addService( ArtifactDescriptorReader.class, DefaultArtifactDescriptorReader.class );
439         //locator.addService( RepositoryConnectorFactory.class, WagonRepositoryConnectorFactory.class );
440         //locator.setServices( WagonProvider.class,  );
441
442         return locator.getService( RepositorySystem.class );
443     }
444
445     public static RepositorySystemSession newRepositorySystemSession( RepositorySystem system, String localRepoDir )
446     {
447         MavenRepositorySystemSession session = new MavenRepositorySystemSession();
448
449         LocalRepository localRepo = new LocalRepository( localRepoDir );
450         session.setLocalRepositoryManager( system.newLocalRepositoryManager( localRepo ) );
451
452         //session.setTransferListener( new ConsoleTransferListener() );
453         //session.setRepositoryListener( new ConsoleRepositoryListener() );
454
455         return session;
456     }
457
458     private String getVersionSelectedFromRange( VersionConstraint constraint )
459     {
460         if ( ( constraint == null ) || ( constraint.getVersion() != null ) )
461         {
462             return null;
463         }
464
465         StringBuilder sb = new StringBuilder();
466         for ( org.sonatype.aether.version.VersionRange range : constraint.getRanges() )
467         {
468             if ( sb.length() > 0 )
469             {
470                 sb.append( ',' );
471             }
472             sb.append( range );
473         }
474
475         return sb.toString();
476     }
477
478     private Artifact getDependencyArtifact( Dependency dep )
479     {
480         org.sonatype.aether.artifact.Artifact artifact = dep.getArtifact();
481
482         return factory.createDependencyArtifact( artifact.getGroupId(), artifact.getArtifactId(),
483                                                  VersionRange.createFromVersion( artifact.getVersion() ),
484                                                  artifact.getExtension(), artifact.getClassifier(), dep.getScope(),
485                                                  dep.isOptional() );
486     }
487
488     private Model buildProject( RepositoryModelResolver modelResolver, String groupId, String artifactId,
489                                 String version )
490         throws ModelBuildingException, UnresolvableModelException
491     {
492         DefaultModelBuildingRequest req = new DefaultModelBuildingRequest();
493         req.setProcessPlugins( false );
494         req.setModelSource( modelResolver.resolveModel( groupId, artifactId, version ) );
495         req.setModelResolver( modelResolver );
496         req.setValidationLevel( ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL );
497         //MRM-1607. olamy this will resolve jdk profiles on the current running archiva jvm
498         req.setSystemProperties( System.getProperties() );
499
500         return builder.build( req ).getEffectiveModel();
501     }
502
503     private ManagedRepository findArtifactInRepositories( List<String> repositoryIds, Artifact projectArtifact )
504         throws RepositoryAdminException
505     {
506         for ( String repoId : repositoryIds )
507         {
508             ManagedRepository managedRepository = managedRepositoryAdmin.getManagedRepository( repoId );
509
510             File repoDir = new File( managedRepository.getLocation() );
511             File file = pathTranslator.toFile( repoDir, projectArtifact.getGroupId(), projectArtifact.getArtifactId(),
512                                                projectArtifact.getBaseVersion(),
513                                                projectArtifact.getArtifactId() + "-" + projectArtifact.getVersion()
514                                                    + ".pom" );
515
516             if ( file.exists() )
517             {
518                 return managedRepository;
519             }
520         }
521         return null;
522     }
523
524     public static class DefaultDependencyResolutionResult
525         implements DependencyResolutionResult
526     {
527
528         private org.sonatype.aether.graph.DependencyNode root;
529
530         private List<Dependency> dependencies = new ArrayList<Dependency>();
531
532         private List<Dependency> resolvedDependencies = new ArrayList<Dependency>();
533
534         private List<Dependency> unresolvedDependencies = new ArrayList<Dependency>();
535
536         private List<Exception> collectionErrors = new ArrayList<Exception>();
537
538         private Map<Dependency, List<Exception>> resolutionErrors = new IdentityHashMap<Dependency, List<Exception>>();
539
540         public org.sonatype.aether.graph.DependencyNode getDependencyGraph()
541         {
542             return root;
543         }
544
545         public void setDependencyGraph( org.sonatype.aether.graph.DependencyNode root )
546         {
547             this.root = root;
548         }
549
550         public List<Dependency> getDependencies()
551         {
552             return dependencies;
553         }
554
555         public List<Dependency> getResolvedDependencies()
556         {
557             return resolvedDependencies;
558         }
559
560         public void addResolvedDependency( Dependency dependency )
561         {
562             dependencies.add( dependency );
563             resolvedDependencies.add( dependency );
564         }
565
566         public List<Dependency> getUnresolvedDependencies()
567         {
568             return unresolvedDependencies;
569         }
570
571         public List<Exception> getCollectionErrors()
572         {
573             return collectionErrors;
574         }
575
576         public void setCollectionErrors( List<Exception> exceptions )
577         {
578             if ( exceptions != null )
579             {
580                 this.collectionErrors = exceptions;
581             }
582             else
583             {
584                 this.collectionErrors = new ArrayList<Exception>();
585             }
586         }
587
588         public List<Exception> getResolutionErrors( Dependency dependency )
589         {
590             List<Exception> errors = resolutionErrors.get( dependency );
591             return ( errors != null ) ? errors : Collections.<Exception>emptyList();
592         }
593
594         public void setResolutionErrors( Dependency dependency, List<Exception> errors )
595         {
596             dependencies.add( dependency );
597             unresolvedDependencies.add( dependency );
598             resolutionErrors.put( dependency, errors );
599         }
600
601     }
602 }