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