View Javadoc

1   package org.apache.maven.artifact.ant;
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.ant.util.AntBuildWriter;
24  import org.apache.maven.artifact.ant.util.AntTaskModified;
25  import org.apache.maven.artifact.ant.util.AntUtil;
26  import org.apache.maven.artifact.factory.ArtifactFactory;
27  import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
28  import org.apache.maven.artifact.repository.ArtifactRepository;
29  import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
30  import org.apache.maven.artifact.resolver.ArtifactResolutionException;
31  import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
32  import org.apache.maven.artifact.resolver.ArtifactResolver;
33  import org.apache.maven.artifact.resolver.filter.AndArtifactFilter;
34  import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
35  import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter;
36  import org.apache.maven.model.Dependency;
37  import org.apache.maven.project.artifact.InvalidDependencyVersionException;
38  import org.apache.maven.project.artifact.MavenMetadataSource;
39  import org.apache.tools.ant.BuildException;
40  import org.apache.tools.ant.Project;
41  import org.apache.tools.ant.types.FileSet;
42  import org.apache.tools.ant.types.Path;
43  import org.codehaus.plexus.util.FileUtils;
44  import org.codehaus.plexus.util.StringUtils;
45  
46  import java.io.File;
47  import java.io.IOException;
48  import java.util.Arrays;
49  import java.util.ArrayList;
50  import java.util.Collections;
51  import java.util.HashSet;
52  import java.util.Iterator;
53  import java.util.List;
54  import java.util.Map;
55  import java.util.Set;
56  
57  /**
58   * Dependencies task, using maven-artifact.
59   *
60   * @author <a href="mailto:brett@apache.org">Brett Porter</a>
61   * @author <a href="mailto:hboutemy@apache.org">Herve Boutemy</a>
62   * @version $Id: DependenciesTask.html 806929 2012-03-01 18:57:40Z hboutemy $
63   */
64  public class DependenciesTask
65      extends AbstractArtifactWithRepositoryTask
66  {
67      private static final String[] SCOPES = { Artifact.SCOPE_COMPILE, Artifact.SCOPE_PROVIDED, Artifact.SCOPE_RUNTIME,
68          Artifact.SCOPE_TEST, Artifact.SCOPE_SYSTEM };
69  
70      private static final Set<String> SCOPES_SET;
71      static
72      {
73          SCOPES_SET = new HashSet<String>( Arrays.asList( SCOPES ) );
74      }
75  
76      public static final String DEFAULT_ANT_BUILD_FILE = "target/build-dependencies.xml";
77  
78      private List<Dependency> dependencies = new ArrayList<Dependency>();
79  
80      /**
81       * The id of the path object containing a list of all dependencies.
82       */
83      private String pathId;
84  
85      /**
86       * The id of the fileset object containing a list of all dependencies.
87       */
88      private String filesetId;
89  
90      /**
91       * The id of the fileset object containing all resolved source jars for the list of dependencies.
92       */
93      private String sourcesFilesetId;
94  
95      /**
96       * The id of the fileset object containing all resolved javadoc jars for the list of dependencies.
97       */
98      private String javadocFilesetId;
99  
100     /**
101      * The id of the object containing a list of all artifact versions.
102      * This is used for things like removing the version from the dependency filenames.
103      */
104     private String versionsId;
105 
106     /**
107      * A specific Maven scope used to determine which dependencies are resolved.
108      * This takes only a single scope and uses the standard Maven ScopeArtifactFilter.
109      */
110     private String useScope;
111 
112     /**
113      * A comma separated list of dependency scopes to include, in the resulting path and fileset.
114      */
115     private String scopes;
116 
117     /**
118      * A comma separated list of dependency types to include in the resulting set of artifacts.
119      */
120     private String type;
121 
122     /**
123      * A comma separated list of dependency types to include in the resulting path object.
124      */
125     private String pathType;
126 
127     /**
128      * The file name to use for the generated Ant build that contains dependency properties and references.
129      */
130     private String dependencyRefsBuildFile;
131 
132     /**
133      * Whether to use a generated Ant build file to cache the list of dependency properties and references.
134      */
135     private boolean cacheDependencyRefs;
136 
137     /**
138      * Main task execution.  Called by parent execute().
139      */
140     protected void doExecute()
141     {
142 
143         if ( useScope != null && scopes != null )
144         {
145             throw new BuildException( "You cannot specify both useScope and scopes in the dependencies task." );
146         }
147 
148         if ( getPom() != null && !this.dependencies.isEmpty() )
149         {
150             throw new BuildException( "You cannot specify both dependencies and a pom in the dependencies task" );
151         }
152 
153         // Try to load dependency refs from an existing Ant cache file
154         if ( isCacheDependencyRefs() )
155         {
156             if ( getDependencyRefsBuildFile() == null )
157             {
158                 setDependencyRefsBuildFile( DEFAULT_ANT_BUILD_FILE );
159             }
160 
161             if ( checkCachedDependencies() )
162             {
163                 log( "Dependency refs loaded from file: " + getDependencyRefsBuildFile(), Project.MSG_VERBOSE );
164                 return;
165             }
166         }
167 
168         doExecuteResolution();
169     }
170 
171     protected ArtifactResolutionResult doExecuteResolution()
172     {
173         ArtifactRepository localRepo = createLocalArtifactRepository();
174         log( "Using local repository: " + localRepo.getBasedir(), Project.MSG_VERBOSE );
175 
176         // Look up required resources from the plexus container
177         ArtifactResolver resolver = (ArtifactResolver) lookup( ArtifactResolver.ROLE );
178         ArtifactFactory artifactFactory = (ArtifactFactory) lookup( ArtifactFactory.ROLE );
179         MavenMetadataSource metadataSource = (MavenMetadataSource) lookup( ArtifactMetadataSource.ROLE );
180 
181         Pom pom = initializePom( localRepo );
182         if ( pom != null )
183         {
184             dependencies = pom.getDependencies();
185         }
186         else
187         {
188             // we have to have some sort of Pom object in order to satisfy the requirements for building the
189             // originating Artifact below...
190             pom = createDummyPom( localRepo );
191         }
192 
193         if ( dependencies.isEmpty() )
194         {
195             log( "There were no dependencies specified", Project.MSG_WARN );
196         }
197         else
198         {
199             // check scopes
200             for ( Dependency dependency : dependencies )
201             {
202                 String scope = dependency.getScope();
203 
204                 if ( Artifact.SCOPE_SYSTEM.equals( scope ) )
205                 {
206                     if ( StringUtils.isBlank( dependency.getSystemPath() ) )
207                     {
208                         throw new BuildException( dependency.toString()
209                             + " is defined with scope='system': systemPath attribute is required." );
210                     }
211                 }
212                 else if ( ( scope != null ) && !SCOPES_SET.contains( scope ) )
213                 {
214                     // see MANTTASKS-190
215                     log( "Unknown scope='" + scope + "' for " + dependency + ", supported scopes are: " + SCOPES_SET,
216                          Project.MSG_WARN );
217                 }
218             }
219         }
220 
221         log( "Resolving dependencies...", Project.MSG_VERBOSE );
222 
223         ArtifactResolutionResult result;
224 
225         List<ArtifactRepository> remoteArtifactRepositories = createRemoteArtifactRepositories( pom.getRepositories() );
226 
227         try
228         {
229             Set<Artifact> artifacts = MavenMetadataSource.createArtifacts( artifactFactory, dependencies, null, null, null );
230 
231             Artifact pomArtifact = artifactFactory.createBuildArtifact( pom.getGroupId(), pom.getArtifactId(),
232                 pom.getVersion(), pom.getPackaging() );
233 
234             List<AntResolutionListener> listeners = Collections.singletonList( new AntResolutionListener( getProject() ) );
235 
236             Map<String,Artifact> managedDependencies = pom.getMavenProject().getManagedVersionMap();
237 
238             ArtifactFilter filter = null;
239             if ( useScope != null )
240             {
241                 filter = new ScopeArtifactFilter( useScope );
242             }
243             if ( scopes != null )
244             {
245                 filter = new SpecificScopesArtifactFilter( scopes );
246             }
247             if ( type != null )
248             {
249                 ArtifactFilter typeArtifactFilter = new TypesArtifactFilter( type );
250                 if ( filter != null )
251                 {
252                     AndArtifactFilter andFilter = new AndArtifactFilter();
253                     andFilter.add( filter );
254                     andFilter.add( typeArtifactFilter );
255                     filter = andFilter;
256                 }
257                 else
258                 {
259                     filter = typeArtifactFilter;
260                 }
261             }
262 
263             result = resolver.resolveTransitively( artifacts, pomArtifact, managedDependencies, localRepo,
264                                                    remoteArtifactRepositories, metadataSource, filter, listeners );
265         }
266         catch ( ArtifactResolutionException e )
267         {
268             throw new BuildException( "Unable to resolve artifact: " + e.getMessage(), e );
269         }
270         catch ( ArtifactNotFoundException e )
271         {
272             throw new BuildException( "Dependency not found: " + e.getMessage(), e );
273         }
274         catch ( InvalidDependencyVersionException e )
275         {
276             throw new BuildException( "Invalid dependency version: " + e.getMessage(), e );
277         }
278 
279         FileSet dependencyFileSet = createFileSet();
280 
281         FileSet sourcesFileSet = createFileSet();
282 
283         FileSet javadocsFileSet = createFileSet();
284 
285         Path dependencyPath = new Path( getProject() );
286 
287         Set<String> versions = new HashSet<String>();
288 
289         ArtifactFilter pathFilter;
290 
291         if ( pathType  != null )
292         {
293             pathFilter = new TypesArtifactFilter( pathType );
294         }
295         else
296         {
297             pathFilter = null;
298         }
299 
300         for ( Iterator<Artifact> i = result.getArtifacts().iterator(); i.hasNext(); )
301         {
302             Artifact artifact = i.next();
303 
304             addArtifactToResult( localRepo, artifact, dependencyFileSet, dependencyPath, pathFilter );
305 
306             versions.add( artifact.getVersion() );
307 
308             if ( sourcesFilesetId != null )
309             {
310                 resolveSource( artifactFactory, resolver, remoteArtifactRepositories, localRepo,
311                                artifact, "sources", sourcesFileSet );
312             }
313 
314             if ( javadocFilesetId != null )
315             {
316                 resolveSource( artifactFactory, resolver, remoteArtifactRepositories, localRepo,
317                                artifact, "javadoc", javadocsFileSet );
318             }
319 
320         }
321 
322         defineFilesetReference( filesetId, dependencyFileSet );
323 
324         defineFilesetReference( sourcesFilesetId, sourcesFileSet );
325 
326         defineFilesetReference( javadocFilesetId, javadocsFileSet );
327 
328         if ( pathId != null )
329         {
330             getProject().addReference( pathId, dependencyPath );
331         }
332 
333         if ( versionsId != null )
334         {
335             String versionsValue = StringUtils.join( versions.iterator(), File.pathSeparator );
336             getProject().setNewProperty( versionsId, versionsValue );
337         }
338 
339         // Write the dependency information to an Ant build file.
340         if ( getDependencyRefsBuildFile() != null || this.isCacheDependencyRefs() )
341         {
342             if ( getDependencyRefsBuildFile() == null || getDependencyRefsBuildFile().equals( "default" ) )
343             {
344                 setDependencyRefsBuildFile( DEFAULT_ANT_BUILD_FILE );
345             }
346             log( "Building ant file: " + getDependencyRefsBuildFile() );
347             AntBuildWriter antBuildWriter = new AntBuildWriter();
348             File antBuildFile = FileUtils.resolveFile( getProject().getBaseDir(), getDependencyRefsBuildFile() );
349             try
350             {
351                 antBuildWriter.openAntBuild( antBuildFile, "maven-dependencies", "init-dependencies" );
352                 antBuildWriter.openTarget( "init-dependencies" );
353                 antBuildWriter.writeEcho( "Loading dependency paths from file: " + antBuildFile.getAbsolutePath() );
354 
355                 for ( Iterator<Artifact> i = result.getArtifacts().iterator(); i.hasNext(); )
356                 {
357                     Artifact artifact = i.next();
358                     String conflictId = artifact.getDependencyConflictId();
359                     antBuildWriter.writeProperty( conflictId, artifact.getFile().getAbsolutePath() );
360                     FileSet singleArtifactFileSet = (FileSet) getProject().getReference( conflictId );
361                     antBuildWriter.writeFileSet( singleArtifactFileSet, conflictId );
362                 }
363 
364                 if ( pathId != null )
365                 {
366                     Path thePath = (Path) getProject().getReference( pathId );
367                     antBuildWriter.writePath( thePath, pathId );
368                 }
369 
370                 if ( filesetId != null )
371                 {
372                     antBuildWriter.writeFileSet( dependencyFileSet, filesetId );
373                 }
374                 if ( sourcesFilesetId != null )
375                 {
376                     antBuildWriter.writeFileSet( sourcesFileSet, sourcesFilesetId );
377                 }
378                 if ( javadocFilesetId != null )
379                 {
380                     antBuildWriter.writeFileSet( sourcesFileSet, javadocFilesetId );
381                 }
382 
383                 String versionsList = getProject().getProperty( versionsId );
384                 if ( versionsList != null )
385                 {
386                     antBuildWriter.writeProperty( versionsId, versionsList );
387                 }
388 
389                 antBuildWriter.closeTarget();
390                 antBuildWriter.closeAntBuild();
391             }
392             catch ( IOException e )
393             {
394                 throw new BuildException ( "Unable to write ant build: " + e);
395             }
396         }
397 
398         return result;
399     }
400 
401     /**
402      * Check if the cache needs to be updated.
403      *
404      * @return true if the dependency refs were successfully loaded, false otherwise
405      */
406     private boolean checkCachedDependencies()
407     {
408         File cacheBuildFile = FileUtils.resolveFile( getProject().getBaseDir(), getDependencyRefsBuildFile() );
409         if ( ! cacheBuildFile.exists() )
410         {
411             return false;
412         }
413 
414         File antBuildFile = new File( getProject().getProperty( "ant.file" ) );
415         if ( antBuildFile.lastModified() > cacheBuildFile.lastModified() )
416         {
417             return false;
418         }
419 
420         Pom pom = getPom();
421         if ( pom != null )
422         {
423             File pomFile = pom.getFile();
424             if ( pomFile != null && ( pomFile.lastModified() > cacheBuildFile.lastModified() ) )
425             {
426                 return false;
427             }
428         }
429 
430         return loadDependenciesFromAntBuildFile();
431     }
432 
433     /**
434      * Load the dependency references from the generated ant build file.
435      *
436      * @return True if the dependency refs were successfully loaded.
437      */
438     private boolean loadDependenciesFromAntBuildFile()
439     {
440         Project currentAntProject = getProject();
441 
442         // Run the ant build with the dependency refs
443         AntTaskModified dependenciesAntBuild = new AntTaskModified();
444         dependenciesAntBuild.setAntfile( getDependencyRefsBuildFile() );
445         dependenciesAntBuild.setProject( currentAntProject );
446         dependenciesAntBuild.execute();
447 
448         // Copy the properties and refs to the current project
449         Project cachedDepsProject = dependenciesAntBuild.getSavedNewProject();
450         AntUtil.copyProperties( cachedDepsProject, currentAntProject );
451         AntUtil.copyReferences( cachedDepsProject, currentAntProject );
452 
453         return true;
454     }
455 
456     private FileSet createFileSet()
457     {
458         FileSet fileSet = new FileSet();
459         fileSet.setProject( getProject() );
460         fileSet.setDir( getLocalRepository().getPath() );
461         return fileSet;
462     }
463 
464     private void defineFilesetReference( String id, FileSet fileSet )
465     {
466         if ( id != null )
467         {
468             if ( !fileSet.hasPatterns() )
469             {
470                 fileSet.createExclude().setName( "**/**" );
471             }
472             getProject().addReference( id, fileSet );
473         }
474     }
475 
476     private void addArtifactToResult( ArtifactRepository localRepo, Artifact artifact,
477                                       FileSet toFileSet )
478     {
479         addArtifactToResult( localRepo, artifact, toFileSet, null, null );
480     }
481 
482     private void addArtifactToResult( ArtifactRepository localRepo, Artifact artifact,
483                                       FileSet toFileSet, Path path, ArtifactFilter filter )
484     {
485         String filename = localRepo.pathOf( artifact );
486 
487         toFileSet.createInclude().setName( filename );
488 
489         getProject().setProperty( artifact.getDependencyConflictId(), artifact.getFile().getAbsolutePath() );
490 
491         FileSet artifactFileSet = new FileSet();
492         artifactFileSet.setProject( getProject() );
493         artifactFileSet.setFile( artifact.getFile() );
494         getProject().addReference( artifact.getDependencyConflictId(), artifactFileSet );
495 
496         if ( path != null && ( filter == null || filter.include( artifact ) ) )
497         {
498             path.addFileset( artifactFileSet );
499         }
500     }
501 
502     private void resolveSource( ArtifactFactory artifactFactory, ArtifactResolver resolver,
503                                 List<ArtifactRepository> remoteArtifactRepositories, ArtifactRepository localRepo,
504                                 Artifact artifact, String classifier, FileSet sourcesFileSet )
505     {
506         Artifact sourceArtifact =
507             artifactFactory.createArtifactWithClassifier( artifact.getGroupId(), artifact.getArtifactId(),
508                                                           artifact.getVersion(), "java-source", classifier );
509         try
510         {
511             resolver.resolve( sourceArtifact, remoteArtifactRepositories, localRepo );
512 
513             addArtifactToResult( localRepo, sourceArtifact, sourcesFileSet );
514         }
515         catch ( ArtifactResolutionException e )
516         {
517             throw new BuildException( "Unable to resolve artifact: " + e.getMessage(), e );
518         }
519         catch ( ArtifactNotFoundException e )
520         {
521             // no source available: no problem, it's optional
522         }
523     }
524 
525     public List<Dependency> getDependencies()
526     {
527         return dependencies;
528     }
529 
530     public void addDependency( Dependency dependency )
531     {
532         dependencies.add( dependency );
533     }
534 
535     public String getPathId()
536     {
537         return pathId;
538     }
539 
540     public void setPathId( String pathId )
541     {
542         this.pathId = pathId;
543     }
544 
545     public String getFilesetId()
546     {
547         return filesetId;
548     }
549 
550     public void setSourcesFilesetId( String filesetId )
551     {
552         this.sourcesFilesetId = filesetId;
553     }
554 
555     public String getSourcesFilesetId()
556     {
557         return sourcesFilesetId;
558     }
559 
560     public void setJavadocFilesetId( String filesetId )
561     {
562         this.javadocFilesetId = filesetId;
563     }
564 
565     public String getJavadocFilesetId()
566     {
567         return javadocFilesetId;
568     }
569 
570     public void setFilesetId( String filesetId )
571     {
572         this.filesetId = filesetId;
573     }
574 
575     public String getVersionsId()
576     {
577         return versionsId;
578     }
579 
580     public void setVersionsId( String versionsId )
581     {
582         this.versionsId = versionsId;
583     }
584 
585     public void setVerbose( boolean verbose )
586     {
587         getProject().log( "Option \"verbose\" is deprecated.  Please use the standard Ant -v option.",
588                           Project.MSG_WARN );
589     }
590 
591     /**
592      * Use the Maven artifact filtering for a particular scope.  This
593      * uses the standard Maven ScopeArtifactFilter.
594      *
595      * @param useScope
596      */
597     public void setUseScope( String useScope )
598     {
599         this.useScope = useScope;
600     }
601 
602     public void setType( String type )
603     {
604         this.type = type;
605     }
606 
607     public void setPathType( String pathType )
608     {
609         this.pathType = pathType;
610     }
611 
612     public String getScopes()
613     {
614         return scopes;
615     }
616 
617     /**
618      * Only include artifacts that fall under one of the specified scopes.
619      *
620      * @return
621      */
622     public void setScopes( String scopes )
623     {
624         this.scopes = scopes;
625     }
626 
627     /**
628      * @deprecated
629      * @param addArtifactFileSetRefs
630      */
631     public void setAddArtifactFileSetRefs( boolean addArtifactFileSetRefs )
632     {
633         this.log( "Parameter addArtifactFileSetRefs is deprecated. A fileset ref is always created " +
634         		"for each dependency.", Project.MSG_WARN );
635     }
636 
637     public String getDependencyRefsBuildFile()
638     {
639         return dependencyRefsBuildFile;
640     }
641 
642     public void setDependencyRefsBuildFile( String dependencyRefsBuildFile )
643     {
644         this.dependencyRefsBuildFile = dependencyRefsBuildFile;
645     }
646 
647     public boolean isCacheDependencyRefs()
648     {
649         return cacheDependencyRefs;
650     }
651 
652     public void setCacheDependencyRefs( boolean cacheDependencyRefs )
653     {
654         this.cacheDependencyRefs = cacheDependencyRefs;
655     }
656 }