View Javadoc

1   package org.apache.maven.plugin.dependency.fromConfiguration;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   * http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.maven.artifact.Artifact;
23  import org.apache.maven.artifact.factory.ArtifactFactory;
24  import org.apache.maven.artifact.repository.ArtifactRepository;
25  import org.apache.maven.artifact.repository.ArtifactRepositoryFactory;
26  import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
27  import org.apache.maven.artifact.resolver.ArtifactResolutionException;
28  import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
29  import org.apache.maven.artifact.versioning.VersionRange;
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.plugin.dependency.AbstractDependencyMojo;
34  import org.apache.maven.plugin.dependency.utils.DependencyUtil;
35  import org.apache.maven.plugin.dependency.utils.filters.ArtifactItemFilter;
36  import org.apache.maven.plugins.annotations.Component;
37  import org.apache.maven.plugins.annotations.Parameter;
38  import org.apache.maven.project.MavenProject;
39  import org.apache.maven.shared.artifact.filter.collection.ArtifactFilterException;
40  import org.codehaus.plexus.util.StringUtils;
41  
42  import java.io.File;
43  import java.util.Collections;
44  import java.util.List;
45  import java.util.Set;
46  
47  /**
48   * Abstract parent class used by mojos that get Artifact information from the plugin configuration as an ArrayList of
49   * ArtifactItems
50   *
51   * @author <a href="mailto:brianf@apache.org">Brian Fox</a>
52   * @version $Id: AbstractFromConfigurationMojo.java 1451088 2013-02-28 04:22:41Z brianf $
53   * @see ArtifactItem
54   */
55  public abstract class AbstractFromConfigurationMojo
56      extends AbstractDependencyMojo
57  {
58      /**
59       * Default location used for mojo unless overridden in ArtifactItem
60       *
61       * @since 1.0
62       */
63      @Parameter( property = "outputDirectory", defaultValue = "${project.build.directory}/dependency" )
64      private File outputDirectory;
65  
66      /**
67       * Overwrite release artifacts
68       *
69       * @since 1.0
70       */
71      @Parameter( property = "mdep.overWriteReleases", defaultValue = "false" )
72      private boolean overWriteReleases;
73  
74      /**
75       * Overwrite snapshot artifacts
76       *
77       * @since 1.0
78       */
79      @Parameter( property = "mdep.overWriteSnapshots", defaultValue = "false" )
80      private boolean overWriteSnapshots;
81  
82      /**
83       * Overwrite if newer
84       *
85       * @since 2.0
86       */
87      @Parameter( property = "mdep.overIfNewer", defaultValue = "true" )
88      private boolean overWriteIfNewer;
89  
90      /**
91       * To search for artifacts within the reactor and ensure consistent behaviour between Maven 2 and Maven 3.
92       */
93      @Parameter( defaultValue = "${reactorProjects}", readonly = true, required = true )
94      protected List<MavenProject> reactorProjects;
95  
96      /**
97       * Collection of ArtifactItems to work on. (ArtifactItem contains groupId, artifactId, version, type, classifier,
98       * outputDirectory, destFileName and overWrite.) 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      * To look up ArtifactRepository implementation
107      */
108     @Component
109     private ArtifactRepositoryFactory artifactRepositoryManager;
110 
111     /**
112      * Path to override default local repository during plugin's execution. To remove all downloaded artifacts as part
113      * of the build, set this value to a location under your project's target directory
114      *
115      * @since 2.2
116      */
117     @Parameter
118     private File localRepositoryDirectory;
119 
120     /**
121      * To host and cache localRepositoryDirectory
122      */
123     private ArtifactRepository overrideLocalRepository;
124 
125     @Component
126     private ArtifactFactory artifactFactory;
127 
128     abstract ArtifactItemFilter getMarkedArtifactFilter( ArtifactItem item );
129     
130     // artifactItems is filled by either field injection or by setArtifact()
131     protected void verifyRequirements() throws MojoFailureException
132     {
133         if ( artifactItems == null || artifactItems.isEmpty() )
134         {
135             throw new MojoFailureException( "Either artifact or artifactItems is required " );
136         }
137     }
138 
139     /**
140      * Preprocesses the list of ArtifactItems. This method defaults the outputDirectory if not set and creates the
141      * output Directory if it doesn't exist.
142      *
143      * @param removeVersion remove the version from the filename.
144      * @param prependGroupId prepend the groupId to the filename.
145      * @param useBaseVersion use the baseVersion of the artifact instead of version for the filename.
146      * @return An ArrayList of preprocessed ArtifactItems
147      * @throws MojoExecutionException with a message if an error occurs.
148      * @see ArtifactItem
149      */
150     protected List<ArtifactItem> getProcessedArtifactItems( ProcessArtifactItemsRequest processArtifactItemsRequest  )
151         throws MojoExecutionException
152     {
153 
154         boolean removeVersion = processArtifactItemsRequest.isRemoveVersion(), prependGroupId =
155             processArtifactItemsRequest.isPrependGroupId(), useBaseVersion =
156             processArtifactItemsRequest.isUseBaseVersion();
157 
158         if ( artifactItems == null || artifactItems.size() < 1 )
159         {
160             throw new MojoExecutionException( "There are no artifactItems configured." );
161         }
162 
163         for ( ArtifactItem artifactItem : artifactItems )
164         {
165             this.getLog().info( "Configured Artifact: " + artifactItem.toString() );
166 
167             if ( artifactItem.getOutputDirectory() == null )
168             {
169                 artifactItem.setOutputDirectory( this.outputDirectory );
170             }
171             artifactItem.getOutputDirectory().mkdirs();
172 
173             // make sure we have a version.
174             if ( StringUtils.isEmpty( artifactItem.getVersion() ) )
175             {
176                 fillMissingArtifactVersion( artifactItem );
177             }
178 
179             artifactItem.setArtifact( this.getArtifact( artifactItem ) );
180 
181             if ( StringUtils.isEmpty( artifactItem.getDestFileName() ) )
182             {
183                 artifactItem.setDestFileName(
184                     DependencyUtil.getFormattedFileName( artifactItem.getArtifact(), removeVersion, prependGroupId, useBaseVersion ) );
185             }
186 
187             try
188             {
189                 artifactItem.setNeedsProcessing( checkIfProcessingNeeded( artifactItem ) );
190             }
191             catch ( ArtifactFilterException e )
192             {
193                 throw new MojoExecutionException( e.getMessage(), e );
194             }
195         }
196         return artifactItems;
197     }
198 
199     private boolean checkIfProcessingNeeded( ArtifactItem item )
200         throws MojoExecutionException, ArtifactFilterException
201     {
202         return StringUtils.equalsIgnoreCase( item.getOverWrite(), "true" )
203             || getMarkedArtifactFilter( item ).isArtifactIncluded( item );
204     }
205 
206     /**
207      * Resolves the Artifact from the remote repository if necessary. If no version is specified, it will be retrieved
208      * from the dependency list or from the DependencyManagement section of the pom.
209      *
210      * @param artifactItem containing information about artifact from plugin configuration.
211      * @return Artifact object representing the specified file.
212      * @throws MojoExecutionException with a message if the version can't be found in DependencyManagement.
213      */
214     protected Artifact getArtifact( ArtifactItem artifactItem )
215         throws MojoExecutionException
216     {
217         Artifact artifact;
218 
219         // Map managedVersions = createManagedVersionMap( factory, project.getId(), project.getDependencyManagement() );
220         VersionRange vr;
221         try
222         {
223             vr = VersionRange.createFromVersionSpec( artifactItem.getVersion() );
224         }
225         catch ( InvalidVersionSpecificationException e1 )
226         {
227             // TODO Auto-generated catch block
228             e1.printStackTrace();
229             vr = VersionRange.createFromVersion( artifactItem.getVersion() );
230         }
231 
232         if ( StringUtils.isEmpty( artifactItem.getClassifier() ) )
233         {
234             artifact = factory.createDependencyArtifact( artifactItem.getGroupId(), artifactItem.getArtifactId(), vr,
235                                                          artifactItem.getType(), null, Artifact.SCOPE_COMPILE );
236         }
237         else
238         {
239             artifact = factory.createDependencyArtifact( artifactItem.getGroupId(), artifactItem.getArtifactId(), vr,
240                                                          artifactItem.getType(), artifactItem.getClassifier(),
241                                                          Artifact.SCOPE_COMPILE );
242         }
243 
244         // Maven 3 will search the reactor for the artifact but Maven 2 does not
245         // to keep consistent behaviour, we search the reactor ourselves.
246         Artifact result = getArtifactFomReactor( artifact );
247         if ( result != null )
248         {
249             return result;
250         }
251 
252         try
253         {
254             // mdep-50 - rolledback for now because it's breaking some functionality.
255             /*
256              * List listeners = new ArrayList(); Set theSet = new HashSet(); theSet.add( artifact );
257              * ArtifactResolutionResult artifactResolutionResult = artifactCollector.collect( theSet, project
258              * .getArtifact(), managedVersions, this.local, project.getRemoteArtifactRepositories(),
259              * artifactMetadataSource, null, listeners ); Iterator iter =
260              * artifactResolutionResult.getArtifactResolutionNodes().iterator(); while ( iter.hasNext() ) {
261              * ResolutionNode node = (ResolutionNode) iter.next(); artifact = node.getArtifact(); }
262              */
263 
264             resolver.resolve( artifact, remoteRepos, getLocal() );
265         }
266         catch ( ArtifactResolutionException e )
267         {
268             throw new MojoExecutionException( "Unable to resolve artifact.", e );
269         }
270         catch ( ArtifactNotFoundException e )
271         {
272             throw new MojoExecutionException( "Unable to find artifact.", e );
273         }
274 
275         return artifact;
276     }
277 
278     /**
279      * Checks to see if the specified artifact is available from the reactor.
280      *
281      * @param artifact The artifact we are looking for.
282      * @return The resolved artifact that is the same as the one we were looking for or <code>null</code> if one could
283      *         not be found.
284      */
285     private Artifact getArtifactFomReactor( Artifact artifact )
286     {
287         // check project dependencies first off
288         for ( Artifact a : (Set<Artifact>) project.getArtifacts() )
289         {
290             if ( equals( artifact, a ) && hasFile( a ) )
291             {
292                 return a;
293             }
294         }
295 
296         // check reactor projects
297         for ( MavenProject p : reactorProjects == null ? Collections.<MavenProject>emptyList() : reactorProjects )
298         {
299             // check the main artifact
300             if ( equals( artifact, p.getArtifact() ) && hasFile( p.getArtifact() ) )
301             {
302                 return p.getArtifact();
303             }
304 
305             // check any side artifacts
306             for ( Artifact a : (List<Artifact>) p.getAttachedArtifacts() )
307             {
308                 if ( equals( artifact, a ) && hasFile( a ) )
309                 {
310                     return a;
311                 }
312             }
313         }
314 
315         // not available
316         return null;
317     }
318 
319     /**
320      * Returns <code>true</code> if the artifact has a file.
321      *
322      * @param artifact the artifact (may be null)
323      * @return <code>true</code> if and only if the artifact is non-null and has a file.
324      */
325     private static boolean hasFile( Artifact artifact )
326     {
327         return artifact != null && artifact.getFile() != null && artifact.getFile().isFile();
328     }
329 
330     /**
331      * Null-safe compare of two artifacts based on groupId, artifactId, version, type and classifier.
332      *
333      * @param a the first artifact.
334      * @param b the second artifact.
335      * @return <code>true</code> if and only if the two artifacts have the same groupId, artifactId, version,
336      *         type and classifier.
337      */
338     private static boolean equals( Artifact a, Artifact b )
339     {
340         return a == b || !( a == null || b == null )
341             && StringUtils.equals( a.getGroupId(), b.getGroupId() )
342             && StringUtils.equals( a.getArtifactId(), b.getArtifactId() )
343             && StringUtils.equals( a.getVersion(), b.getVersion() )
344             && StringUtils.equals( a.getType(), b.getType() )
345             && StringUtils.equals( a.getClassifier(), b.getClassifier() );
346     }
347 
348     /**
349      * Tries to find missing version from dependency list and dependency management. If found, the artifact is updated
350      * with the correct version. It will first look for an exact match on artifactId/groupId/classifier/type and if it
351      * doesn't find a match, it will try again looking for artifactId and groupId only.
352      *
353      * @param artifact representing configured file.
354      * @throws MojoExecutionException
355      */
356     private void fillMissingArtifactVersion( ArtifactItem artifact )
357         throws MojoExecutionException
358     {
359         @SuppressWarnings( "unchecked" ) List<Dependency> deps = project.getDependencies();
360         @SuppressWarnings( "unchecked" ) List<Dependency> depMngt = project.getDependencyManagement() == null
361             ? Collections.<Dependency>emptyList()
362             : project.getDependencyManagement().getDependencies();
363 
364         if ( !findDependencyVersion( artifact, deps, false )
365             && ( project.getDependencyManagement() == null || !findDependencyVersion( artifact, depMngt, false ) )
366             && !findDependencyVersion( artifact, deps, true )
367             && ( project.getDependencyManagement() == null || !findDependencyVersion( artifact, depMngt, true ) ) )
368         {
369             throw new MojoExecutionException(
370                 "Unable to find artifact version of " + artifact.getGroupId() + ":" + artifact.getArtifactId()
371                     + " in either dependency list or in project's dependency management." );
372         }
373     }
374 
375     /**
376      * Tries to find missing version from a list of dependencies. If found, the artifact is updated with the correct
377      * version.
378      *
379      * @param artifact     representing configured file.
380      * @param dependencies list of dependencies to search.
381      * @param looseMatch   only look at artifactId and groupId
382      * @return the found dependency
383      */
384     private boolean findDependencyVersion( ArtifactItem artifact, List<Dependency> dependencies, boolean looseMatch )
385     {
386         for ( Dependency dependency : dependencies )
387         {
388             if ( StringUtils.equals( dependency.getArtifactId(), artifact.getArtifactId() )
389                 && StringUtils.equals( dependency.getGroupId(), artifact.getGroupId() )
390                 && ( looseMatch || StringUtils.equals( dependency.getClassifier(), artifact.getClassifier() ) )
391                 && ( looseMatch || StringUtils.equals( dependency.getType(), artifact.getType() ) ) )
392             {
393                 artifact.setVersion( dependency.getVersion() );
394 
395                 return true;
396             }
397         }
398 
399         return false;
400     }
401 
402     /*
403      * private Map createManagedVersionMap( ArtifactFactory artifactFactory, String projectId, DependencyManagement
404      * dependencyManagement ) throws MojoExecutionException { Map map; if ( dependencyManagement != null &&
405      * dependencyManagement.getDependencies() != null ) { map = new HashMap(); for ( Iterator i =
406      * dependencyManagement.getDependencies().iterator(); i.hasNext(); ) { Dependency d = (Dependency) i.next(); try {
407      * VersionRange versionRange = VersionRange.createFromVersionSpec( d.getVersion() ); Artifact artifact =
408      * artifactFactory.createDependencyArtifact( d.getGroupId(), d.getArtifactId(), versionRange, d.getType(), d
409      * .getClassifier(), d.getScope(), d .isOptional() ); map.put( d.getManagementKey(), artifact ); } catch (
410      * InvalidVersionSpecificationException e ) { throw new MojoExecutionException( "Unable to parse version", e ); } }
411      * } else { map = Collections.EMPTY_MAP; } return map; }
412      */
413 
414     /**
415      * Override the base to
416      *
417      * @return Returns the local.
418      */
419     protected ArtifactRepository getLocal()
420     {
421         if ( this.overrideLocalRepository != null )
422         {
423             return this.overrideLocalRepository;
424         }
425 
426         ArtifactRepository local = super.getLocal();
427 
428         if ( this.localRepositoryDirectory != null )
429         {
430             // create a new local repo using existing layout, snapshots, and releases policy
431             String url = "file://" + this.localRepositoryDirectory.getAbsolutePath();
432             this.overrideLocalRepository =
433                 artifactRepositoryManager.createArtifactRepository( local.getId(), url, local.getLayout(),
434                                                                     local.getSnapshots(), local.getReleases() );
435 
436             this.getLog().debug( "Execution local repository is at: " + this.overrideLocalRepository.getBasedir() );
437         }
438         else
439         {
440             this.overrideLocalRepository = local;
441         }
442 
443         return this.overrideLocalRepository;
444     }
445 
446     /**
447      * @return Returns the artifactItems.
448      */
449     public List<ArtifactItem> getArtifactItems()
450     {
451         return this.artifactItems;
452     }
453 
454     /**
455      * @param theArtifactItems The artifactItems to set.
456      */
457     public void setArtifactItems( List<ArtifactItem> theArtifactItems )
458     {
459         this.artifactItems = theArtifactItems;
460     }
461 
462     /**
463      * @return Returns the outputDirectory.
464      */
465     public File getOutputDirectory()
466     {
467         return this.outputDirectory;
468     }
469 
470     /**
471      * @param theOutputDirectory The outputDirectory to set.
472      */
473     public void setOutputDirectory( File theOutputDirectory )
474     {
475         this.outputDirectory = theOutputDirectory;
476     }
477 
478     /**
479      * @return Returns the overWriteIfNewer.
480      */
481     public boolean isOverWriteIfNewer()
482     {
483         return this.overWriteIfNewer;
484     }
485 
486     /**
487      * @param theOverWriteIfNewer The overWriteIfNewer to set.
488      */
489     public void setOverWriteIfNewer( boolean theOverWriteIfNewer )
490     {
491         this.overWriteIfNewer = theOverWriteIfNewer;
492     }
493 
494     /**
495      * @return Returns the overWriteReleases.
496      */
497     public boolean isOverWriteReleases()
498     {
499         return this.overWriteReleases;
500     }
501 
502     /**
503      * @param theOverWriteReleases The overWriteReleases to set.
504      */
505     public void setOverWriteReleases( boolean theOverWriteReleases )
506     {
507         this.overWriteReleases = theOverWriteReleases;
508     }
509 
510     /**
511      * @return Returns the overWriteSnapshots.
512      */
513     public boolean isOverWriteSnapshots()
514     {
515         return this.overWriteSnapshots;
516     }
517 
518     /**
519      * @param theOverWriteSnapshots The overWriteSnapshots to set.
520      */
521     public void setOverWriteSnapshots( boolean theOverWriteSnapshots )
522     {
523         this.overWriteSnapshots = theOverWriteSnapshots;
524     }
525 
526     public void setLocalRepositoryDirectory( File localRepositoryDirectory )
527     {
528         this.localRepositoryDirectory = localRepositoryDirectory;
529     }
530 
531     public void setArtifact( String artifact )
532         throws MojoFailureException
533     {
534         if ( artifact != null )
535         {
536             String packaging = "jar";
537             String classifier;
538             String[] tokens = StringUtils.split( artifact, ":" );
539             if ( tokens.length < 3 || tokens.length > 5 )
540             {
541                 throw new MojoFailureException(
542                     "Invalid artifact, you must specify groupId:artifactId:version[:packaging][:classifier] "
543                         + artifact );
544             }
545             String groupId = tokens[0];
546             String artifactId = tokens[1];
547             String version = tokens[2];
548             if ( tokens.length >= 4 )
549             {
550                 packaging = tokens[3];
551             }
552             if ( tokens.length == 5 )
553             {
554                 classifier = tokens[4];
555             }
556             else
557             {
558                 classifier = null;
559             }
560     
561             Artifact toUnpack = classifier == null
562             ? artifactFactory.createBuildArtifact( groupId, artifactId, version, packaging )
563             : artifactFactory.createArtifactWithClassifier( groupId, artifactId, version, packaging, classifier );
564             
565             setArtifactItems( Collections.singletonList( new ArtifactItem( toUnpack ) ) );
566         }
567     }
568 }