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  
29  import org.apache.maven.RepositoryUtils;
30  import org.apache.maven.api.DependencyScope;
31  import org.apache.maven.model.Dependency;
32  import org.apache.maven.model.Plugin;
33  import org.apache.maven.plugin.PluginResolutionException;
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.repository.RemoteRepository;
45  import org.eclipse.aether.resolution.ArtifactDescriptorException;
46  import org.eclipse.aether.resolution.ArtifactDescriptorRequest;
47  import org.eclipse.aether.resolution.ArtifactDescriptorResult;
48  import org.eclipse.aether.resolution.ArtifactRequest;
49  import org.eclipse.aether.resolution.ArtifactResolutionException;
50  import org.eclipse.aether.resolution.DependencyRequest;
51  import org.eclipse.aether.resolution.DependencyResolutionException;
52  import org.eclipse.aether.resolution.DependencyResult;
53  import org.eclipse.aether.util.filter.AndDependencyFilter;
54  import org.eclipse.aether.util.filter.ScopeDependencyFilter;
55  import org.eclipse.aether.util.graph.visitor.DependencyGraphDumper;
56  import org.eclipse.aether.util.repository.SimpleArtifactDescriptorPolicy;
57  import org.slf4j.Logger;
58  import org.slf4j.LoggerFactory;
59  
60  /**
61   * Assists in resolving the dependencies of a plugin. <strong>Warning:</strong> This is an internal utility class that
62   * is only public for technical reasons, it is not part of the public API. In particular, this class can be changed or
63   * deleted without prior notice.
64   *
65   * @since 3.0
66   */
67  @Named
68  @Singleton
69  public class DefaultPluginDependenciesResolver implements PluginDependenciesResolver {
70      private static final String REPOSITORY_CONTEXT = "plugin";
71  
72      private final Logger logger = LoggerFactory.getLogger(getClass());
73  
74      private final RepositorySystem repoSystem;
75  
76      private final List<MavenPluginDependenciesValidator> dependenciesValidators;
77  
78      @Inject
79      public DefaultPluginDependenciesResolver(
80              RepositorySystem repoSystem, List<MavenPluginDependenciesValidator> dependenciesValidators) {
81          this.repoSystem = repoSystem;
82          this.dependenciesValidators = dependenciesValidators;
83      }
84  
85      private Artifact toArtifact(Plugin plugin, RepositorySystemSession session) {
86          return new DefaultArtifact(
87                  plugin.getGroupId(),
88                  plugin.getArtifactId(),
89                  null,
90                  "jar",
91                  plugin.getVersion(),
92                  session.getArtifactTypeRegistry().get("maven-plugin"));
93      }
94  
95      public Artifact resolve(Plugin plugin, List<RemoteRepository> repositories, RepositorySystemSession session)
96              throws PluginResolutionException {
97          RequestTrace trace = RequestTrace.newChild(null, plugin);
98  
99          Artifact pluginArtifact = toArtifact(plugin, session);
100 
101         try {
102             DefaultRepositorySystemSession pluginSession = new DefaultRepositorySystemSession(session);
103             pluginSession.setArtifactDescriptorPolicy(new SimpleArtifactDescriptorPolicy(true, false));
104 
105             ArtifactDescriptorRequest request =
106                     new ArtifactDescriptorRequest(pluginArtifact, repositories, REPOSITORY_CONTEXT);
107             request.setTrace(trace);
108             ArtifactDescriptorResult result = repoSystem.readArtifactDescriptor(pluginSession, request);
109 
110             for (MavenPluginDependenciesValidator dependenciesValidator : dependenciesValidators) {
111                 dependenciesValidator.validate(session, pluginArtifact, result);
112             }
113 
114             pluginArtifact = result.getArtifact();
115 
116             if (logger.isWarnEnabled() && !result.getRelocations().isEmpty()) {
117                 String message = pluginArtifact instanceof org.apache.maven.repository.internal.RelocatedArtifact
118                         ? ": " + ((org.apache.maven.repository.internal.RelocatedArtifact) pluginArtifact).getMessage()
119                         : "";
120                 logger.warn(
121                         "The artifact {} has been relocated to {}{}",
122                         result.getRelocations().get(0),
123                         pluginArtifact,
124                         message);
125             }
126 
127             String requiredMavenVersion = (String) result.getProperties().get("prerequisites.maven");
128             if (requiredMavenVersion != null) {
129                 Map<String, String> props = new LinkedHashMap<>(pluginArtifact.getProperties());
130                 props.put("requiredMavenVersion", requiredMavenVersion);
131                 pluginArtifact = pluginArtifact.setProperties(props);
132             }
133         } catch (ArtifactDescriptorException e) {
134             throw new PluginResolutionException(plugin, e);
135         }
136 
137         try {
138             ArtifactRequest request = new ArtifactRequest(pluginArtifact, repositories, REPOSITORY_CONTEXT);
139             request.setTrace(trace);
140             pluginArtifact = repoSystem.resolveArtifact(session, request).getArtifact();
141         } catch (ArtifactResolutionException e) {
142             throw new PluginResolutionException(plugin, e);
143         }
144 
145         return pluginArtifact;
146     }
147 
148     /**
149      * @since 3.3.0
150      */
151     public DependencyResult resolveCoreExtension(
152             Plugin plugin,
153             DependencyFilter dependencyFilter,
154             List<RemoteRepository> repositories,
155             RepositorySystemSession session)
156             throws PluginResolutionException {
157         return resolveInternal(plugin, null /* pluginArtifact */, dependencyFilter, repositories, session);
158     }
159 
160     public DependencyResult resolvePlugin(
161             Plugin plugin,
162             Artifact pluginArtifact,
163             DependencyFilter dependencyFilter,
164             List<RemoteRepository> repositories,
165             RepositorySystemSession session)
166             throws PluginResolutionException {
167         return resolveInternal(plugin, pluginArtifact, dependencyFilter, repositories, session);
168     }
169 
170     public DependencyNode resolve(
171             Plugin plugin,
172             Artifact pluginArtifact,
173             DependencyFilter dependencyFilter,
174             List<RemoteRepository> repositories,
175             RepositorySystemSession session)
176             throws PluginResolutionException {
177         return resolveInternal(plugin, pluginArtifact, dependencyFilter, repositories, session)
178                 .getRoot();
179     }
180 
181     private DependencyResult resolveInternal(
182             Plugin plugin,
183             Artifact pluginArtifact,
184             DependencyFilter dependencyFilter,
185             List<RemoteRepository> repositories,
186             RepositorySystemSession session)
187             throws PluginResolutionException {
188         RequestTrace trace = RequestTrace.newChild(null, plugin);
189 
190         if (pluginArtifact == null) {
191             pluginArtifact = toArtifact(plugin, session);
192         }
193 
194         DependencyFilter collectionFilter = new ScopeDependencyFilter("provided", "test");
195         DependencyFilter resolutionFilter = AndDependencyFilter.newInstance(collectionFilter, dependencyFilter);
196 
197         DependencyNode node;
198 
199         try {
200             DefaultRepositorySystemSession pluginSession = new DefaultRepositorySystemSession(session);
201             pluginSession.setDependencySelector(session.getDependencySelector());
202             pluginSession.setDependencyGraphTransformer(session.getDependencyGraphTransformer());
203 
204             CollectRequest request = new CollectRequest();
205             request.setRequestContext(REPOSITORY_CONTEXT);
206             request.setRepositories(repositories);
207             request.setRoot(new org.eclipse.aether.graph.Dependency(pluginArtifact, null));
208             for (Dependency dependency : plugin.getDependencies()) {
209                 org.eclipse.aether.graph.Dependency pluginDep =
210                         RepositoryUtils.toDependency(dependency, session.getArtifactTypeRegistry());
211                 if (!DependencyScope.SYSTEM.is(pluginDep.getScope())) {
212                     pluginDep = pluginDep.setScope(DependencyScope.RUNTIME.id());
213                 }
214                 request.addDependency(pluginDep);
215             }
216 
217             DependencyRequest depRequest = new DependencyRequest(request, resolutionFilter);
218             depRequest.setTrace(trace);
219 
220             request.setTrace(RequestTrace.newChild(trace, depRequest));
221 
222             node = repoSystem.collectDependencies(pluginSession, request).getRoot();
223 
224             if (logger.isDebugEnabled()) {
225                 node.accept(new DependencyGraphDumper(logger::debug));
226             }
227 
228             depRequest.setRoot(node);
229             return repoSystem.resolveDependencies(session, depRequest);
230         } catch (DependencyCollectionException e) {
231             throw new PluginResolutionException(plugin, e);
232         } catch (DependencyResolutionException e) {
233             throw new PluginResolutionException(plugin, e.getCause());
234         }
235     }
236 }