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.api.DependencyScope;
33  import org.apache.maven.model.Dependency;
34  import org.apache.maven.model.Plugin;
35  import org.apache.maven.plugin.PluginResolutionException;
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.graph.DependencyFilter;
45  import org.eclipse.aether.graph.DependencyNode;
46  import org.eclipse.aether.repository.RemoteRepository;
47  import org.eclipse.aether.resolution.ArtifactDescriptorException;
48  import org.eclipse.aether.resolution.ArtifactDescriptorRequest;
49  import org.eclipse.aether.resolution.ArtifactDescriptorResult;
50  import org.eclipse.aether.resolution.ArtifactRequest;
51  import org.eclipse.aether.resolution.ArtifactResolutionException;
52  import org.eclipse.aether.resolution.DependencyRequest;
53  import org.eclipse.aether.resolution.DependencyResolutionException;
54  import org.eclipse.aether.resolution.DependencyResult;
55  import org.eclipse.aether.util.filter.AndDependencyFilter;
56  import org.eclipse.aether.util.filter.ScopeDependencyFilter;
57  import org.eclipse.aether.util.graph.visitor.DependencyGraphDumper;
58  import org.eclipse.aether.util.repository.SimpleArtifactDescriptorPolicy;
59  import org.slf4j.Logger;
60  import org.slf4j.LoggerFactory;
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   */
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      private final List<MavenPluginDependenciesValidator> dependenciesValidators;
79  
80      @Inject
81      public DefaultPluginDependenciesResolver(
82              RepositorySystem repoSystem, List<MavenPluginDependenciesValidator> dependenciesValidators) {
83          this.repoSystem = repoSystem;
84          this.dependenciesValidators = dependenciesValidators;
85      }
86  
87      private Artifact toArtifact(Plugin plugin, RepositorySystemSession session) {
88          return new DefaultArtifact(
89                  plugin.getGroupId(),
90                  plugin.getArtifactId(),
91                  null,
92                  "jar",
93                  plugin.getVersion(),
94                  session.getArtifactTypeRegistry().get("maven-plugin"));
95      }
96  
97      public Artifact resolve(Plugin plugin, List<RemoteRepository> repositories, RepositorySystemSession session)
98              throws PluginResolutionException {
99          RequestTrace trace = RequestTrace.newChild(null, plugin);
100 
101         Artifact pluginArtifact = toArtifact(plugin, session);
102 
103         try {
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             for (MavenPluginDependenciesValidator dependenciesValidator : dependenciesValidators) {
113                 dependenciesValidator.validate(session, pluginArtifact, result);
114             }
115 
116             pluginArtifact = result.getArtifact();
117 
118             if (logger.isWarnEnabled() && !result.getRelocations().isEmpty()) {
119                 String message =
120                         pluginArtifact instanceof org.apache.maven.internal.impl.resolver.RelocatedArtifact relocated
121                                 ? ": " + relocated.getMessage()
122                                 : "";
123                 logger.warn(
124                         "The artifact {} has been relocated to {}{}",
125                         result.getRelocations().get(0),
126                         pluginArtifact,
127                         message);
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      */
156     public DependencyResult resolveCoreExtension(
157             Plugin plugin,
158             DependencyFilter dependencyFilter,
159             List<RemoteRepository> repositories,
160             RepositorySystemSession session)
161             throws PluginResolutionException {
162         return resolveInternal(plugin, null /* pluginArtifact */, dependencyFilter, repositories, session);
163     }
164 
165     public DependencyResult resolvePlugin(
166             Plugin plugin,
167             Artifact pluginArtifact,
168             DependencyFilter dependencyFilter,
169             List<RemoteRepository> repositories,
170             RepositorySystemSession session)
171             throws PluginResolutionException {
172         return resolveInternal(plugin, pluginArtifact, dependencyFilter, repositories, session);
173     }
174 
175     public DependencyNode resolve(
176             Plugin plugin,
177             Artifact pluginArtifact,
178             DependencyFilter dependencyFilter,
179             List<RemoteRepository> repositories,
180             RepositorySystemSession session)
181             throws PluginResolutionException {
182         return resolveInternal(plugin, pluginArtifact, dependencyFilter, repositories, session)
183                 .getRoot();
184     }
185 
186     private DependencyResult resolveInternal(
187             Plugin plugin,
188             Artifact pluginArtifact,
189             DependencyFilter dependencyFilter,
190             List<RemoteRepository> repositories,
191             RepositorySystemSession session)
192             throws PluginResolutionException {
193         RequestTrace trace = RequestTrace.newChild(null, plugin);
194 
195         if (pluginArtifact == null) {
196             pluginArtifact = toArtifact(plugin, session);
197         }
198 
199         DependencyFilter collectionFilter = new ScopeDependencyFilter("provided", "test");
200         DependencyFilter resolutionFilter = AndDependencyFilter.newInstance(collectionFilter, dependencyFilter);
201 
202         DependencyNode node;
203 
204         try {
205             DefaultRepositorySystemSession pluginSession = new DefaultRepositorySystemSession(session);
206             pluginSession.setDependencySelector(session.getDependencySelector());
207             pluginSession.setDependencyGraphTransformer(session.getDependencyGraphTransformer());
208 
209             CollectRequest request = new CollectRequest();
210             request.setRequestContext(REPOSITORY_CONTEXT);
211             request.setRepositories(repositories);
212             request.setRoot(new org.eclipse.aether.graph.Dependency(pluginArtifact, null));
213             for (Dependency dependency : plugin.getDependencies()) {
214                 org.eclipse.aether.graph.Dependency pluginDep =
215                         RepositoryUtils.toDependency(dependency, session.getArtifactTypeRegistry());
216                 if (!DependencyScope.SYSTEM.is(pluginDep.getScope())) {
217                     pluginDep = pluginDep.setScope(DependencyScope.RUNTIME.id());
218                 }
219                 request.addDependency(pluginDep);
220             }
221 
222             DependencyRequest depRequest = new DependencyRequest(request, resolutionFilter);
223             depRequest.setTrace(trace);
224 
225             request.setTrace(RequestTrace.newChild(trace, depRequest));
226 
227             node = repoSystem.collectDependencies(pluginSession, request).getRoot();
228 
229             if (logger.isDebugEnabled()) {
230                 node.accept(new DependencyGraphDumper(logger::debug));
231             }
232 
233             depRequest.setRoot(node);
234             return repoSystem.resolveDependencies(session, depRequest);
235         } catch (DependencyCollectionException e) {
236             throw new PluginResolutionException(
237                     plugin, e.getResult().getExceptions(), logger.isDebugEnabled() ? e : null);
238         } catch (DependencyResolutionException e) {
239             List<Exception> exceptions = Stream.concat(
240                             e.getResult().getCollectExceptions().stream(),
241                             e.getResult().getArtifactResults().stream()
242                                     .filter(r -> !r.isResolved())
243                                     .flatMap(r -> r.getExceptions().stream()))
244                     .collect(Collectors.toList());
245             throw new PluginResolutionException(plugin, exceptions, logger.isDebugEnabled() ? e : null);
246         }
247     }
248 }