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.plugins.dependency.utils;
20  
21  import javax.inject.Inject;
22  import javax.inject.Named;
23  import javax.inject.Provider;
24  import javax.inject.Singleton;
25  
26  import java.util.ArrayList;
27  import java.util.Collection;
28  import java.util.Collections;
29  import java.util.LinkedHashSet;
30  import java.util.List;
31  import java.util.Objects;
32  import java.util.Optional;
33  import java.util.stream.Collectors;
34  
35  import org.apache.maven.RepositoryUtils;
36  import org.apache.maven.execution.MavenSession;
37  import org.apache.maven.model.ModelBase;
38  import org.apache.maven.model.Plugin;
39  import org.apache.maven.model.PluginContainer;
40  import org.apache.maven.model.ReportPlugin;
41  import org.apache.maven.model.Reporting;
42  import org.apache.maven.project.MavenProject;
43  import org.eclipse.aether.RepositorySystem;
44  import org.eclipse.aether.RepositorySystemSession;
45  import org.eclipse.aether.artifact.Artifact;
46  import org.eclipse.aether.artifact.ArtifactType;
47  import org.eclipse.aether.artifact.ArtifactTypeRegistry;
48  import org.eclipse.aether.artifact.DefaultArtifact;
49  import org.eclipse.aether.collection.CollectRequest;
50  import org.eclipse.aether.collection.CollectResult;
51  import org.eclipse.aether.collection.DependencyCollectionException;
52  import org.eclipse.aether.graph.Dependency;
53  import org.eclipse.aether.repository.RemoteRepository;
54  import org.eclipse.aether.repository.RepositoryPolicy;
55  import org.eclipse.aether.resolution.ArtifactRequest;
56  import org.eclipse.aether.resolution.ArtifactResolutionException;
57  import org.eclipse.aether.resolution.ArtifactResult;
58  import org.eclipse.aether.resolution.DependencyRequest;
59  import org.eclipse.aether.resolution.DependencyResolutionException;
60  import org.eclipse.aether.resolution.DependencyResult;
61  import org.eclipse.aether.util.graph.visitor.PreorderNodeListGenerator;
62  
63  /**
64   * Helper class for using Resolver API.
65   */
66  @Named
67  @Singleton
68  public class ResolverUtil {
69  
70      private final RepositorySystem repositorySystem;
71  
72      private final Provider<MavenSession> mavenSessionProvider;
73  
74      @Inject
75      public ResolverUtil(RepositorySystem repositorySystem, Provider<MavenSession> mavenSessionProvider) {
76          this.repositorySystem = repositorySystem;
77          this.mavenSessionProvider = mavenSessionProvider;
78      }
79  
80      /**
81       * Collects the transitive dependencies.
82       *
83       * @param root a root dependency for collections
84       * @return a resolved dependencies collections
85       */
86      public Collection<Dependency> collectDependencies(Dependency root) throws DependencyCollectionException {
87  
88          MavenSession session = mavenSessionProvider.get();
89  
90          CollectRequest request =
91                  new CollectRequest(root, session.getCurrentProject().getRemoteProjectRepositories());
92          CollectResult result = repositorySystem.collectDependencies(session.getRepositorySession(), request);
93  
94          PreorderNodeListGenerator nodeListGenerator = new PreorderNodeListGenerator();
95          result.getRoot().accept(nodeListGenerator);
96          return nodeListGenerator.getDependencies(true);
97      }
98  
99      /**
100      * Resolve given artifact.
101      *
102      * @param artifact     an artifact to resolve
103      * @param repositories remote repositories list
104      * @return resolved artifact
105      * @throws ArtifactResolutionException if the artifact could not be resolved
106      */
107     public Artifact resolveArtifact(Artifact artifact, List<RemoteRepository> repositories)
108             throws ArtifactResolutionException {
109         MavenSession session = mavenSessionProvider.get();
110         ArtifactRequest request = new ArtifactRequest(artifact, repositories, null);
111         ArtifactResult result = repositorySystem.resolveArtifact(session.getRepositorySession(), request);
112         return result.getArtifact();
113     }
114 
115     /**
116      * Resolve given plugin artifact.
117      *
118      * @param plugin a plugin to resolve
119      * @return resolved artifact
120      * @throws ArtifactResolutionException if the artifact could not be resolved
121      */
122     public Artifact resolvePlugin(Plugin plugin) throws ArtifactResolutionException {
123         MavenSession session = mavenSessionProvider.get();
124         Artifact artifact = toArtifact(plugin);
125         return resolveArtifact(artifact, session.getCurrentProject().getRemotePluginRepositories());
126     }
127 
128     /**
129      * Resolve transitive dependencies for artifact.
130      *
131      * @param artifact     an artifact to resolve
132      * @param repositories remote repositories list
133      * @return list of transitive dependencies for artifact
134      * @throws DependencyResolutionException if the dependency tree could not be built or any dependency artifact could
135      *                                       not be resolved
136      */
137     public List<Artifact> resolveDependencies(Artifact artifact, List<RemoteRepository> repositories)
138             throws DependencyResolutionException {
139         return resolveDependencies(artifact, null, repositories);
140     }
141 
142     /**
143      * Resolve transitive dependencies for artifact.
144      *
145      * @param artifact an artifact to resolve
146      * @param dependencies a list of additional dependencies for artifact
147      * @param repositories remote repositories list
148      * @return list of transitive dependencies for artifact
149      * @throws DependencyResolutionException if the dependency tree could not be built or any dependency artifact could
150      *                                       not be resolved
151      */
152     public List<Artifact> resolveDependencies(
153             Artifact artifact, List<Dependency> dependencies, List<RemoteRepository> repositories)
154             throws DependencyResolutionException {
155         MavenSession session = mavenSessionProvider.get();
156 
157         CollectRequest collectRequest = new CollectRequest(new Dependency(artifact, null), dependencies, repositories);
158         DependencyRequest request = new DependencyRequest(collectRequest, null);
159 
160         DependencyResult result = repositorySystem.resolveDependencies(session.getRepositorySession(), request);
161         return result.getArtifactResults().stream()
162                 .map(ArtifactResult::getArtifact)
163                 .collect(Collectors.toList());
164     }
165 
166     /**
167      * Resolve transitive dependencies for artifact with managed dependencies.
168      *
169      * @param rootArtifact a root artifact to resolve
170      * @param dependencies a list of dependencies for artifact
171      * @param managedDependencies  a list of managed dependencies for artifact
172      * @param remoteProjectRepositories remote repositories list
173      * @return Resolved dependencies
174      * @throws DependencyResolutionException if the dependency tree could not be built or any dependency artifact could
175      *                                       not be resolved
176      */
177     public List<Artifact> resolveDependenciesForArtifact(
178             Artifact rootArtifact,
179             List<Dependency> dependencies,
180             List<Dependency> managedDependencies,
181             List<RemoteRepository> remoteProjectRepositories)
182             throws DependencyResolutionException {
183         MavenSession session = mavenSessionProvider.get();
184 
185         CollectRequest collectRequest =
186                 new CollectRequest(dependencies, managedDependencies, remoteProjectRepositories);
187         collectRequest.setRootArtifact(rootArtifact);
188         DependencyRequest request = new DependencyRequest(collectRequest, null);
189         DependencyResult result = repositorySystem.resolveDependencies(session.getRepositorySession(), request);
190         return result.getArtifactResults().stream()
191                 .map(ArtifactResult::getArtifact)
192                 .collect(Collectors.toList());
193     }
194 
195     /**
196      * Resolve transitive dependencies for plugin.
197      *
198      * @param plugin aa plugin to resolve
199      * @return list of transitive dependencies for plugin
200      * @throws DependencyResolutionException if the dependency tree could not be built or any dependency artifact could
201      *                                       not be resolved
202      */
203     public List<Artifact> resolveDependencies(final Plugin plugin) throws DependencyResolutionException {
204 
205         MavenSession session = mavenSessionProvider.get();
206 
207         org.eclipse.aether.artifact.Artifact artifact = toArtifact(plugin);
208         List<Dependency> pluginDependencies = plugin.getDependencies().stream()
209                 .map(d -> RepositoryUtils.toDependency(
210                         d, session.getRepositorySession().getArtifactTypeRegistry()))
211                 .collect(Collectors.toList());
212 
213         return resolveDependencies(
214                 artifact, pluginDependencies, session.getCurrentProject().getRemoteProjectRepositories());
215     }
216 
217     private Artifact toArtifact(Plugin plugin) {
218         MavenSession session = mavenSessionProvider.get();
219         return new DefaultArtifact(
220                 plugin.getGroupId(),
221                 plugin.getArtifactId(),
222                 null,
223                 "jar",
224                 plugin.getVersion(),
225                 session.getRepositorySession().getArtifactTypeRegistry().get("maven-plugin"));
226     }
227 
228     /**
229      * Prepare a remote repositories list for given descriptions.
230      *
231      * @param repositories remote repositories descriptions
232      * @return a list of remote repositories
233      */
234     public List<RemoteRepository> remoteRepositories(List<String> repositories) {
235         MavenSession mavenSession = mavenSessionProvider.get();
236         List<RemoteRepository> projectRepositories =
237                 mavenSession.getCurrentProject().getRemoteProjectRepositories();
238         if (repositories == null || repositories.isEmpty()) {
239             return projectRepositories;
240         }
241 
242         List<RemoteRepository> repositoriesList =
243                 repositories.stream().map(this::prepareRemoteRepository).collect(Collectors.toList());
244         repositoriesList =
245                 repositorySystem.newResolutionRepositories(mavenSession.getRepositorySession(), repositoriesList);
246 
247         List<RemoteRepository> result = new ArrayList<>(projectRepositories);
248         result.addAll(repositoriesList);
249         return result;
250     }
251 
252     // protected for testing purpose
253     protected RemoteRepository prepareRemoteRepository(String repository) {
254         String[] items = Objects.requireNonNull(repository, "repository must be not null")
255                 .split("::");
256         String id = "temp";
257         String type = null;
258         String url;
259         switch (items.length) {
260             case 3:
261                 id = items[0];
262                 type = items[1];
263                 url = items[2];
264                 break;
265             case 2:
266                 id = items[0];
267                 url = items[1];
268                 break;
269             case 1:
270                 url = items[0];
271                 break;
272             default:
273                 throw new IllegalArgumentException("Invalid repository: " + repository);
274         }
275 
276         if (type == null || type.isEmpty()) {
277             type = "default";
278         }
279 
280         MavenSession mavenSession = mavenSessionProvider.get();
281         RepositorySystemSession repositorySession = mavenSession.getRepositorySession();
282 
283         String checksumPolicy = repositorySession.getChecksumPolicy();
284         if (checksumPolicy == null) {
285             checksumPolicy = RepositoryPolicy.CHECKSUM_POLICY_WARN;
286         }
287         String updatePolicy =
288                 mavenSession.getRequest().isUpdateSnapshots() ? RepositoryPolicy.UPDATE_POLICY_ALWAYS : null;
289         RepositoryPolicy repositoryPolicy = new RepositoryPolicy(true, updatePolicy, checksumPolicy);
290 
291         RemoteRepository.Builder builder = new RemoteRepository.Builder(id, type, url);
292         builder.setReleasePolicy(repositoryPolicy);
293         builder.setSnapshotPolicy(repositoryPolicy);
294 
295         return builder.build();
296     }
297 
298     /**
299      * Create an artifact based on configuration from Mojo.
300      *
301      * @param paramArtifact an artifact configuration
302      * @return new artifact
303      */
304     public Artifact createArtifactFromParams(ParamArtifact paramArtifact) {
305         Objects.requireNonNull(paramArtifact);
306         if (paramArtifact.getArtifact() != null) {
307             return createArtifactFromString(paramArtifact.getArtifact());
308         } else {
309             ArtifactType artifactType = getArtifactType(paramArtifact.getPackaging());
310             return new DefaultArtifact(
311                     paramArtifact.getGroupId(),
312                     paramArtifact.getArtifactId(),
313                     paramArtifact.getClassifier(),
314                     artifactType.getExtension(),
315                     paramArtifact.getVersion(),
316                     artifactType);
317         }
318     }
319 
320     private Artifact createArtifactFromString(String artifact) {
321         // groupId:artifactId:version[:packaging[:classifier]].
322         String[] items = artifact.split(":");
323         if (items.length < 3) {
324             throw new IllegalArgumentException("Invalid artifact format: " + artifact);
325         }
326 
327         ArtifactType artifactType = getArtifactType(items.length > 3 ? items[3] : null);
328         String classifier = items.length > 4 ? items[4] : null;
329 
330         return new DefaultArtifact(items[0], items[1], classifier, artifactType.getExtension(), items[2], artifactType);
331     }
332 
333     private ArtifactType getArtifactType(String packaging) {
334         ArtifactTypeRegistry artifactTypeRegistry =
335                 mavenSessionProvider.get().getRepositorySession().getArtifactTypeRegistry();
336         return artifactTypeRegistry.get(packaging != null ? packaging : "jar");
337     }
338 
339     /**
340      * Retrieve all plugins used in project either in build or reporting section.
341      *
342      * @param project a maven project
343      * @return a collection of plugins
344      */
345     public Collection<Plugin> getProjectPlugins(MavenProject project) {
346         List<Plugin> reportPlugins = Optional.ofNullable(project.getModel())
347                 .map(ModelBase::getReporting)
348                 .map(Reporting::getPlugins)
349                 .orElse(Collections.emptyList())
350                 .stream()
351                 .map(p -> toPlugin(p, project))
352                 .collect(Collectors.toList());
353 
354         List<Plugin> projectPlugins = project.getBuild().getPlugins();
355 
356         LinkedHashSet<Plugin> result = new LinkedHashSet<>(reportPlugins.size() + projectPlugins.size());
357         result.addAll(reportPlugins);
358         result.addAll(projectPlugins);
359         return result;
360     }
361 
362     private Plugin toPlugin(ReportPlugin reportPlugin, MavenProject project) {
363         // first look in the pluginManagement section
364         Plugin plugin = Optional.ofNullable(project.getBuild().getPluginManagement())
365                 .map(PluginContainer::getPluginsAsMap)
366                 .orElseGet(Collections::emptyMap)
367                 .get(reportPlugin.getKey());
368 
369         if (plugin == null) {
370             plugin = project.getBuild().getPluginsAsMap().get(reportPlugin.getKey());
371         }
372 
373         if (plugin == null) {
374             plugin = new Plugin();
375             plugin.setGroupId(reportPlugin.getGroupId());
376             plugin.setArtifactId(reportPlugin.getArtifactId());
377             plugin.setVersion(reportPlugin.getVersion());
378         } else {
379             // override the version with the one from the report plugin if specified
380             if (reportPlugin.getVersion() != null) {
381                 plugin.setVersion(reportPlugin.getVersion());
382             }
383         }
384 
385         if (plugin.getVersion() == null) {
386             plugin.setVersion("RELEASE");
387         }
388 
389         return plugin;
390     }
391 }