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