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.fromConfiguration;
20  
21  import java.io.File;
22  import java.util.Collections;
23  import java.util.List;
24  import java.util.Objects;
25  
26  import org.apache.maven.artifact.Artifact;
27  import org.apache.maven.artifact.handler.ArtifactHandler;
28  import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
29  import org.apache.maven.model.Dependency;
30  import org.apache.maven.plugin.MojoExecutionException;
31  import org.apache.maven.plugin.MojoFailureException;
32  import org.apache.maven.plugins.annotations.Component;
33  import org.apache.maven.plugins.annotations.Parameter;
34  import org.apache.maven.plugins.dependency.AbstractDependencyMojo;
35  import org.apache.maven.plugins.dependency.utils.DependencyUtil;
36  import org.apache.maven.plugins.dependency.utils.StringUtils;
37  import org.apache.maven.plugins.dependency.utils.filters.ArtifactItemFilter;
38  import org.apache.maven.project.MavenProject;
39  import org.apache.maven.project.ProjectBuildingRequest;
40  import org.apache.maven.shared.artifact.filter.collection.ArtifactFilterException;
41  import org.apache.maven.shared.transfer.artifact.DefaultArtifactCoordinate;
42  import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResolver;
43  import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResolverException;
44  import org.apache.maven.shared.transfer.repository.RepositoryManager;
45  
46  /**
47   * Abstract parent class used by mojos that get Artifact information from the plugin configuration as an ArrayList of
48   * ArtifactItems
49   *
50   * @author <a href="mailto:brianf@apache.org">Brian Fox</a>
51   * @see ArtifactItem
52   */
53  public abstract class AbstractFromConfigurationMojo extends AbstractDependencyMojo {
54      /**
55       * Default output location used for mojo, unless overridden in ArtifactItem.
56       *
57       * @since 1.0
58       */
59      @Parameter(property = "outputDirectory", defaultValue = "${project.build.directory}/dependency")
60      private File outputDirectory;
61  
62      /**
63       * Overwrite release artifacts
64       *
65       * @since 1.0
66       */
67      @Parameter(property = "mdep.overWriteReleases", defaultValue = "false")
68      private boolean overWriteReleases;
69  
70      /**
71       * Overwrite snapshot artifacts
72       *
73       * @since 1.0
74       */
75      @Parameter(property = "mdep.overWriteSnapshots", defaultValue = "false")
76      private boolean overWriteSnapshots;
77  
78      /**
79       * Overwrite if newer
80       *
81       * @since 2.0
82       * @deprecated Use 'overWriteIfNewer' or 'mdep.overWriteIfNewer' as this does nothing now.
83       */
84      @Deprecated
85      @Parameter(property = "mdep.overIfNewer", defaultValue = "true")
86      private boolean overIfNewer;
87  
88      /**
89       * Overwrite if newer
90       *
91       * @since 3.7.0
92       */
93      @Parameter(property = "mdep.overWriteIfNewer", defaultValue = "true")
94      private boolean overWriteIfNewer;
95  
96      /**
97       * Collection of ArtifactItems to work on. (ArtifactItem contains groupId, artifactId, version, type, classifier,
98       * outputDirectory, destFileName, overWrite and encoding.) See <a href="./usage.html">Usage</a> for details.
99       *
100      * @since 1.0
101      */
102     @Parameter
103     private List<ArtifactItem> artifactItems;
104 
105     /**
106      * Path to override default local repository during plugin's execution. To remove all downloaded artifacts as part
107      * of the build, set this value to a location under your project's target directory
108      *
109      * @since 2.2
110      */
111     @Parameter
112     private File localRepositoryDirectory;
113 
114     @Component
115     private ArtifactResolver artifactResolver;
116 
117     @Component
118     private RepositoryManager repositoryManager;
119 
120     @Component
121     private ArtifactHandlerManager artifactHandlerManager;
122 
123     abstract ArtifactItemFilter getMarkedArtifactFilter(ArtifactItem item);
124 
125     /**
126      * artifactItems is filled by either field injection or by setArtifact().
127      *
128      * @throws MojoFailureException in case of an error.
129      */
130     protected void verifyRequirements() throws MojoFailureException {
131         if (artifactItems == null || artifactItems.isEmpty()) {
132             throw new MojoFailureException("Either artifact or artifactItems is required ");
133         }
134     }
135 
136     /**
137      * Preprocesses the list of ArtifactItems. This method defaults the outputDirectory if not set and creates the
138      * output Directory if it doesn't exist.
139      *
140      * @param processArtifactItemsRequest preprocessing instructions
141      * @return An ArrayList of preprocessed ArtifactItems
142      * @throws MojoExecutionException with a message if an error occurs.
143      * @see ArtifactItem
144      */
145     protected List<ArtifactItem> getProcessedArtifactItems(ProcessArtifactItemsRequest processArtifactItemsRequest)
146             throws MojoExecutionException {
147 
148         boolean removeVersion = processArtifactItemsRequest.isRemoveVersion();
149         boolean prependGroupId = processArtifactItemsRequest.isPrependGroupId();
150         boolean useBaseVersion = processArtifactItemsRequest.isUseBaseVersion();
151         boolean removeClassifier = processArtifactItemsRequest.isRemoveClassifier();
152 
153         if (artifactItems == null || artifactItems.isEmpty()) {
154             throw new MojoExecutionException("There are no artifactItems configured.");
155         }
156 
157         for (ArtifactItem artifactItem : artifactItems) {
158             this.getLog().info("Configured Artifact: " + artifactItem.toString());
159 
160             if (artifactItem.getOutputDirectory() == null) {
161                 artifactItem.setOutputDirectory(this.outputDirectory);
162             }
163             artifactItem.getOutputDirectory().mkdirs();
164 
165             // make sure we have a version.
166             if (StringUtils.isEmpty(artifactItem.getVersion())) {
167                 fillMissingArtifactVersion(artifactItem);
168             }
169 
170             artifactItem.setArtifact(this.getArtifact(artifactItem));
171 
172             if (StringUtils.isEmpty(artifactItem.getDestFileName())) {
173                 artifactItem.setDestFileName(DependencyUtil.getFormattedFileName(
174                         artifactItem.getArtifact(), removeVersion, prependGroupId, useBaseVersion, removeClassifier));
175             }
176 
177             try {
178                 artifactItem.setNeedsProcessing(checkIfProcessingNeeded(artifactItem));
179             } catch (ArtifactFilterException e) {
180                 throw new MojoExecutionException(e.getMessage(), e);
181             }
182         }
183         return artifactItems;
184     }
185 
186     private boolean checkIfProcessingNeeded(ArtifactItem item) throws ArtifactFilterException {
187         return Boolean.parseBoolean(item.getOverWrite())
188                 || getMarkedArtifactFilter(item).isArtifactIncluded(item);
189     }
190 
191     /**
192      * Resolves the Artifact from the remote repository if necessary. If no version is specified, it will be retrieved
193      * from the dependency list or from the DependencyManagement section of the pom.
194      *
195      * @param artifactItem containing information about artifact from plugin configuration
196      * @return Artifact object representing the specified file
197      * @throws MojoExecutionException if the version can't be found in DependencyManagement
198      */
199     protected Artifact getArtifact(ArtifactItem artifactItem) throws MojoExecutionException {
200         Artifact artifact;
201 
202         try {
203             ProjectBuildingRequest buildingRequest = newResolveArtifactProjectBuildingRequest();
204 
205             if (localRepositoryDirectory != null) {
206                 buildingRequest =
207                         repositoryManager.setLocalRepositoryBasedir(buildingRequest, localRepositoryDirectory);
208             }
209 
210             // Map dependency to artifact coordinate
211             DefaultArtifactCoordinate coordinate = new DefaultArtifactCoordinate();
212             coordinate.setGroupId(artifactItem.getGroupId());
213             coordinate.setArtifactId(artifactItem.getArtifactId());
214             coordinate.setVersion(artifactItem.getVersion());
215             coordinate.setClassifier(artifactItem.getClassifier());
216 
217             final String extension;
218             ArtifactHandler artifactHandler = artifactHandlerManager.getArtifactHandler(artifactItem.getType());
219             if (artifactHandler != null) {
220                 extension = artifactHandler.getExtension();
221             } else {
222                 extension = artifactItem.getType();
223             }
224             coordinate.setExtension(extension);
225 
226             artifact = artifactResolver
227                     .resolveArtifact(buildingRequest, coordinate)
228                     .getArtifact();
229         } catch (ArtifactResolverException e) {
230             throw new MojoExecutionException("Unable to find/resolve artifact.", e);
231         }
232 
233         return artifact;
234     }
235 
236     /**
237      * Tries to find missing version from dependency list and dependency management. If found, the artifact is updated
238      * with the correct version. It will first look for an exact match on artifactId/groupId/classifier/type and if it
239      * doesn't find a match, it will try again looking for artifactId and groupId only.
240      *
241      * @param artifact representing configured file.
242      * @throws MojoExecutionException
243      */
244     private void fillMissingArtifactVersion(ArtifactItem artifact) throws MojoExecutionException {
245         MavenProject project = getProject();
246         List<Dependency> deps = project.getDependencies();
247         List<Dependency> depMngt = project.getDependencyManagement() == null
248                 ? Collections.emptyList()
249                 : project.getDependencyManagement().getDependencies();
250 
251         if (!findDependencyVersion(artifact, deps, false)
252                 && (project.getDependencyManagement() == null || !findDependencyVersion(artifact, depMngt, false))
253                 && !findDependencyVersion(artifact, deps, true)
254                 && (project.getDependencyManagement() == null || !findDependencyVersion(artifact, depMngt, true))) {
255             throw new MojoExecutionException("Unable to find artifact version of " + artifact.getGroupId() + ":"
256                     + artifact.getArtifactId() + " in either dependency list or in project's dependency management.");
257         }
258     }
259 
260     /**
261      * Tries to find missing version from a list of dependencies. If found, the artifact is updated with the correct
262      * version.
263      *
264      * @param artifact representing configured file.
265      * @param dependencies list of dependencies to search.
266      * @param looseMatch only look at artifactId and groupId
267      * @return the found dependency
268      */
269     private boolean findDependencyVersion(ArtifactItem artifact, List<Dependency> dependencies, boolean looseMatch) {
270         for (Dependency dependency : dependencies) {
271             if (Objects.equals(dependency.getArtifactId(), artifact.getArtifactId())
272                     && Objects.equals(dependency.getGroupId(), artifact.getGroupId())
273                     && (looseMatch || Objects.equals(dependency.getClassifier(), artifact.getClassifier()))
274                     && (looseMatch || Objects.equals(dependency.getType(), artifact.getType()))) {
275                 artifact.setVersion(dependency.getVersion());
276 
277                 return true;
278             }
279         }
280 
281         return false;
282     }
283 
284     /**
285      * @return Returns the artifactItems.
286      */
287     public List<ArtifactItem> getArtifactItems() {
288         return this.artifactItems;
289     }
290 
291     /**
292      * @param theArtifactItems The artifactItems to set.
293      */
294     public void setArtifactItems(List<ArtifactItem> theArtifactItems) {
295         this.artifactItems = theArtifactItems;
296     }
297 
298     /**
299      * @return Returns the outputDirectory.
300      */
301     public File getOutputDirectory() {
302         return this.outputDirectory;
303     }
304 
305     /**
306      * @param theOutputDirectory The outputDirectory to set.
307      */
308     public void setOutputDirectory(File theOutputDirectory) {
309         this.outputDirectory = theOutputDirectory;
310     }
311 
312     /**
313      * @return Returns the overWriteIfNewer.
314      */
315     public boolean isOverWriteIfNewer() {
316         return this.overWriteIfNewer;
317     }
318 
319     /**
320      * @param theOverWriteIfNewer The overWriteIfNewer to set.
321      */
322     public void setOverWriteIfNewer(boolean theOverWriteIfNewer) {
323         this.overWriteIfNewer = theOverWriteIfNewer;
324     }
325 
326     /**
327      * @return Returns the overWriteReleases.
328      */
329     public boolean isOverWriteReleases() {
330         return this.overWriteReleases;
331     }
332 
333     /**
334      * @param theOverWriteReleases The overWriteReleases to set.
335      */
336     public void setOverWriteReleases(boolean theOverWriteReleases) {
337         this.overWriteReleases = theOverWriteReleases;
338     }
339 
340     /**
341      * @return Returns the overWriteSnapshots.
342      */
343     public boolean isOverWriteSnapshots() {
344         return this.overWriteSnapshots;
345     }
346 
347     /**
348      * @param theOverWriteSnapshots The overWriteSnapshots to set.
349      */
350     public void setOverWriteSnapshots(boolean theOverWriteSnapshots) {
351         this.overWriteSnapshots = theOverWriteSnapshots;
352     }
353 
354     /**
355      * @param localRepositoryDirectory {@link #localRepositoryDirectory}
356      */
357     public void setLocalRepositoryDirectory(File localRepositoryDirectory) {
358         this.localRepositoryDirectory = localRepositoryDirectory;
359     }
360 
361     /**
362      * @param artifact The artifact.
363      * @throws MojoFailureException in case of an error.
364      */
365     public void setArtifact(String artifact) throws MojoFailureException {
366         if (artifact != null) {
367             String packaging = "jar";
368             String classifier;
369             String[] tokens = artifact.split(":");
370             if (tokens.length < 3 || tokens.length > 5) {
371                 throw new MojoFailureException("Invalid artifact, "
372                         + "you must specify groupId:artifactId:version[:packaging[:classifier]] " + artifact);
373             }
374             String groupId = tokens[0];
375             String artifactId = tokens[1];
376             String version = tokens[2];
377             if (tokens.length >= 4) {
378                 packaging = tokens[3];
379             }
380             if (tokens.length == 5) {
381                 classifier = tokens[4];
382             } else {
383                 classifier = null;
384             }
385 
386             ArtifactItem artifactItem = new ArtifactItem();
387             artifactItem.setGroupId(groupId);
388             artifactItem.setArtifactId(artifactId);
389             artifactItem.setVersion(version);
390             artifactItem.setType(packaging);
391             artifactItem.setClassifier(classifier);
392 
393             setArtifactItems(Collections.singletonList(artifactItem));
394         }
395     }
396 }