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 with a message 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             // mdep-50 - rolledback for now because it's breaking some functionality.
204             /*
205              * List listeners = new ArrayList(); Set theSet = new HashSet(); theSet.add( artifact );
206              * ArtifactResolutionResult artifactResolutionResult = artifactCollector.collect( theSet, project
207              * .getArtifact(), managedVersions, this.local, project.getRemoteArtifactRepositories(),
208              * artifactMetadataSource, null, listeners ); Iterator iter =
209              * artifactResolutionResult.getArtifactResolutionNodes().iterator(); while ( iter.hasNext() ) {
210              * ResolutionNode node = (ResolutionNode) iter.next(); artifact = node.getArtifact(); }
211              */
212 
213             ProjectBuildingRequest buildingRequest = newResolveArtifactProjectBuildingRequest();
214 
215             if (localRepositoryDirectory != null) {
216                 buildingRequest =
217                         repositoryManager.setLocalRepositoryBasedir(buildingRequest, localRepositoryDirectory);
218             }
219 
220             // Map dependency to artifact coordinate
221             DefaultArtifactCoordinate coordinate = new DefaultArtifactCoordinate();
222             coordinate.setGroupId(artifactItem.getGroupId());
223             coordinate.setArtifactId(artifactItem.getArtifactId());
224             coordinate.setVersion(artifactItem.getVersion());
225             coordinate.setClassifier(artifactItem.getClassifier());
226 
227             final String extension;
228             ArtifactHandler artifactHandler = artifactHandlerManager.getArtifactHandler(artifactItem.getType());
229             if (artifactHandler != null) {
230                 extension = artifactHandler.getExtension();
231             } else {
232                 extension = artifactItem.getType();
233             }
234             coordinate.setExtension(extension);
235 
236             artifact = artifactResolver
237                     .resolveArtifact(buildingRequest, coordinate)
238                     .getArtifact();
239         } catch (ArtifactResolverException e) {
240             throw new MojoExecutionException("Unable to find/resolve artifact.", e);
241         }
242 
243         return artifact;
244     }
245 
246     /**
247      * Tries to find missing version from dependency list and dependency management. If found, the artifact is updated
248      * with the correct version. It will first look for an exact match on artifactId/groupId/classifier/type and if it
249      * doesn't find a match, it will try again looking for artifactId and groupId only.
250      *
251      * @param artifact representing configured file.
252      * @throws MojoExecutionException
253      */
254     private void fillMissingArtifactVersion(ArtifactItem artifact) throws MojoExecutionException {
255         MavenProject project = getProject();
256         List<Dependency> deps = project.getDependencies();
257         List<Dependency> depMngt = project.getDependencyManagement() == null
258                 ? Collections.emptyList()
259                 : project.getDependencyManagement().getDependencies();
260 
261         if (!findDependencyVersion(artifact, deps, false)
262                 && (project.getDependencyManagement() == null || !findDependencyVersion(artifact, depMngt, false))
263                 && !findDependencyVersion(artifact, deps, true)
264                 && (project.getDependencyManagement() == null || !findDependencyVersion(artifact, depMngt, true))) {
265             throw new MojoExecutionException("Unable to find artifact version of " + artifact.getGroupId() + ":"
266                     + artifact.getArtifactId() + " in either dependency list or in project's dependency management.");
267         }
268     }
269 
270     /**
271      * Tries to find missing version from a list of dependencies. If found, the artifact is updated with the correct
272      * version.
273      *
274      * @param artifact representing configured file.
275      * @param dependencies list of dependencies to search.
276      * @param looseMatch only look at artifactId and groupId
277      * @return the found dependency
278      */
279     private boolean findDependencyVersion(ArtifactItem artifact, List<Dependency> dependencies, boolean looseMatch) {
280         for (Dependency dependency : dependencies) {
281             if (Objects.equals(dependency.getArtifactId(), artifact.getArtifactId())
282                     && Objects.equals(dependency.getGroupId(), artifact.getGroupId())
283                     && (looseMatch || Objects.equals(dependency.getClassifier(), artifact.getClassifier()))
284                     && (looseMatch || Objects.equals(dependency.getType(), artifact.getType()))) {
285                 artifact.setVersion(dependency.getVersion());
286 
287                 return true;
288             }
289         }
290 
291         return false;
292     }
293 
294     /**
295      * @return Returns the artifactItems.
296      */
297     public List<ArtifactItem> getArtifactItems() {
298         return this.artifactItems;
299     }
300 
301     /**
302      * @param theArtifactItems The artifactItems to set.
303      */
304     public void setArtifactItems(List<ArtifactItem> theArtifactItems) {
305         this.artifactItems = theArtifactItems;
306     }
307 
308     /**
309      * @return Returns the outputDirectory.
310      */
311     public File getOutputDirectory() {
312         return this.outputDirectory;
313     }
314 
315     /**
316      * @param theOutputDirectory The outputDirectory to set.
317      */
318     public void setOutputDirectory(File theOutputDirectory) {
319         this.outputDirectory = theOutputDirectory;
320     }
321 
322     /**
323      * @return Returns the overWriteIfNewer.
324      */
325     public boolean isOverWriteIfNewer() {
326         return this.overWriteIfNewer;
327     }
328 
329     /**
330      * @param theOverWriteIfNewer The overWriteIfNewer to set.
331      */
332     public void setOverWriteIfNewer(boolean theOverWriteIfNewer) {
333         this.overWriteIfNewer = theOverWriteIfNewer;
334     }
335 
336     /**
337      * @return Returns the overWriteReleases.
338      */
339     public boolean isOverWriteReleases() {
340         return this.overWriteReleases;
341     }
342 
343     /**
344      * @param theOverWriteReleases The overWriteReleases to set.
345      */
346     public void setOverWriteReleases(boolean theOverWriteReleases) {
347         this.overWriteReleases = theOverWriteReleases;
348     }
349 
350     /**
351      * @return Returns the overWriteSnapshots.
352      */
353     public boolean isOverWriteSnapshots() {
354         return this.overWriteSnapshots;
355     }
356 
357     /**
358      * @param theOverWriteSnapshots The overWriteSnapshots to set.
359      */
360     public void setOverWriteSnapshots(boolean theOverWriteSnapshots) {
361         this.overWriteSnapshots = theOverWriteSnapshots;
362     }
363 
364     /**
365      * @param localRepositoryDirectory {@link #localRepositoryDirectory}
366      */
367     public void setLocalRepositoryDirectory(File localRepositoryDirectory) {
368         this.localRepositoryDirectory = localRepositoryDirectory;
369     }
370 
371     /**
372      * @param artifact The artifact.
373      * @throws MojoFailureException in case of an error.
374      */
375     public void setArtifact(String artifact) throws MojoFailureException {
376         if (artifact != null) {
377             String packaging = "jar";
378             String classifier;
379             String[] tokens = artifact.split(":");
380             if (tokens.length < 3 || tokens.length > 5) {
381                 throw new MojoFailureException("Invalid artifact, "
382                         + "you must specify groupId:artifactId:version[:packaging[:classifier]] " + artifact);
383             }
384             String groupId = tokens[0];
385             String artifactId = tokens[1];
386             String version = tokens[2];
387             if (tokens.length >= 4) {
388                 packaging = tokens[3];
389             }
390             if (tokens.length == 5) {
391                 classifier = tokens[4];
392             } else {
393                 classifier = null;
394             }
395 
396             ArtifactItem artifactItem = new ArtifactItem();
397             artifactItem.setGroupId(groupId);
398             artifactItem.setArtifactId(artifactId);
399             artifactItem.setVersion(version);
400             artifactItem.setType(packaging);
401             artifactItem.setClassifier(classifier);
402 
403             setArtifactItems(Collections.singletonList(artifactItem));
404         }
405     }
406 }