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