001package 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
022import java.util.Collection;
023import java.util.HashMap;
024import java.util.Map;
025
026import org.apache.maven.RepositoryUtils;
027import org.apache.maven.artifact.Artifact;
028import org.apache.maven.model.Dependency;
029import org.apache.maven.model.DependencyManagement;
030import org.apache.maven.model.Exclusion;
031import org.apache.maven.model.InputLocation;
032import org.apache.maven.model.InputSource;
033import org.codehaus.plexus.component.annotations.Component;
034import org.codehaus.plexus.component.annotations.Requirement;
035import org.codehaus.plexus.logging.Logger;
036import org.codehaus.plexus.util.StringUtils;
037import org.eclipse.aether.DefaultRepositorySystemSession;
038import org.eclipse.aether.RepositorySystem;
039import org.eclipse.aether.RepositorySystemSession;
040import org.eclipse.aether.RequestTrace;
041import org.eclipse.aether.artifact.ArtifactProperties;
042import org.eclipse.aether.artifact.ArtifactType;
043import org.eclipse.aether.artifact.ArtifactTypeRegistry;
044import org.eclipse.aether.collection.CollectRequest;
045import org.eclipse.aether.collection.DependencyCollectionException;
046import org.eclipse.aether.graph.DependencyFilter;
047import org.eclipse.aether.graph.DependencyNode;
048import org.eclipse.aether.graph.DependencyVisitor;
049import org.eclipse.aether.resolution.ArtifactResult;
050import org.eclipse.aether.resolution.DependencyRequest;
051import org.eclipse.aether.util.artifact.ArtifactIdUtils;
052import org.eclipse.aether.util.artifact.JavaScopes;
053import org.eclipse.aether.util.graph.manager.DependencyManagerUtils;
054
055/**
056 * @author Benjamin Bentmann
057 */
058@Component( role = ProjectDependenciesResolver.class )
059public 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}