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.eclipse.aether.DefaultRepositorySystemSession;
038    import org.eclipse.aether.RepositorySystem;
039    import org.eclipse.aether.RepositorySystemSession;
040    import org.eclipse.aether.RequestTrace;
041    import org.eclipse.aether.artifact.ArtifactProperties;
042    import org.eclipse.aether.artifact.ArtifactType;
043    import org.eclipse.aether.artifact.ArtifactTypeRegistry;
044    import org.eclipse.aether.collection.CollectRequest;
045    import org.eclipse.aether.collection.DependencyCollectionException;
046    import org.eclipse.aether.graph.DependencyFilter;
047    import org.eclipse.aether.graph.DependencyNode;
048    import org.eclipse.aether.graph.DependencyVisitor;
049    import org.eclipse.aether.resolution.ArtifactResult;
050    import org.eclipse.aether.resolution.DependencyRequest;
051    import org.eclipse.aether.util.artifact.ArtifactIdUtils;
052    import org.eclipse.aether.util.artifact.JavaScopes;
053    import org.eclipse.aether.util.graph.manager.DependencyManagerUtils;
054    
055    /**
056     * @author Benjamin Bentmann
057     */
058    @Component( role = ProjectDependenciesResolver.class )
059    public class DefaultProjectDependenciesResolver
060        implements ProjectDependenciesResolver
061    {
062    
063        @Requirement
064        private Logger logger;
065    
066        @Requirement
067        private RepositorySystem repoSystem;
068    
069        public DependencyResolutionResult resolve( DependencyResolutionRequest request )
070            throws DependencyResolutionException
071        {
072            RequestTrace trace = RequestTrace.newChild( null, request );
073    
074            DefaultDependencyResolutionResult result = new DefaultDependencyResolutionResult();
075    
076            MavenProject project = request.getMavenProject();
077            RepositorySystemSession session = request.getRepositorySession();
078            if ( logger.isDebugEnabled()
079                && session.getConfigProperties().get( DependencyManagerUtils.CONFIG_PROP_VERBOSE ) == null )
080            {
081                DefaultRepositorySystemSession verbose = new DefaultRepositorySystemSession( session );
082                verbose.setConfigProperty( DependencyManagerUtils.CONFIG_PROP_VERBOSE, Boolean.TRUE );
083                session = verbose;
084            }
085            DependencyFilter filter = request.getResolutionFilter();
086    
087            ArtifactTypeRegistry stereotypes = session.getArtifactTypeRegistry();
088    
089            CollectRequest collect = new CollectRequest();
090            collect.setRootArtifact( RepositoryUtils.toArtifact( project.getArtifact() ) );
091            collect.setRequestContext( "project" );
092            collect.setRepositories( project.getRemoteProjectRepositories() );
093    
094            if ( project.getDependencyArtifacts() == null )
095            {
096                for ( Dependency dependency : project.getDependencies() )
097                {
098                    if ( StringUtils.isEmpty( dependency.getGroupId() ) || StringUtils.isEmpty( dependency.getArtifactId() )
099                        || StringUtils.isEmpty( dependency.getVersion() ) )
100                    {
101                        // guard against case where best-effort resolution for invalid models is requested
102                        continue;
103                    }
104                    collect.addDependency( RepositoryUtils.toDependency( dependency, stereotypes ) );
105                }
106            }
107            else
108            {
109                Map<String, Dependency> dependencies = new HashMap<String, Dependency>();
110                for ( Dependency dependency : project.getDependencies() )
111                {
112                    String classifier = dependency.getClassifier();
113                    if ( classifier == null )
114                    {
115                        ArtifactType type = stereotypes.get( dependency.getType() );
116                        if ( type != null )
117                        {
118                            classifier = type.getClassifier();
119                        }
120                    }
121                    String key =
122                        ArtifactIdUtils.toVersionlessId( dependency.getGroupId(), dependency.getArtifactId(),
123                                                        dependency.getType(), classifier );
124                    dependencies.put( key, dependency );
125                }
126                for ( Artifact artifact : project.getDependencyArtifacts() )
127                {
128                    String key = artifact.getDependencyConflictId();
129                    Dependency dependency = dependencies.get( key );
130                    Collection<Exclusion> exclusions = dependency != null ? dependency.getExclusions() : null;
131                    org.eclipse.aether.graph.Dependency dep = RepositoryUtils.toDependency( artifact, exclusions );
132                    if ( !JavaScopes.SYSTEM.equals( dep.getScope() ) && dep.getArtifact().getFile() != null )
133                    {
134                        // enable re-resolution
135                        org.eclipse.aether.artifact.Artifact art = dep.getArtifact();
136                        art = art.setFile( null ).setVersion( art.getBaseVersion() );
137                        dep = dep.setArtifact( art );
138                    }
139                    collect.addDependency( dep );
140                }
141            }
142    
143            DependencyManagement depMngt = project.getDependencyManagement();
144            if ( depMngt != null )
145            {
146                for ( Dependency dependency : depMngt.getDependencies() )
147                {
148                    collect.addManagedDependency( RepositoryUtils.toDependency( dependency, stereotypes ) );
149                }
150            }
151    
152            DependencyRequest depRequest = new DependencyRequest( collect, filter );
153            depRequest.setTrace( trace );
154    
155            DependencyNode node;
156            try
157            {
158                collect.setTrace( RequestTrace.newChild( trace, depRequest ) );
159                node = repoSystem.collectDependencies( session, collect ).getRoot();
160                result.setDependencyGraph( node );
161            }
162            catch ( DependencyCollectionException e )
163            {
164                result.setDependencyGraph( e.getResult().getRoot() );
165                result.setCollectionErrors( e.getResult().getExceptions() );
166    
167                throw new DependencyResolutionException( result, "Could not resolve dependencies for project "
168                    + project.getId() + ": " + e.getMessage(), e );
169            }
170    
171            depRequest.setRoot( node );
172    
173            if ( logger.isWarnEnabled() )
174            {
175                for ( DependencyNode child : node.getChildren() )
176                {
177                    if ( !child.getRelocations().isEmpty() )
178                    {
179                        logger.warn( "The artifact " + child.getRelocations().get( 0 ) + " has been relocated to "
180                            + child.getDependency().getArtifact() );
181                    }
182                }
183            }
184    
185            if ( logger.isDebugEnabled() )
186            {
187                node.accept( new GraphLogger( project ) );
188            }
189    
190            try
191            {
192                process( result, repoSystem.resolveDependencies( session, depRequest ).getArtifactResults() );
193            }
194            catch ( org.eclipse.aether.resolution.DependencyResolutionException e )
195            {
196                process( result, e.getResult().getArtifactResults() );
197    
198                throw new DependencyResolutionException( result, "Could not resolve dependencies for project "
199                    + project.getId() + ": " + e.getMessage(), e );
200            }
201    
202            return result;
203        }
204    
205        private void process( DefaultDependencyResolutionResult result, Collection<ArtifactResult> results )
206        {
207            for ( ArtifactResult ar : results )
208            {
209                DependencyNode node = ar.getRequest().getDependencyNode();
210                if ( ar.isResolved() )
211                {
212                    result.addResolvedDependency( node.getDependency() );
213                }
214                else
215                {
216                    result.setResolutionErrors( node.getDependency(), ar.getExceptions() );
217                }
218            }
219        }
220    
221        class GraphLogger
222            implements DependencyVisitor
223        {
224    
225            private final MavenProject project;
226    
227            private String indent = "";
228    
229            private Map<String, Dependency> managed;
230    
231            public GraphLogger( MavenProject project )
232            {
233                this.project = project;
234            }
235    
236            public boolean visitEnter( DependencyNode node )
237            {
238                StringBuilder buffer = new StringBuilder( 128 );
239                buffer.append( indent );
240                org.eclipse.aether.graph.Dependency dep = node.getDependency();
241                if ( dep != null )
242                {
243                    org.eclipse.aether.artifact.Artifact art = dep.getArtifact();
244    
245                    buffer.append( art );
246                    buffer.append( ':' ).append( dep.getScope() );
247    
248                    String premanagedScope = DependencyManagerUtils.getPremanagedScope( node );
249                    if ( premanagedScope != null && !premanagedScope.equals( dep.getScope() ) )
250                    {
251                        buffer.append( " (scope managed from " ).append( premanagedScope );
252                        appendManagementSource( buffer, art, "scope" );
253                        buffer.append( ")" );
254                    }
255    
256                    String premanagedVersion = DependencyManagerUtils.getPremanagedVersion( node );
257                    if ( premanagedVersion != null && !premanagedVersion.equals( art.getVersion() ) )
258                    {
259                        buffer.append( " (version managed from " ).append( premanagedVersion );
260                        appendManagementSource( buffer, art, "version" );
261                        buffer.append( ")" );
262                    }
263                }
264                else
265                {
266                    buffer.append( project.getGroupId() );
267                    buffer.append( ':' ).append( project.getArtifactId() );
268                    buffer.append( ':' ).append( project.getPackaging() );
269                    buffer.append( ':' ).append( project.getVersion() );
270                }
271    
272                logger.debug( buffer.toString() );
273                indent += "   ";
274                return true;
275            }
276    
277            public boolean visitLeave( DependencyNode node )
278            {
279                indent = indent.substring( 0, indent.length() - 3 );
280                return true;
281            }
282    
283            private void appendManagementSource( StringBuilder buffer, org.eclipse.aether.artifact.Artifact artifact,
284                                                 String field )
285            {
286                if ( managed == null )
287                {
288                    managed = new HashMap<String, Dependency>();
289                    if ( project.getDependencyManagement() != null )
290                    {
291                        for ( Dependency dep : project.getDependencyManagement().getDependencies() )
292                        {
293                            managed.put( dep.getManagementKey(), dep );
294                        }
295                    }
296                }
297    
298                String key =
299                    ArtifactIdUtils.toVersionlessId( artifact.getGroupId(), artifact.getArtifactId(),
300                                                    artifact.getProperty( ArtifactProperties.TYPE, "jar" ),
301                                                    artifact.getClassifier() );
302    
303                Dependency dependency = managed.get( key );
304                if ( dependency != null )
305                {
306                    InputLocation location = dependency.getLocation( field );
307                    if ( location != null )
308                    {
309                        InputSource source = location.getSource();
310                        if ( source != null )
311                        {
312                            buffer.append( " by " ).append( source.getModelId() );
313                        }
314                    }
315                }
316            }
317    
318        }
319    
320    }