View Javadoc
1   package org.apache.maven.plugin.internal;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.util.Collection;
23  import java.util.LinkedHashMap;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.Objects;
27  
28  import javax.inject.Inject;
29  import javax.inject.Named;
30  import javax.inject.Singleton;
31  
32  import org.apache.maven.RepositoryUtils;
33  import org.apache.maven.model.Dependency;
34  import org.apache.maven.model.Plugin;
35  import org.apache.maven.plugin.PluginResolutionException;
36  import org.codehaus.plexus.util.StringUtils;
37  import org.eclipse.aether.DefaultRepositorySystemSession;
38  import org.eclipse.aether.RepositorySystem;
39  import org.eclipse.aether.RepositorySystemSession;
40  import org.eclipse.aether.RequestTrace;
41  import org.eclipse.aether.artifact.Artifact;
42  import org.eclipse.aether.artifact.DefaultArtifact;
43  import org.eclipse.aether.collection.CollectRequest;
44  import org.eclipse.aether.collection.DependencyCollectionException;
45  import org.eclipse.aether.graph.DependencyFilter;
46  import org.eclipse.aether.graph.DependencyNode;
47  import org.eclipse.aether.graph.DependencyVisitor;
48  import org.eclipse.aether.repository.RemoteRepository;
49  import org.eclipse.aether.resolution.ArtifactDescriptorException;
50  import org.eclipse.aether.resolution.ArtifactDescriptorRequest;
51  import org.eclipse.aether.resolution.ArtifactDescriptorResult;
52  import org.eclipse.aether.resolution.ArtifactRequest;
53  import org.eclipse.aether.resolution.ArtifactResolutionException;
54  import org.eclipse.aether.resolution.DependencyRequest;
55  import org.eclipse.aether.resolution.DependencyResolutionException;
56  import org.eclipse.aether.util.artifact.JavaScopes;
57  import org.eclipse.aether.util.filter.AndDependencyFilter;
58  import org.eclipse.aether.util.filter.ScopeDependencyFilter;
59  import org.eclipse.aether.util.graph.manager.DependencyManagerUtils;
60  import org.eclipse.aether.util.repository.SimpleArtifactDescriptorPolicy;
61  import org.slf4j.Logger;
62  import org.slf4j.LoggerFactory;
63  
64  /**
65   * Assists in resolving the dependencies of a plugin. <strong>Warning:</strong> This is an internal utility class that
66   * is only public for technical reasons, it is not part of the public API. In particular, this class can be changed or
67   * deleted without prior notice.
68   *
69   * @since 3.0
70   * @author Benjamin Bentmann
71   */
72  @Named
73  @Singleton
74  public class DefaultPluginDependenciesResolver
75      implements PluginDependenciesResolver
76  {
77      private static final String REPOSITORY_CONTEXT = "plugin";
78  
79      private final Logger logger = LoggerFactory.getLogger( getClass() );
80  
81      private final RepositorySystem repoSystem;
82  
83      @Inject
84      public DefaultPluginDependenciesResolver( RepositorySystem repoSystem )
85      {
86          this.repoSystem = repoSystem;
87      }
88  
89      private Artifact toArtifact( Plugin plugin, RepositorySystemSession session )
90      {
91          return new DefaultArtifact( plugin.getGroupId(), plugin.getArtifactId(), null, "jar", plugin.getVersion(),
92                                      session.getArtifactTypeRegistry().get( "maven-plugin" ) );
93      }
94  
95      public Artifact resolve( Plugin plugin, List<RemoteRepository> repositories, RepositorySystemSession session )
96          throws PluginResolutionException
97      {
98          RequestTrace trace = RequestTrace.newChild( null, plugin );
99  
100         Artifact pluginArtifact = toArtifact( plugin, session );
101 
102         try
103         {
104             DefaultRepositorySystemSession pluginSession = new DefaultRepositorySystemSession( session );
105             pluginSession.setArtifactDescriptorPolicy( new SimpleArtifactDescriptorPolicy( true, false ) );
106 
107             ArtifactDescriptorRequest request =
108                 new ArtifactDescriptorRequest( pluginArtifact, repositories, REPOSITORY_CONTEXT );
109             request.setTrace( trace );
110             ArtifactDescriptorResult result = repoSystem.readArtifactDescriptor( pluginSession, request );
111 
112             pluginArtifact = result.getArtifact();
113 
114             if ( logger.isWarnEnabled() )
115             {
116                 if ( !result.getRelocations().isEmpty() )
117                 {
118                     String message = pluginArtifact instanceof org.apache.maven.repository.internal.RelocatedArtifact
119                             ? ( ( org.apache.maven.repository.internal.RelocatedArtifact ) pluginArtifact ).getMessage()
120                             : null;
121                     logger.warn( "The artifact " + result.getRelocations().get( 0 ) + " has been relocated to "
122                             + pluginArtifact + ( message != null ? ": " + message : "" ) );
123                 }
124             }
125 
126             String requiredMavenVersion = (String) result.getProperties().get( "prerequisites.maven" );
127             if ( requiredMavenVersion != null )
128             {
129                 Map<String, String> props = new LinkedHashMap<>( pluginArtifact.getProperties() );
130                 props.put( "requiredMavenVersion", requiredMavenVersion );
131                 pluginArtifact = pluginArtifact.setProperties( props );
132             }
133         }
134         catch ( ArtifactDescriptorException e )
135         {
136             throw new PluginResolutionException( plugin, e );
137         }
138 
139         try
140         {
141             ArtifactRequest request = new ArtifactRequest( pluginArtifact, repositories, REPOSITORY_CONTEXT );
142             request.setTrace( trace );
143             pluginArtifact = repoSystem.resolveArtifact( session, request ).getArtifact();
144         }
145         catch ( ArtifactResolutionException e )
146         {
147             throw new PluginResolutionException( plugin, e );
148         }
149 
150         return pluginArtifact;
151     }
152 
153     /**
154      * @since 3.3.0
155      */
156     public DependencyNode resolveCoreExtension( Plugin plugin, DependencyFilter dependencyFilter,
157                                                 List<RemoteRepository> repositories, RepositorySystemSession session )
158         throws PluginResolutionException
159     {
160         return resolveInternal( plugin, null /* pluginArtifact */, dependencyFilter,
161                                 repositories, session );
162     }
163 
164     public DependencyNode resolve( Plugin plugin, Artifact pluginArtifact, DependencyFilter dependencyFilter,
165                                    List<RemoteRepository> repositories, RepositorySystemSession session )
166         throws PluginResolutionException
167     {
168         return resolveInternal( plugin, pluginArtifact, dependencyFilter, repositories,
169                                 session );
170     }
171 
172     private DependencyNode resolveInternal( Plugin plugin, Artifact pluginArtifact, DependencyFilter dependencyFilter,
173                                             List<RemoteRepository> repositories, RepositorySystemSession session )
174         throws PluginResolutionException
175     {
176         RequestTrace trace = RequestTrace.newChild( null, plugin );
177 
178         if ( pluginArtifact == null )
179         {
180             pluginArtifact = toArtifact( plugin, session );
181         }
182 
183         DependencyFilter collectionFilter = new ScopeDependencyFilter( "provided", "test" );
184         DependencyFilter resolutionFilter = AndDependencyFilter.newInstance( collectionFilter, dependencyFilter );
185 
186         DependencyNode node;
187 
188         try
189         {
190             DefaultRepositorySystemSession pluginSession = new DefaultRepositorySystemSession( session );
191             pluginSession.setDependencySelector( session.getDependencySelector() );
192             pluginSession.setDependencyGraphTransformer( session.getDependencyGraphTransformer() );
193 
194             CollectRequest request = new CollectRequest();
195             request.setRequestContext( REPOSITORY_CONTEXT );
196             request.setRepositories( repositories );
197             request.setRoot( new org.eclipse.aether.graph.Dependency( pluginArtifact, null ) );
198             for ( Dependency dependency : plugin.getDependencies() )
199             {
200                 org.eclipse.aether.graph.Dependency pluginDep =
201                     RepositoryUtils.toDependency( dependency, session.getArtifactTypeRegistry() );
202                 if ( !JavaScopes.SYSTEM.equals( pluginDep.getScope() ) )
203                 {
204                     pluginDep = pluginDep.setScope( JavaScopes.RUNTIME );
205                 }
206                 request.addDependency( pluginDep );
207             }
208 
209             DependencyRequest depRequest = new DependencyRequest( request, resolutionFilter );
210             depRequest.setTrace( trace );
211 
212             request.setTrace( RequestTrace.newChild( trace, depRequest ) );
213 
214             node = repoSystem.collectDependencies( pluginSession, request ).getRoot();
215 
216             if ( logger.isDebugEnabled() )
217             {
218                 node.accept( new GraphLogger() );
219             }
220 
221             depRequest.setRoot( node );
222             repoSystem.resolveDependencies( session, depRequest );
223         }
224         catch ( DependencyCollectionException e )
225         {
226             throw new PluginResolutionException( plugin, e );
227         }
228         catch ( DependencyResolutionException e )
229         {
230             throw new PluginResolutionException( plugin, e.getCause() );
231         }
232 
233         return node;
234     }
235 
236     // Keep this class in sync with org.apache.maven.project.DefaultProjectDependenciesResolver.GraphLogger
237     class GraphLogger
238         implements DependencyVisitor
239     {
240 
241         private String indent = "";
242 
243         public boolean visitEnter( DependencyNode node )
244         {
245             StringBuilder buffer = new StringBuilder( 128 );
246             buffer.append( indent );
247             org.eclipse.aether.graph.Dependency dep = node.getDependency();
248             if ( dep != null )
249             {
250                 org.eclipse.aether.artifact.Artifact art = dep.getArtifact();
251 
252                 buffer.append( art );
253                 if ( StringUtils.isNotEmpty( dep.getScope() ) )
254                 {
255                     buffer.append( ':' ).append( dep.getScope() );
256                 }
257 
258                 if ( dep.isOptional() )
259                 {
260                     buffer.append( " (optional)" );
261                 }
262 
263                 // TODO We currently cannot tell which <dependencyManagement> section contained the management
264                 //      information. When the resolver provides this information, these log messages should be updated
265                 //      to contain it.
266                 if ( ( node.getManagedBits() & DependencyNode.MANAGED_SCOPE ) == DependencyNode.MANAGED_SCOPE )
267                 {
268                     final String premanagedScope = DependencyManagerUtils.getPremanagedScope( node );
269                     buffer.append( " (scope managed from " );
270                     buffer.append( Objects.toString( premanagedScope, "default" ) );
271                     buffer.append( ')' );
272                 }
273 
274                 if ( ( node.getManagedBits() & DependencyNode.MANAGED_VERSION ) == DependencyNode.MANAGED_VERSION )
275                 {
276                     final String premanagedVersion = DependencyManagerUtils.getPremanagedVersion( node );
277                     buffer.append( " (version managed from " );
278                     buffer.append( Objects.toString( premanagedVersion, "default" ) );
279                     buffer.append( ')' );
280                 }
281 
282                 if ( ( node.getManagedBits() & DependencyNode.MANAGED_OPTIONAL ) == DependencyNode.MANAGED_OPTIONAL )
283                 {
284                     final Boolean premanagedOptional = DependencyManagerUtils.getPremanagedOptional( node );
285                     buffer.append( " (optionality managed from " );
286                     buffer.append( Objects.toString( premanagedOptional, "default" ) );
287                     buffer.append( ')' );
288                 }
289 
290                 if ( ( node.getManagedBits() & DependencyNode.MANAGED_EXCLUSIONS )
291                          == DependencyNode.MANAGED_EXCLUSIONS )
292                 {
293                     final Collection<org.eclipse.aether.graph.Exclusion> premanagedExclusions =
294                         DependencyManagerUtils.getPremanagedExclusions( node );
295 
296                     buffer.append( " (exclusions managed from " );
297                     buffer.append( Objects.toString( premanagedExclusions, "default" ) );
298                     buffer.append( ')' );
299                 }
300 
301                 if ( ( node.getManagedBits() & DependencyNode.MANAGED_PROPERTIES )
302                          == DependencyNode.MANAGED_PROPERTIES )
303                 {
304                     final Map<String, String> premanagedProperties =
305                         DependencyManagerUtils.getPremanagedProperties( node );
306 
307                     buffer.append( " (properties managed from " );
308                     buffer.append( Objects.toString( premanagedProperties, "default" ) );
309                     buffer.append( ')' );
310                 }
311             }
312 
313             logger.debug( buffer.toString() );
314             indent += "   ";
315             return true;
316         }
317 
318         public boolean visitLeave( DependencyNode node )
319         {
320             indent = indent.substring( 0, indent.length() - 3 );
321             return true;
322         }
323 
324     }
325 
326 }