001package org.apache.maven.plugin.internal;
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.LinkedHashMap;
023import java.util.List;
024import java.util.Map;
025
026import org.apache.maven.RepositoryUtils;
027import org.apache.maven.model.Dependency;
028import org.apache.maven.model.Plugin;
029import org.apache.maven.plugin.PluginResolutionException;
030import org.codehaus.plexus.component.annotations.Component;
031import org.codehaus.plexus.component.annotations.Requirement;
032import org.codehaus.plexus.logging.Logger;
033import org.eclipse.aether.DefaultRepositorySystemSession;
034import org.eclipse.aether.RepositorySystem;
035import org.eclipse.aether.RepositorySystemSession;
036import org.eclipse.aether.RequestTrace;
037import org.eclipse.aether.artifact.Artifact;
038import org.eclipse.aether.artifact.DefaultArtifact;
039import org.eclipse.aether.collection.CollectRequest;
040import org.eclipse.aether.collection.DependencyCollectionException;
041import org.eclipse.aether.collection.DependencyGraphTransformer;
042import org.eclipse.aether.collection.DependencySelector;
043import org.eclipse.aether.graph.DependencyFilter;
044import org.eclipse.aether.graph.DependencyNode;
045import org.eclipse.aether.graph.DependencyVisitor;
046import org.eclipse.aether.repository.RemoteRepository;
047import org.eclipse.aether.resolution.ArtifactDescriptorException;
048import org.eclipse.aether.resolution.ArtifactDescriptorRequest;
049import org.eclipse.aether.resolution.ArtifactDescriptorResult;
050import org.eclipse.aether.resolution.ArtifactRequest;
051import org.eclipse.aether.resolution.ArtifactResolutionException;
052import org.eclipse.aether.resolution.DependencyRequest;
053import org.eclipse.aether.resolution.DependencyResolutionException;
054import org.eclipse.aether.util.artifact.JavaScopes;
055import org.eclipse.aether.util.filter.AndDependencyFilter;
056import org.eclipse.aether.util.filter.ScopeDependencyFilter;
057import org.eclipse.aether.util.graph.selector.AndDependencySelector;
058import org.eclipse.aether.util.graph.transformer.ChainedDependencyGraphTransformer;
059import org.eclipse.aether.util.repository.SimpleArtifactDescriptorPolicy;
060
061/**
062 * Assists in resolving the dependencies of a plugin. <strong>Warning:</strong> This is an internal utility class that
063 * is only public for technical reasons, it is not part of the public API. In particular, this class can be changed or
064 * deleted without prior notice.
065 *
066 * @since 3.0
067 * @author Benjamin Bentmann
068 */
069@Component( role = PluginDependenciesResolver.class )
070public class DefaultPluginDependenciesResolver
071    implements PluginDependenciesResolver
072{
073
074    private static final String REPOSITORY_CONTEXT = "plugin";
075
076    @Requirement
077    private Logger logger;
078
079    @Requirement
080    private RepositorySystem repoSystem;
081
082    private Artifact toArtifact( Plugin plugin, RepositorySystemSession session )
083    {
084        return new DefaultArtifact( plugin.getGroupId(), plugin.getArtifactId(), null, "jar", plugin.getVersion(),
085                                    session.getArtifactTypeRegistry().get( "maven-plugin" ) );
086    }
087
088    public Artifact resolve( Plugin plugin, List<RemoteRepository> repositories, RepositorySystemSession session )
089        throws PluginResolutionException
090    {
091        RequestTrace trace = RequestTrace.newChild( null, plugin );
092
093        Artifact pluginArtifact = toArtifact( plugin, session );
094
095        try
096        {
097            DefaultRepositorySystemSession pluginSession = new DefaultRepositorySystemSession( session );
098            pluginSession.setArtifactDescriptorPolicy( new SimpleArtifactDescriptorPolicy( true, false ) );
099
100            ArtifactDescriptorRequest request =
101                new ArtifactDescriptorRequest( pluginArtifact, repositories, REPOSITORY_CONTEXT );
102            request.setTrace( trace );
103            ArtifactDescriptorResult result = repoSystem.readArtifactDescriptor( pluginSession, request );
104
105            pluginArtifact = result.getArtifact();
106
107            String requiredMavenVersion = (String) result.getProperties().get( "prerequisites.maven" );
108            if ( requiredMavenVersion != null )
109            {
110                Map<String, String> props = new LinkedHashMap<>( pluginArtifact.getProperties() );
111                props.put( "requiredMavenVersion", requiredMavenVersion );
112                pluginArtifact = pluginArtifact.setProperties( props );
113            }
114        }
115        catch ( ArtifactDescriptorException e )
116        {
117            throw new PluginResolutionException( plugin, e );
118        }
119
120        try
121        {
122            ArtifactRequest request = new ArtifactRequest( pluginArtifact, repositories, REPOSITORY_CONTEXT );
123            request.setTrace( trace );
124            pluginArtifact = repoSystem.resolveArtifact( session, request ).getArtifact();
125        }
126        catch ( ArtifactResolutionException e )
127        {
128            throw new PluginResolutionException( plugin, e );
129        }
130
131        return pluginArtifact;
132    }
133
134    /**
135     * @since 3.3.0
136     */
137    public DependencyNode resolveCoreExtension( Plugin plugin, DependencyFilter dependencyFilter,
138                                                List<RemoteRepository> repositories, RepositorySystemSession session )
139        throws PluginResolutionException
140    {
141        return resolveInternal( plugin, null /* pluginArtifact */, dependencyFilter, null /* transformer */,
142                                repositories, session );
143    }
144
145    public DependencyNode resolve( Plugin plugin, Artifact pluginArtifact, DependencyFilter dependencyFilter,
146                                   List<RemoteRepository> repositories, RepositorySystemSession session )
147        throws PluginResolutionException
148    {
149        return resolveInternal( plugin, pluginArtifact, dependencyFilter, new PlexusUtilsInjector(), repositories,
150                                session );
151    }
152
153    private DependencyNode resolveInternal( Plugin plugin, Artifact pluginArtifact, DependencyFilter dependencyFilter,
154                                            DependencyGraphTransformer transformer,
155                                            List<RemoteRepository> repositories, RepositorySystemSession session )
156        throws PluginResolutionException
157    {
158        RequestTrace trace = RequestTrace.newChild( null, plugin );
159
160        if ( pluginArtifact == null )
161        {
162            pluginArtifact = toArtifact( plugin, session );
163        }
164
165        DependencyFilter collectionFilter = new ScopeDependencyFilter( "provided", "test" );
166        DependencyFilter resolutionFilter = AndDependencyFilter.newInstance( collectionFilter, dependencyFilter );
167
168        DependencyNode node;
169
170        try
171        {
172            DependencySelector selector =
173                AndDependencySelector.newInstance( session.getDependencySelector(), new WagonExcluder() );
174
175            transformer =
176                ChainedDependencyGraphTransformer.newInstance( session.getDependencyGraphTransformer(), transformer );
177
178            DefaultRepositorySystemSession pluginSession = new DefaultRepositorySystemSession( session );
179            pluginSession.setDependencySelector( selector );
180            pluginSession.setDependencyGraphTransformer( transformer );
181
182            CollectRequest request = new CollectRequest();
183            request.setRequestContext( REPOSITORY_CONTEXT );
184            request.setRepositories( repositories );
185            request.setRoot( new org.eclipse.aether.graph.Dependency( pluginArtifact, null ) );
186            for ( Dependency dependency : plugin.getDependencies() )
187            {
188                org.eclipse.aether.graph.Dependency pluginDep =
189                    RepositoryUtils.toDependency( dependency, session.getArtifactTypeRegistry() );
190                if ( !JavaScopes.SYSTEM.equals( pluginDep.getScope() ) )
191                {
192                    pluginDep = pluginDep.setScope( JavaScopes.RUNTIME );
193                }
194                request.addDependency( pluginDep );
195            }
196
197            DependencyRequest depRequest = new DependencyRequest( request, resolutionFilter );
198            depRequest.setTrace( trace );
199
200            request.setTrace( RequestTrace.newChild( trace, depRequest ) );
201
202            node = repoSystem.collectDependencies( pluginSession, request ).getRoot();
203
204            if ( logger.isDebugEnabled() )
205            {
206                node.accept( new GraphLogger() );
207            }
208
209            depRequest.setRoot( node );
210            repoSystem.resolveDependencies( session, depRequest );
211        }
212        catch ( DependencyCollectionException e )
213        {
214            throw new PluginResolutionException( plugin, e );
215        }
216        catch ( DependencyResolutionException e )
217        {
218            throw new PluginResolutionException( plugin, e.getCause() );
219        }
220
221        return node;
222    }
223
224    class GraphLogger
225        implements DependencyVisitor
226    {
227
228        private String indent = "";
229
230        public boolean visitEnter( DependencyNode node )
231        {
232            StringBuilder buffer = new StringBuilder( 128 );
233            buffer.append( indent );
234            org.eclipse.aether.graph.Dependency dep = node.getDependency();
235            if ( dep != null )
236            {
237                Artifact art = dep.getArtifact();
238
239                buffer.append( art );
240                buffer.append( ':' ).append( dep.getScope() );
241            }
242
243            logger.debug( buffer.toString() );
244            indent += "   ";
245            return true;
246        }
247
248        public boolean visitLeave( DependencyNode node )
249        {
250            indent = indent.substring( 0, indent.length() - 3 );
251            return true;
252        }
253
254    }
255
256}