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