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 javax.inject.Inject;
22  import javax.inject.Named;
23  import javax.inject.Singleton;
24  
25  import java.util.LinkedHashMap;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.stream.Collectors;
29  import java.util.stream.Stream;
30  
31  import org.apache.maven.RepositoryUtils;
32  import org.apache.maven.model.Dependency;
33  import org.apache.maven.model.Plugin;
34  import org.apache.maven.plugin.PluginResolutionException;
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.DependencyCollectionChecker;
43  import org.eclipse.aether.collection.DependencyCollectionException;
44  import org.eclipse.aether.collection.DependencySelector;
45  import org.eclipse.aether.collection.VersionFilterBuilder;
46  import org.eclipse.aether.graph.DependencyFilter;
47  import org.eclipse.aether.graph.DependencyNode;
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.resolution.DependencyResult;
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.selector.AndDependencySelector;
61  import org.eclipse.aether.util.graph.visitor.DependencyGraphDumper;
62  import org.eclipse.aether.util.repository.SimpleArtifactDescriptorPolicy;
63  import org.slf4j.Logger;
64  import org.slf4j.LoggerFactory;
65  
66  /**
67   * Assists in resolving the dependencies of a plugin. <strong>Warning:</strong> This is an internal utility class that
68   * is only public for technical reasons, it is not part of the public API. In particular, this class can be changed or
69   * deleted without prior notice.
70   *
71   * @since 3.0
72   * @author Benjamin Bentmann
73   */
74  @Singleton
75  @Named
76  public class DefaultPluginDependenciesResolver implements PluginDependenciesResolver {
77  
78      private static final String REPOSITORY_CONTEXT = "plugin";
79  
80      private final Logger logger = LoggerFactory.getLogger(getClass());
81  
82      @Inject
83      private RepositorySystem repoSystem;
84  
85      @Inject
86      private List<MavenPluginDependenciesValidator> dependenciesValidators;
87  
88      private Artifact toArtifact(Plugin plugin, RepositorySystemSession session) {
89          return new DefaultArtifact(
90                  plugin.getGroupId(),
91                  plugin.getArtifactId(),
92                  null,
93                  "jar",
94                  plugin.getVersion(),
95                  session.getArtifactTypeRegistry().get("maven-plugin"));
96      }
97  
98      @Override
99      public Artifact resolve(Plugin plugin, List<RemoteRepository> repositories, RepositorySystemSession session)
100             throws PluginResolutionException {
101         RequestTrace trace = RequestTrace.newChild(null, plugin);
102 
103         Artifact pluginArtifact = toArtifact(plugin, session);
104 
105         try {
106             DefaultRepositorySystemSession pluginSession = new DefaultRepositorySystemSession(session);
107             pluginSession.setArtifactDescriptorPolicy(new SimpleArtifactDescriptorPolicy(true, false));
108 
109             ArtifactDescriptorRequest request =
110                     new ArtifactDescriptorRequest(pluginArtifact, repositories, REPOSITORY_CONTEXT);
111             request.setTrace(trace);
112             ArtifactDescriptorResult result = repoSystem.readArtifactDescriptor(pluginSession, request);
113 
114             for (MavenPluginDependenciesValidator dependenciesValidator : dependenciesValidators) {
115                 dependenciesValidator.validate(session, pluginArtifact, result);
116             }
117 
118             pluginArtifact = result.getArtifact();
119 
120             if (logger.isWarnEnabled()) {
121                 if (!result.getRelocations().isEmpty()) {
122                     String message = pluginArtifact instanceof org.apache.maven.repository.internal.RelocatedArtifact
123                             ? ((org.apache.maven.repository.internal.RelocatedArtifact) pluginArtifact).getMessage()
124                             : null;
125                     logger.warn("The artifact " + result.getRelocations().get(0) + " has been relocated to "
126                             + pluginArtifact + (message != null ? ": " + message : ""));
127                 }
128             }
129 
130             String requiredMavenVersion = (String) result.getProperties().get("prerequisites.maven");
131             if (requiredMavenVersion != null) {
132                 Map<String, String> props = new LinkedHashMap<>(pluginArtifact.getProperties());
133                 props.put("requiredMavenVersion", requiredMavenVersion);
134                 pluginArtifact = pluginArtifact.setProperties(props);
135             }
136         } catch (ArtifactDescriptorException e) {
137             throw new PluginResolutionException(
138                     plugin, e.getResult().getExceptions(), logger.isDebugEnabled() ? e : null);
139         }
140 
141         try {
142             ArtifactRequest request = new ArtifactRequest(pluginArtifact, repositories, REPOSITORY_CONTEXT);
143             request.setTrace(trace);
144             pluginArtifact = repoSystem.resolveArtifact(session, request).getArtifact();
145         } catch (ArtifactResolutionException e) {
146             throw new PluginResolutionException(
147                     plugin, e.getResult().getExceptions(), logger.isDebugEnabled() ? e : null);
148         }
149 
150         return pluginArtifact;
151     }
152 
153     /**
154      * @since 3.3.0
155      * @deprecated Is unused since 3.10.0
156      */
157     @Deprecated
158     public DependencyNode resolveCoreExtension(
159             Plugin plugin,
160             DependencyFilter dependencyFilter,
161             List<RemoteRepository> repositories,
162             RepositorySystemSession session)
163             throws PluginResolutionException {
164         return resolveInternal(plugin, null /* pluginArtifact */, dependencyFilter, repositories, session)
165                 .getRoot();
166     }
167 
168     /**
169      * @since 3.10.0
170      */
171     @Override
172     public DependencyResult resolveCoreExtensionAndFlatten(
173             Plugin plugin,
174             DependencyFilter dependencyFilter,
175             List<RemoteRepository> repositories,
176             RepositorySystemSession session)
177             throws PluginResolutionException {
178         return resolveInternal(plugin, null /* pluginArtifact */, dependencyFilter, repositories, session);
179     }
180 
181     @Override
182     public DependencyNode resolve(
183             Plugin plugin,
184             Artifact pluginArtifact,
185             DependencyFilter dependencyFilter,
186             List<RemoteRepository> repositories,
187             RepositorySystemSession session)
188             throws PluginResolutionException {
189         return resolveInternal(plugin, pluginArtifact, dependencyFilter, repositories, session)
190                 .getRoot();
191     }
192 
193     @Override
194     public DependencyResult resolvePluginAndFlatten(
195             Plugin plugin,
196             Artifact pluginArtifact,
197             DependencyFilter dependencyFilter,
198             List<RemoteRepository> repositories,
199             RepositorySystemSession session)
200             throws PluginResolutionException {
201         return resolveInternal(plugin, pluginArtifact, dependencyFilter, repositories, session);
202     }
203 
204     private DependencyResult resolveInternal(
205             Plugin plugin,
206             Artifact pluginArtifact,
207             DependencyFilter dependencyFilter,
208             List<RemoteRepository> repositories,
209             RepositorySystemSession session)
210             throws PluginResolutionException {
211         RequestTrace trace = RequestTrace.newChild(null, plugin);
212 
213         if (pluginArtifact == null) {
214             pluginArtifact = toArtifact(plugin, session);
215         }
216 
217         DependencyFilter collectionFilter = new ScopeDependencyFilter("provided", "test");
218         DependencyFilter resolutionFilter = AndDependencyFilter.newInstance(collectionFilter, dependencyFilter);
219 
220         try {
221             DependencySelector selector =
222                     AndDependencySelector.newInstance(session.getDependencySelector(), new WagonExcluder());
223 
224             DefaultRepositorySystemSession pluginSession = new DefaultRepositorySystemSession(session);
225             pluginSession.setConfigProperty(VersionFilterBuilder.VERSION_FILTER_SUPPRESSED, Boolean.TRUE.toString());
226             pluginSession.setConfigProperty(
227                     DependencyCollectionChecker.COLLECTOR_CHECKER_SUPPRESSED, Boolean.TRUE.toString());
228             pluginSession.setDependencySelector(selector);
229             pluginSession.setDependencyGraphTransformer(session.getDependencyGraphTransformer());
230 
231             CollectRequest request = new CollectRequest();
232             request.setRequestContext(REPOSITORY_CONTEXT);
233             request.setRepositories(repositories);
234             request.setRoot(new org.eclipse.aether.graph.Dependency(pluginArtifact, null));
235             for (Dependency dependency : plugin.getDependencies()) {
236                 org.eclipse.aether.graph.Dependency pluginDep =
237                         RepositoryUtils.toDependency(dependency, session.getArtifactTypeRegistry());
238                 if (!JavaScopes.SYSTEM.equals(pluginDep.getScope())) {
239                     pluginDep = pluginDep.setScope(JavaScopes.RUNTIME);
240                 }
241                 request.addDependency(pluginDep);
242             }
243 
244             DependencyRequest depRequest = new DependencyRequest(request, resolutionFilter);
245             depRequest.setTrace(trace);
246 
247             request.setTrace(RequestTrace.newChild(trace, depRequest));
248 
249             DependencyNode node =
250                     repoSystem.collectDependencies(pluginSession, request).getRoot();
251 
252             if (logger.isDebugEnabled()) {
253                 node.accept(new DependencyGraphDumper(logger::debug));
254             }
255 
256             depRequest.setRoot(node);
257             return repoSystem.resolveDependencies(session, depRequest);
258 
259         } catch (DependencyCollectionException e) {
260             throw new PluginResolutionException(
261                     plugin, e.getResult().getExceptions(), logger.isDebugEnabled() ? e : null);
262         } catch (DependencyResolutionException e) {
263             List<Exception> exceptions = Stream.concat(
264                             e.getResult().getCollectExceptions().stream(),
265                             e.getResult().getArtifactResults().stream()
266                                     .filter(r -> !r.isResolved())
267                                     .flatMap(r -> r.getExceptions().stream()))
268                     .collect(Collectors.toList());
269             throw new PluginResolutionException(plugin, exceptions, logger.isDebugEnabled() ? e : null);
270         }
271     }
272 }