View Javadoc
1   package org.apache.maven.resolver.internal.ant.tasks;
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 java.io.File;
23  import java.io.IOException;
24  import java.util.ArrayList;
25  import java.util.Collection;
26  import java.util.Collections;
27  import java.util.HashMap;
28  import java.util.HashSet;
29  import java.util.LinkedList;
30  import java.util.List;
31  import java.util.Map;
32  
33  import org.apache.maven.resolver.internal.ant.AntRepoSys;
34  import org.apache.maven.resolver.internal.ant.Names;
35  import org.apache.maven.resolver.internal.ant.types.Dependencies;
36  import org.apache.maven.resolver.internal.ant.types.Pom;
37  import org.apache.tools.ant.BuildException;
38  import org.apache.tools.ant.Project;
39  import org.apache.tools.ant.ProjectComponent;
40  import org.apache.tools.ant.types.FileSet;
41  import org.apache.tools.ant.types.Reference;
42  import org.apache.tools.ant.types.resources.FileResource;
43  import org.apache.tools.ant.types.resources.Resources;
44  import org.apache.tools.ant.util.FileUtils;
45  import org.eclipse.aether.RepositorySystem;
46  import org.eclipse.aether.RepositorySystemSession;
47  import org.eclipse.aether.artifact.Artifact;
48  import org.eclipse.aether.graph.DependencyFilter;
49  import org.eclipse.aether.graph.DependencyNode;
50  import org.eclipse.aether.resolution.ArtifactRequest;
51  import org.eclipse.aether.resolution.ArtifactResolutionException;
52  import org.eclipse.aether.resolution.ArtifactResult;
53  import org.eclipse.aether.util.artifact.SubArtifact;
54  import org.eclipse.aether.util.filter.ScopeDependencyFilter;
55  
56  /**
57   */
58  public class Resolve
59      extends AbstractResolvingTask
60  {
61  
62      private List<ArtifactConsumer> consumers = new ArrayList<ArtifactConsumer>();
63  
64      private boolean failOnMissingAttachments;
65  
66      public void setFailOnMissingAttachments( boolean failOnMissingAttachments )
67      {
68          this.failOnMissingAttachments = failOnMissingAttachments;
69      }
70  
71      public Path createPath()
72      {
73          Path path = new Path();
74          consumers.add( path );
75          return path;
76      }
77  
78      public Files createFiles()
79      {
80          Files files = new Files();
81          consumers.add( files );
82          return files;
83      }
84  
85      public Props createProperties()
86      {
87          Props props = new Props();
88          consumers.add( props );
89          return props;
90      }
91  
92      private void validate()
93      {
94          for ( ArtifactConsumer consumer : consumers )
95          {
96              consumer.validate();
97          }
98  
99          Pom pom = AntRepoSys.getInstance( getProject() ).getDefaultPom();
100         if ( dependencies == null && pom != null )
101         {
102             log( "Using default pom for dependency resolution (" + pom.toString() + ")", Project.MSG_INFO );
103             dependencies = new Dependencies();
104             dependencies.setProject( getProject() );
105             getProject().addReference( Names.ID_DEFAULT_POM, pom );
106             dependencies.setPomRef( new Reference( getProject(), Names.ID_DEFAULT_POM ) );
107         }
108 
109         if ( dependencies != null )
110         {
111             dependencies.validate( this );
112         }
113         else
114         {
115             throw new BuildException( "No <dependencies> set for resolution" );
116         }
117     }
118 
119     @Override
120     public void execute()
121         throws BuildException
122     {
123         validate();
124 
125 
126         AntRepoSys sys = AntRepoSys.getInstance( getProject() );
127 
128         RepositorySystemSession session = sys.getSession( this, localRepository );
129         RepositorySystem system = sys.getSystem();
130         log( "Using local repository " + session.getLocalRepository(), Project.MSG_VERBOSE );
131 
132         DependencyNode root = collectDependencies().getRoot();
133         root.accept( new DependencyGraphLogger( this ) );
134 
135         Map<String, Group> groups = new HashMap<String, Group>();
136         for ( ArtifactConsumer consumer : consumers )
137         {
138             String classifier = consumer.getClassifier();
139             Group group = groups.get( classifier );
140             if ( group == null )
141             {
142                 group = new Group( classifier );
143                 groups.put( classifier, group );
144             }
145             group.add( consumer );
146         }
147 
148         for ( Group group : groups.values() )
149         {
150             group.createRequests( root );
151         }
152 
153         log( "Resolving artifacts", Project.MSG_INFO );
154 
155         for ( Group group : groups.values() )
156         {
157             List<ArtifactResult> results;
158             try
159             {
160                 results = system.resolveArtifacts( session, group.getRequests() );
161             }
162             catch ( ArtifactResolutionException e )
163             {
164                 if ( !group.isAttachments() || failOnMissingAttachments )
165                 {
166                     throw new BuildException( "Could not resolve artifacts: " + e.getMessage(), e );
167                 }
168                 results = e.getResults();
169                 for ( ArtifactResult result : results )
170                 {
171                     if ( result.isMissing() )
172                     {
173                         log( "Ignoring missing attachment " + result.getRequest().getArtifact(), Project.MSG_VERBOSE );
174                     }
175                     else if ( !result.isResolved() )
176                     {
177                         throw new BuildException( "Could not resolve artifacts: " + e.getMessage(), e );
178                     }
179                 }
180             }
181 
182             group.processResults( results, session );
183         }
184     }
185 
186     /**
187      */
188     public abstract static class ArtifactConsumer
189         extends ProjectComponent
190     {
191 
192         private DependencyFilter filter;
193 
194         public boolean accept( org.eclipse.aether.graph.DependencyNode node, List<DependencyNode> parents )
195         {
196             return filter == null || filter.accept( node, parents );
197         }
198 
199         public String getClassifier()
200         {
201             return null;
202         }
203 
204         public void validate()
205         {
206 
207         }
208 
209         public abstract void process( Artifact artifact, RepositorySystemSession session );
210 
211         public void setScopes( String scopes )
212         {
213             if ( filter != null )
214             {
215                 throw new BuildException( "You must not specify both 'scopes' and 'classpath'" );
216             }
217 
218             Collection<String> included = new HashSet<String>();
219             Collection<String> excluded = new HashSet<String>();
220 
221             String[] split = scopes.split( "[, ]" );
222             for ( String scope : split )
223             {
224                 scope = scope.trim();
225                 Collection<String> dst;
226                 if ( scope.startsWith( "-" ) || scope.startsWith( "!" ) )
227                 {
228                     dst = excluded;
229                     scope = scope.substring( 1 );
230                 }
231                 else
232                 {
233                     dst = included;
234                 }
235                 if ( scope.length() > 0 )
236                 {
237                     dst.add( scope );
238                 }
239             }
240 
241             filter = new ScopeDependencyFilter( included, excluded );
242         }
243 
244         public void setClasspath( String classpath )
245         {
246             if ( "compile".equals( classpath ) )
247             {
248                 setScopes( "provided,system,compile" );
249             }
250             else if ( "runtime".equals( classpath ) )
251             {
252                 setScopes( "compile,runtime" );
253             }
254             else if ( "test".equals( classpath ) )
255             {
256                 setScopes( "provided,system,compile,runtime,test" );
257             }
258             else
259             {
260                 throw new BuildException( "The classpath '" + classpath + "' is not defined"
261                     + ", must be one of 'compile', 'runtime' or 'test'" );
262             }
263         }
264 
265     }
266 
267     /**
268      */
269     public class Path
270         extends ArtifactConsumer
271     {
272 
273         private String refid;
274 
275         private org.apache.tools.ant.types.Path path;
276 
277         public void setRefId( String refId )
278         {
279             this.refid = refId;
280         }
281 
282         public void validate()
283         {
284             if ( refid == null )
285             {
286                 throw new BuildException( "You must specify the 'refid' for the path" );
287             }
288         }
289 
290         public void process( Artifact artifact, RepositorySystemSession session )
291         {
292             if ( path == null )
293             {
294                 path = new org.apache.tools.ant.types.Path( getProject() );
295                 getProject().addReference( refid, path );
296             }
297             File file = artifact.getFile();
298             path.add( new FileResource( file.getParentFile(), file.getName() ) );
299         }
300 
301     }
302 
303     /**
304      */
305     public class Files
306         extends ArtifactConsumer
307     {
308 
309         private static final String DEFAULT_LAYOUT = Layout.GID_DIRS + "/" + Layout.AID + "/" + Layout.BVER + "/"
310             + Layout.AID + "-" + Layout.VER + "-" + Layout.CLS + "." + Layout.EXT;
311 
312         private String refid;
313 
314         private String classifier;
315 
316         private File dir;
317 
318         private Layout layout;
319 
320         private FileSet fileset;
321 
322         private Resources resources;
323 
324         public void setRefId( String refId )
325         {
326             this.refid = refId;
327         }
328 
329         public String getClassifier()
330         {
331             return classifier;
332         }
333 
334         public void setAttachments( String attachments )
335         {
336             if ( "sources".equals( attachments ) )
337             {
338                 classifier = "*-sources";
339             }
340             else if ( "javadoc".equals( attachments ) )
341             {
342                 classifier = "*-javadoc";
343             }
344             else
345             {
346                 throw new BuildException( "The attachment type '" + attachments
347                     + "' is not defined, must be one of 'sources' or 'javadoc'" );
348             }
349         }
350 
351         public void setDir( File dir )
352         {
353             this.dir = dir;
354             if ( dir != null && layout == null )
355             {
356                 layout = new Layout( DEFAULT_LAYOUT );
357             }
358         }
359 
360         public void setLayout( String layout )
361         {
362             this.layout = new Layout( layout );
363         }
364 
365         public void validate()
366         {
367             if ( refid == null && dir == null )
368             {
369                 throw new BuildException( "You must either specify the 'refid' for the resource collection"
370                     + " or a 'dir' to copy the files to" );
371             }
372             if ( dir == null && layout != null )
373             {
374                 throw new BuildException( "You must not specify a 'layout' unless 'dir' is also specified" );
375             }
376         }
377 
378         public void process( Artifact artifact, RepositorySystemSession session )
379         {
380             if ( dir != null )
381             {
382                 if ( refid != null && fileset == null )
383                 {
384                     fileset = new FileSet();
385                     fileset.setProject( getProject() );
386                     fileset.setDir( dir );
387                     getProject().addReference( refid, fileset );
388                 }
389 
390                 String path = layout.getPath( artifact );
391 
392                 if ( fileset != null )
393                 {
394                     fileset.createInclude().setName( path );
395                 }
396 
397                 File src = artifact.getFile();
398                 File dst = new File( dir, path );
399 
400                 if ( src.lastModified() != dst.lastModified() || src.length() != dst.length() )
401                 {
402                     try
403                     {
404                         Resolve.this.log( "Copy " + src + " to " + dst, Project.MSG_VERBOSE );
405                         FileUtils.getFileUtils().copyFile( src, dst, null, true, true );
406                     }
407                     catch ( IOException e )
408                     {
409                         throw new BuildException( "Failed to copy artifact file " + src + " to " + dst + ": "
410                             + e.getMessage(), e );
411                     }
412                 }
413                 else
414                 {
415                     Resolve.this.log( "Omit to copy " + src + " to " + dst + ", seems unchanged", Project.MSG_VERBOSE );
416                 }
417             }
418             else
419             {
420                 if ( resources == null )
421                 {
422                     resources = new Resources();
423                     resources.setProject( getProject() );
424                     getProject().addReference( refid, resources );
425                 }
426 
427                 FileResource resource = new FileResource( artifact.getFile() );
428                 resource.setBaseDir( session.getLocalRepository().getBasedir() );
429                 resource.setProject( getProject() );
430                 resources.add( resource );
431             }
432         }
433 
434     }
435 
436     /**
437      */
438     public class Props
439         extends ArtifactConsumer
440     {
441 
442         private String prefix;
443 
444         private String classifier;
445 
446         public void setPrefix( String prefix )
447         {
448             this.prefix = prefix;
449         }
450 
451         public String getClassifier()
452         {
453             return classifier;
454         }
455 
456         public void setAttachments( String attachments )
457         {
458             if ( "sources".equals( attachments ) )
459             {
460                 classifier = "*-sources";
461             }
462             else if ( "javadoc".equals( attachments ) )
463             {
464                 classifier = "*-javadoc";
465             }
466             else
467             {
468                 throw new BuildException( "The attachment type '" + attachments
469                     + "' is not defined, must be one of 'sources' or 'javadoc'" );
470             }
471         }
472 
473         public void process( Artifact artifact, RepositorySystemSession session )
474         {
475             StringBuilder buffer = new StringBuilder( 256 );
476             if ( prefix != null && prefix.length() > 0 )
477             {
478                 buffer.append( prefix );
479                 if ( !prefix.endsWith( "." ) )
480                 {
481                     buffer.append( '.' );
482                 }
483             }
484             buffer.append( artifact.getGroupId() );
485             buffer.append( ':' );
486             buffer.append( artifact.getArtifactId() );
487             buffer.append( ':' );
488             buffer.append( artifact.getExtension() );
489             if ( artifact.getClassifier().length() > 0 )
490             {
491                 buffer.append( ':' );
492                 buffer.append( artifact.getClassifier() );
493             }
494 
495             String path = artifact.getFile().getAbsolutePath();
496 
497             getProject().setProperty( buffer.toString(), path );
498         }
499 
500     }
501 
502     private static class Group
503     {
504 
505         private String classifier;
506 
507         private List<ArtifactConsumer> consumers = new ArrayList<ArtifactConsumer>();
508 
509         private List<ArtifactRequest> requests = new ArrayList<ArtifactRequest>();
510 
511         Group( String classifier )
512         {
513             this.classifier = classifier;
514         }
515 
516         public boolean isAttachments()
517         {
518             return classifier != null;
519         }
520 
521         public void add( ArtifactConsumer consumer )
522         {
523             consumers.add( consumer );
524         }
525 
526         public void createRequests( DependencyNode node )
527         {
528             createRequests( node, new LinkedList<DependencyNode>() );
529         }
530 
531         private void createRequests( DependencyNode node, LinkedList<DependencyNode> parents )
532         {
533             if ( node.getDependency() != null )
534             {
535                 for ( ArtifactConsumer consumer : consumers )
536                 {
537                     if ( consumer.accept( node, parents ) )
538                     {
539                         ArtifactRequest request = new ArtifactRequest( node );
540                         if ( classifier != null )
541                         {
542                             request.setArtifact( new SubArtifact( request.getArtifact(), classifier, "jar" ) );
543                         }
544                         requests.add( request );
545                         break;
546                     }
547                 }
548             }
549 
550             parents.addFirst( node );
551 
552             for ( DependencyNode child : node.getChildren() )
553             {
554                 createRequests( child, parents );
555             }
556 
557             parents.removeFirst();
558         }
559 
560         public List<ArtifactRequest> getRequests()
561         {
562             return requests;
563         }
564 
565         public void processResults( List<ArtifactResult> results, RepositorySystemSession session )
566         {
567             for ( ArtifactResult result : results )
568             {
569                 if ( !result.isResolved() )
570                 {
571                     continue;
572                 }
573                 for ( ArtifactConsumer consumer : consumers )
574                 {
575                     if ( consumer.accept( result.getRequest().getDependencyNode(),
576                                           Collections.<DependencyNode>emptyList() ) )
577                     {
578                         consumer.process( result.getArtifact(), session );
579                     }
580                 }
581             }
582         }
583 
584     }
585 
586 }