001    package org.apache.maven.project;
002    
003    /*
004     * Licensed to the Apache Software Foundation (ASF) under one
005     * or more contributor license agreements.  See the NOTICE file
006     * distributed with this work for additional information
007     * regarding copyright ownership.  The ASF licenses this file
008     * to you under the Apache License, Version 2.0 (the
009     * "License"); you may not use this file except in compliance
010     * with the License.  You may obtain a copy of the License at
011     *
012     *   http://www.apache.org/licenses/LICENSE-2.0
013     *
014     * Unless required by applicable law or agreed to in writing,
015     * software distributed under the License is distributed on an
016     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017     * KIND, either express or implied.  See the License for the
018     * specific language governing permissions and limitations
019     * under the License.
020     */
021    
022    import java.util.Collection;
023    import java.util.HashMap;
024    import java.util.Map;
025    
026    import org.apache.maven.RepositoryUtils;
027    import org.apache.maven.artifact.Artifact;
028    import org.apache.maven.model.Dependency;
029    import org.apache.maven.model.DependencyManagement;
030    import org.apache.maven.model.Exclusion;
031    import org.apache.maven.model.InputLocation;
032    import org.apache.maven.model.InputSource;
033    import org.codehaus.plexus.component.annotations.Component;
034    import org.codehaus.plexus.component.annotations.Requirement;
035    import org.codehaus.plexus.logging.Logger;
036    import org.codehaus.plexus.util.StringUtils;
037    import org.sonatype.aether.RepositorySystem;
038    import org.sonatype.aether.RepositorySystemSession;
039    import org.sonatype.aether.RequestTrace;
040    import org.sonatype.aether.artifact.ArtifactType;
041    import org.sonatype.aether.artifact.ArtifactTypeRegistry;
042    import org.sonatype.aether.collection.CollectRequest;
043    import org.sonatype.aether.collection.DependencyCollectionException;
044    import org.sonatype.aether.graph.DependencyFilter;
045    import org.sonatype.aether.graph.DependencyNode;
046    import org.sonatype.aether.graph.DependencyVisitor;
047    import org.sonatype.aether.resolution.ArtifactResult;
048    import org.sonatype.aether.resolution.DependencyRequest;
049    import org.sonatype.aether.util.DefaultRequestTrace;
050    import org.sonatype.aether.util.artifact.ArtifacIdUtils;
051    import org.sonatype.aether.util.artifact.ArtifactProperties;
052    import org.sonatype.aether.util.artifact.JavaScopes;
053    
054    /**
055     * @author Benjamin Bentmann
056     */
057    @Component( role = ProjectDependenciesResolver.class )
058    public class DefaultProjectDependenciesResolver
059        implements ProjectDependenciesResolver
060    {
061    
062        @Requirement
063        private Logger logger;
064    
065        @Requirement
066        private RepositorySystem repoSystem;
067    
068        public DependencyResolutionResult resolve( DependencyResolutionRequest request )
069            throws DependencyResolutionException
070        {
071            RequestTrace trace = DefaultRequestTrace.newChild( null, request );
072    
073            DefaultDependencyResolutionResult result = new DefaultDependencyResolutionResult();
074    
075            MavenProject project = request.getMavenProject();
076            RepositorySystemSession session = request.getRepositorySession();
077            DependencyFilter filter = request.getResolutionFilter();
078    
079            ArtifactTypeRegistry stereotypes = session.getArtifactTypeRegistry();
080    
081            CollectRequest collect = new CollectRequest();
082            collect.setRequestContext( "project" );
083            collect.setRepositories( project.getRemoteProjectRepositories() );
084    
085            if ( project.getDependencyArtifacts() == null )
086            {
087                for ( Dependency dependency : project.getDependencies() )
088                {
089                    if ( StringUtils.isEmpty( dependency.getGroupId() ) || StringUtils.isEmpty( dependency.getArtifactId() )
090                        || StringUtils.isEmpty( dependency.getVersion() ) )
091                    {
092                        // guard against case where best-effort resolution for invalid models is requested
093                        continue;
094                    }
095                    collect.addDependency( RepositoryUtils.toDependency( dependency, stereotypes ) );
096                }
097            }
098            else
099            {
100                Map<String, Dependency> dependencies = new HashMap<String, Dependency>();
101                for ( Dependency dependency : project.getDependencies() )
102                {
103                    String classifier = dependency.getClassifier();
104                    if ( classifier == null )
105                    {
106                        ArtifactType type = stereotypes.get( dependency.getType() );
107                        if ( type != null )
108                        {
109                            classifier = type.getClassifier();
110                        }
111                    }
112                    String key =
113                        ArtifacIdUtils.toVersionlessId( dependency.getGroupId(), dependency.getArtifactId(),
114                                                        dependency.getType(), classifier );
115                    dependencies.put( key, dependency );
116                }
117                for ( Artifact artifact : project.getDependencyArtifacts() )
118                {
119                    String key = artifact.getDependencyConflictId();
120                    Dependency dependency = dependencies.get( key );
121                    Collection<Exclusion> exclusions = dependency != null ? dependency.getExclusions() : null;
122                    org.sonatype.aether.graph.Dependency dep = RepositoryUtils.toDependency( artifact, exclusions );
123                    if ( !JavaScopes.SYSTEM.equals( dep.getScope() ) && dep.getArtifact().getFile() != null )
124                    {
125                        // enable re-resolution
126                        org.sonatype.aether.artifact.Artifact art = dep.getArtifact();
127                        art = art.setFile( null ).setVersion( art.getBaseVersion() );
128                        dep = dep.setArtifact( art );
129                    }
130                    collect.addDependency( dep );
131                }
132            }
133    
134            DependencyManagement depMngt = project.getDependencyManagement();
135            if ( depMngt != null )
136            {
137                for ( Dependency dependency : depMngt.getDependencies() )
138                {
139                    collect.addManagedDependency( RepositoryUtils.toDependency( dependency, stereotypes ) );
140                }
141            }
142    
143            DependencyRequest depRequest = new DependencyRequest( collect, filter );
144            depRequest.setTrace( trace );
145    
146            DependencyNode node;
147            try
148            {
149                collect.setTrace( DefaultRequestTrace.newChild( trace, depRequest ) );
150                node = repoSystem.collectDependencies( session, collect ).getRoot();
151                result.setDependencyGraph( node );
152            }
153            catch ( DependencyCollectionException e )
154            {
155                result.setDependencyGraph( e.getResult().getRoot() );
156                result.setCollectionErrors( e.getResult().getExceptions() );
157    
158                throw new DependencyResolutionException( result, "Could not resolve dependencies for project "
159                    + project.getId() + ": " + e.getMessage(), e );
160            }
161    
162            depRequest.setRoot( node );
163    
164            if ( logger.isWarnEnabled() )
165            {
166                for ( DependencyNode child : node.getChildren() )
167                {
168                    if ( !child.getRelocations().isEmpty() )
169                    {
170                        logger.warn( "The artifact " + child.getRelocations().get( 0 ) + " has been relocated to "
171                            + child.getDependency().getArtifact() );
172                    }
173                }
174            }
175    
176            if ( logger.isDebugEnabled() )
177            {
178                node.accept( new GraphLogger( project ) );
179            }
180    
181            try
182            {
183                process( result, repoSystem.resolveDependencies( session, depRequest ).getArtifactResults() );
184            }
185            catch ( org.sonatype.aether.resolution.DependencyResolutionException e )
186            {
187                process( result, e.getResult().getArtifactResults() );
188    
189                throw new DependencyResolutionException( result, "Could not resolve dependencies for project "
190                    + project.getId() + ": " + e.getMessage(), e );
191            }
192    
193            return result;
194        }
195    
196        private void process( DefaultDependencyResolutionResult result, Collection<ArtifactResult> results )
197        {
198            for ( ArtifactResult ar : results )
199            {
200                DependencyNode node = ar.getRequest().getDependencyNode();
201                if ( ar.isResolved() )
202                {
203                    result.addResolvedDependency( node.getDependency() );
204                }
205                else
206                {
207                    result.setResolutionErrors( node.getDependency(), ar.getExceptions() );
208                }
209            }
210        }
211    
212        class GraphLogger
213            implements DependencyVisitor
214        {
215    
216            private final MavenProject project;
217    
218            private String indent = "";
219    
220            private Map<String, Dependency> managed;
221    
222            public GraphLogger( MavenProject project )
223            {
224                this.project = project;
225            }
226    
227            public boolean visitEnter( DependencyNode node )
228            {
229                StringBuilder buffer = new StringBuilder( 128 );
230                buffer.append( indent );
231                org.sonatype.aether.graph.Dependency dep = node.getDependency();
232                if ( dep != null )
233                {
234                    org.sonatype.aether.artifact.Artifact art = dep.getArtifact();
235    
236                    buffer.append( art );
237                    buffer.append( ':' ).append( dep.getScope() );
238    
239                    if ( node.getPremanagedScope() != null && !node.getPremanagedScope().equals( dep.getScope() ) )
240                    {
241                        buffer.append( " (scope managed from " ).append( node.getPremanagedScope() );
242                        appendManagementSource( buffer, art, "scope" );
243                        buffer.append( ")" );
244                    }
245    
246                    if ( node.getPremanagedVersion() != null && !node.getPremanagedVersion().equals( art.getVersion() ) )
247                    {
248                        buffer.append( " (version managed from " ).append( node.getPremanagedVersion() );
249                        appendManagementSource( buffer, art, "version" );
250                        buffer.append( ")" );
251                    }
252                }
253                else
254                {
255                    buffer.append( project.getGroupId() );
256                    buffer.append( ':' ).append( project.getArtifactId() );
257                    buffer.append( ':' ).append( project.getPackaging() );
258                    buffer.append( ':' ).append( project.getVersion() );
259                }
260    
261                logger.debug( buffer.toString() );
262                indent += "   ";
263                return true;
264            }
265    
266            public boolean visitLeave( DependencyNode node )
267            {
268                indent = indent.substring( 0, indent.length() - 3 );
269                return true;
270            }
271    
272            private void appendManagementSource( StringBuilder buffer, org.sonatype.aether.artifact.Artifact artifact,
273                                                 String field )
274            {
275                if ( managed == null )
276                {
277                    managed = new HashMap<String, Dependency>();
278                    if ( project.getDependencyManagement() != null )
279                    {
280                        for ( Dependency dep : project.getDependencyManagement().getDependencies() )
281                        {
282                            managed.put( dep.getManagementKey(), dep );
283                        }
284                    }
285                }
286    
287                String key =
288                    ArtifacIdUtils.toVersionlessId( artifact.getGroupId(), artifact.getArtifactId(),
289                                                    artifact.getProperty( ArtifactProperties.TYPE, "jar" ),
290                                                    artifact.getClassifier() );
291    
292                Dependency dependency = managed.get( key );
293                if ( dependency != null )
294                {
295                    InputLocation location = dependency.getLocation( field );
296                    if ( location != null )
297                    {
298                        InputSource source = location.getSource();
299                        if ( source != null )
300                        {
301                            buffer.append( " by " ).append( source.getModelId() );
302                        }
303                    }
304                }
305            }
306    
307        }
308    
309    }