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.api.services;
20  
21  import java.util.ArrayList;
22  import java.util.Collection;
23  import java.util.Collections;
24  import java.util.List;
25  import java.util.Optional;
26  
27  import org.apache.maven.api.Artifact;
28  import org.apache.maven.api.DependencyCoordinate;
29  import org.apache.maven.api.Project;
30  import org.apache.maven.api.Session;
31  import org.apache.maven.api.annotations.Experimental;
32  import org.apache.maven.api.annotations.Immutable;
33  import org.apache.maven.api.annotations.Nonnull;
34  import org.apache.maven.api.annotations.NotThreadSafe;
35  import org.apache.maven.api.annotations.Nullable;
36  
37  import static org.apache.maven.api.services.BaseRequest.nonNull;
38  
39  /**
40   * A request to collect the transitive dependencies and to build a dependency graph from them. There are three ways to
41   * create a dependency graph. First, only the root dependency can be given. Second, a root dependency and direct
42   * dependencies can be specified in which case the specified direct dependencies are merged with the direct dependencies
43   * retrieved from the artifact descriptor of the root dependency. And last, only direct dependencies can be specified in
44   * which case the root node of the resulting graph has no associated dependency.
45   *
46   * @since 4.0
47   * @see DependencyCollector#collect(DependencyCollectorRequest)
48   */
49  @Experimental
50  @Immutable
51  public interface DependencyCollectorRequest {
52  
53      @Nonnull
54      Session getSession();
55  
56      @Nonnull
57      Optional<Artifact> getRootArtifact();
58  
59      @Nonnull
60      Optional<DependencyCoordinate> getRoot();
61  
62      @Nonnull
63      Collection<DependencyCoordinate> getDependencies();
64  
65      @Nonnull
66      Collection<DependencyCoordinate> getManagedDependencies();
67  
68      boolean getVerbose();
69  
70      @Nonnull
71      static DependencyCollectorRequest build(@Nonnull Session session, Artifact root) {
72          return builder()
73                  .session(nonNull(session, "session cannot be null"))
74                  .rootArtifact(nonNull(root, "root cannot be null"))
75                  .build();
76      }
77  
78      @Nonnull
79      static DependencyCollectorRequest build(@Nonnull Session session, @Nonnull DependencyCoordinate root) {
80          return builder()
81                  .session(nonNull(session, "session cannot be null"))
82                  .root(nonNull(root, "root cannot be null"))
83                  .build();
84      }
85  
86      @Nonnull
87      static DependencyCollectorRequest build(@Nonnull Session session, @Nonnull Project project) {
88          return builder()
89                  .session(nonNull(session, "session cannot be null"))
90                  .rootArtifact(nonNull(project, "project cannot be null").getArtifact())
91                  .dependencies(project.getDependencies())
92                  .managedDependencies(project.getManagedDependencies())
93                  .build();
94      }
95  
96      @Nonnull
97      static DependencyCollectorRequestBuilder builder() {
98          return new DependencyCollectorRequestBuilder();
99      }
100 
101     @NotThreadSafe
102     class DependencyCollectorRequestBuilder {
103 
104         Session session;
105         Artifact rootArtifact;
106         DependencyCoordinate root;
107         List<DependencyCoordinate> dependencies = Collections.emptyList();
108         List<DependencyCoordinate> managedDependencies = Collections.emptyList();
109         boolean verbose;
110 
111         DependencyCollectorRequestBuilder() {}
112 
113         @Nonnull
114         public DependencyCollectorRequestBuilder session(@Nonnull Session session) {
115             this.session = session;
116             return this;
117         }
118 
119         /**
120          * Sets the root artifact for the dependency graph.
121          * This must not be confused with {@link #root(DependencyCoordinate)}: The root <em>dependency</em>, like any
122          * other specified dependency, will be subject to dependency collection/resolution, i.e. should have an artifact
123          * descriptor and a corresponding artifact file. The root <em>artifact</em> on the other hand is only used
124          * as a label for the root node of the graph in case no root dependency was specified. As such, the configured
125          * root artifact is ignored if {@link #root(DependencyCoordinate)} has been set.
126          *
127          * @param rootArtifact the root artifact for the dependency graph, may be {@code null}
128          * @return this request for chaining, never {@code null}
129          */
130         @Nonnull
131         public DependencyCollectorRequestBuilder rootArtifact(@Nullable Artifact rootArtifact) {
132             this.rootArtifact = rootArtifact;
133             return this;
134         }
135 
136         /**
137          * @param root The root dependency
138          * @return this request for chaining, never {@code null}
139          */
140         @Nonnull
141         public DependencyCollectorRequestBuilder root(@Nonnull DependencyCoordinate root) {
142             this.root = root;
143             return this;
144         }
145 
146         /**
147          * Sets the direct dependencies. If both a root dependency and direct dependencies are given in the request, the
148          * direct dependencies from the request will be merged with the direct dependencies from the root dependency's
149          * artifact descriptor, giving higher priority to the dependencies from the request.
150          *
151          * @param dependencies the direct dependencies, may be {@code null}
152          * @return this request for chaining, never {@code null}
153          */
154         @Nonnull
155         public DependencyCollectorRequestBuilder dependencies(@Nullable List<DependencyCoordinate> dependencies) {
156             this.dependencies = (dependencies != null) ? dependencies : Collections.emptyList();
157             return this;
158         }
159 
160         /**
161          * Adds the specified direct dependency.
162          *
163          * @param dependency the dependency to add, may be {@code null}
164          * @return this request for chaining, never {@code null}
165          */
166         @Nonnull
167         public DependencyCollectorRequestBuilder dependency(@Nullable DependencyCoordinate dependency) {
168             if (dependency != null) {
169                 if (this.dependencies.isEmpty()) {
170                     this.dependencies = new ArrayList<>();
171                 }
172                 this.dependencies.add(dependency);
173             }
174             return this;
175         }
176 
177         /**
178          * Sets the dependency management to apply to transitive dependencies. To clarify, this management does not
179          * apply to
180          * the direct dependencies of the root node.
181          *
182          * @param managedDependencies the dependency management, may be {@code null}
183          * @return this request for chaining, never {@code null}
184          */
185         @Nonnull
186         public DependencyCollectorRequestBuilder managedDependencies(
187                 @Nullable List<DependencyCoordinate> managedDependencies) {
188             this.managedDependencies = (managedDependencies != null) ? managedDependencies : Collections.emptyList();
189             return this;
190         }
191 
192         /**
193          * Adds the specified managed dependency.
194          *
195          * @param managedDependency The managed dependency to add, may be {@code null} in which case the call
196          *                          will have no effect.
197          * @return this request for chaining, never {@code null}
198          */
199         @Nonnull
200         public DependencyCollectorRequestBuilder managedDependency(@Nullable DependencyCoordinate managedDependency) {
201             if (managedDependency != null) {
202                 if (this.managedDependencies.isEmpty()) {
203                     this.managedDependencies = new ArrayList<>();
204                 }
205                 this.managedDependencies.add(managedDependency);
206             }
207             return this;
208         }
209 
210         /**
211          * Specifies that the collection should be verbose.
212          *
213          * @param verbose whether the collection should be verbose or not
214          * @return this request for chaining, never {@code null}
215          */
216         @Nonnull
217         public DependencyCollectorRequestBuilder verbose(boolean verbose) {
218             this.verbose = verbose;
219             return this;
220         }
221 
222         @Nonnull
223         public DependencyCollectorRequest build() {
224             return new DefaultDependencyCollectorRequest(
225                     session, rootArtifact, root, dependencies, managedDependencies, verbose);
226         }
227 
228         static class DefaultDependencyCollectorRequest extends BaseRequest implements DependencyCollectorRequest {
229             private final Artifact rootArtifact;
230             private final DependencyCoordinate root;
231             private final Collection<DependencyCoordinate> dependencies;
232             private final Collection<DependencyCoordinate> managedDependencies;
233             private final boolean verbose;
234 
235             /**
236              * Creates a request with the specified properties.
237              *
238              * @param session      {@link Session}
239              * @param rootArtifact The root dependency whose transitive dependencies should be collected, may be {@code
240              *                     null}.
241              */
242             DefaultDependencyCollectorRequest(
243                     @Nonnull Session session,
244                     @Nullable Artifact rootArtifact,
245                     @Nullable DependencyCoordinate root,
246                     @Nonnull Collection<DependencyCoordinate> dependencies,
247                     @Nonnull Collection<DependencyCoordinate> managedDependencies,
248                     boolean verbose) {
249                 super(session);
250                 this.rootArtifact = rootArtifact;
251                 this.root = root;
252                 this.dependencies = unmodifiable(nonNull(dependencies, "dependencies cannot be null"));
253                 this.managedDependencies =
254                         unmodifiable(nonNull(managedDependencies, "managedDependencies cannot be null"));
255                 this.verbose = verbose;
256             }
257 
258             @Nonnull
259             @Override
260             public Optional<Artifact> getRootArtifact() {
261                 return Optional.ofNullable(rootArtifact);
262             }
263 
264             @Nonnull
265             @Override
266             public Optional<DependencyCoordinate> getRoot() {
267                 return Optional.ofNullable(root);
268             }
269 
270             @Nonnull
271             @Override
272             public Collection<DependencyCoordinate> getDependencies() {
273                 return dependencies;
274             }
275 
276             @Nonnull
277             @Override
278             public Collection<DependencyCoordinate> getManagedDependencies() {
279                 return managedDependencies;
280             }
281 
282             @Override
283             public boolean getVerbose() {
284                 return verbose;
285             }
286 
287             @Nonnull
288             @Override
289             public String toString() {
290                 return getRoot() + " -> " + getDependencies();
291             }
292         }
293     }
294 }