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