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             path.setLocation( artifact.getFile() );
298         }
299 
300     }
301 
302     /**
303      */
304     public class Files
305         extends ArtifactConsumer
306     {
307 
308         private static final String DEFAULT_LAYOUT = Layout.GID_DIRS + "/" + Layout.AID + "/" + Layout.BVER + "/"
309             + Layout.AID + "-" + Layout.VER + "-" + Layout.CLS + "." + Layout.EXT;
310 
311         private String refid;
312 
313         private String classifier;
314 
315         private File dir;
316 
317         private Layout layout;
318 
319         private FileSet fileset;
320 
321         private Resources resources;
322 
323         public void setRefId( String refId )
324         {
325             this.refid = refId;
326         }
327 
328         public String getClassifier()
329         {
330             return classifier;
331         }
332 
333         public void setAttachments( String attachments )
334         {
335             if ( "sources".equals( attachments ) )
336             {
337                 classifier = "*-sources";
338             }
339             else if ( "javadoc".equals( attachments ) )
340             {
341                 classifier = "*-javadoc";
342             }
343             else
344             {
345                 throw new BuildException( "The attachment type '" + attachments
346                     + "' is not defined, must be one of 'sources' or 'javadoc'" );
347             }
348         }
349 
350         public void setDir( File dir )
351         {
352             this.dir = dir;
353             if ( dir != null && layout == null )
354             {
355                 layout = new Layout( DEFAULT_LAYOUT );
356             }
357         }
358 
359         public void setLayout( String layout )
360         {
361             this.layout = new Layout( layout );
362         }
363 
364         public void validate()
365         {
366             if ( refid == null && dir == null )
367             {
368                 throw new BuildException( "You must either specify the 'refid' for the resource collection"
369                     + " or a 'dir' to copy the files to" );
370             }
371             if ( dir == null && layout != null )
372             {
373                 throw new BuildException( "You must not specify a 'layout' unless 'dir' is also specified" );
374             }
375         }
376 
377         public void process( Artifact artifact, RepositorySystemSession session )
378         {
379             if ( dir != null )
380             {
381                 if ( refid != null && fileset == null )
382                 {
383                     fileset = new FileSet();
384                     fileset.setProject( getProject() );
385                     fileset.setDir( dir );
386                     getProject().addReference( refid, fileset );
387                 }
388 
389                 String path = layout.getPath( artifact );
390 
391                 if ( fileset != null )
392                 {
393                     fileset.createInclude().setName( path );
394                 }
395 
396                 File src = artifact.getFile();
397                 File dst = new File( dir, path );
398 
399                 if ( src.lastModified() != dst.lastModified() || src.length() != dst.length() )
400                 {
401                     try
402                     {
403                         Resolve.this.log( "Copy " + src + " to " + dst, Project.MSG_VERBOSE );
404                         FileUtils.getFileUtils().copyFile( src, dst, null, true, true );
405                     }
406                     catch ( IOException e )
407                     {
408                         throw new BuildException( "Failed to copy artifact file " + src + " to " + dst + ": "
409                             + e.getMessage(), e );
410                     }
411                 }
412                 else
413                 {
414                     Resolve.this.log( "Omit to copy " + src + " to " + dst + ", seems unchanged", Project.MSG_VERBOSE );
415                 }
416             }
417             else
418             {
419                 if ( resources == null )
420                 {
421                     resources = new Resources();
422                     resources.setProject( getProject() );
423                     getProject().addReference( refid, resources );
424                 }
425 
426                 FileResource resource = new FileResource( artifact.getFile() );
427                 resource.setBaseDir( session.getLocalRepository().getBasedir() );
428                 resource.setProject( getProject() );
429                 resources.add( resource );
430             }
431         }
432 
433     }
434 
435     /**
436      */
437     public class Props
438         extends ArtifactConsumer
439     {
440 
441         private String prefix;
442 
443         private String classifier;
444 
445         public void setPrefix( String prefix )
446         {
447             this.prefix = prefix;
448         }
449 
450         public String getClassifier()
451         {
452             return classifier;
453         }
454 
455         public void setAttachments( String attachments )
456         {
457             if ( "sources".equals( attachments ) )
458             {
459                 classifier = "*-sources";
460             }
461             else if ( "javadoc".equals( attachments ) )
462             {
463                 classifier = "*-javadoc";
464             }
465             else
466             {
467                 throw new BuildException( "The attachment type '" + attachments
468                     + "' is not defined, must be one of 'sources' or 'javadoc'" );
469             }
470         }
471 
472         public void process( Artifact artifact, RepositorySystemSession session )
473         {
474             StringBuilder buffer = new StringBuilder( 256 );
475             if ( prefix != null && prefix.length() > 0 )
476             {
477                 buffer.append( prefix );
478                 if ( !prefix.endsWith( "." ) )
479                 {
480                     buffer.append( '.' );
481                 }
482             }
483             buffer.append( artifact.getGroupId() );
484             buffer.append( ':' );
485             buffer.append( artifact.getArtifactId() );
486             buffer.append( ':' );
487             buffer.append( artifact.getExtension() );
488             if ( artifact.getClassifier().length() > 0 )
489             {
490                 buffer.append( ':' );
491                 buffer.append( artifact.getClassifier() );
492             }
493 
494             String path = artifact.getFile().getAbsolutePath();
495 
496             getProject().setProperty( buffer.toString(), path );
497         }
498 
499     }
500 
501     private static class Group
502     {
503 
504         private String classifier;
505 
506         private List<ArtifactConsumer> consumers = new ArrayList<ArtifactConsumer>();
507 
508         private List<ArtifactRequest> requests = new ArrayList<ArtifactRequest>();
509 
510         Group( String classifier )
511         {
512             this.classifier = classifier;
513         }
514 
515         public boolean isAttachments()
516         {
517             return classifier != null;
518         }
519 
520         public void add( ArtifactConsumer consumer )
521         {
522             consumers.add( consumer );
523         }
524 
525         public void createRequests( DependencyNode node )
526         {
527             createRequests( node, new LinkedList<DependencyNode>() );
528         }
529 
530         private void createRequests( DependencyNode node, LinkedList<DependencyNode> parents )
531         {
532             if ( node.getDependency() != null )
533             {
534                 for ( ArtifactConsumer consumer : consumers )
535                 {
536                     if ( consumer.accept( node, parents ) )
537                     {
538                         ArtifactRequest request = new ArtifactRequest( node );
539                         if ( classifier != null )
540                         {
541                             request.setArtifact( new SubArtifact( request.getArtifact(), classifier, "jar" ) );
542                         }
543                         requests.add( request );
544                         break;
545                     }
546                 }
547             }
548 
549             parents.addFirst( node );
550 
551             for ( DependencyNode child : node.getChildren() )
552             {
553                 createRequests( child, parents );
554             }
555 
556             parents.removeFirst();
557         }
558 
559         public List<ArtifactRequest> getRequests()
560         {
561             return requests;
562         }
563 
564         public void processResults( List<ArtifactResult> results, RepositorySystemSession session )
565         {
566             for ( ArtifactResult result : results )
567             {
568                 if ( !result.isResolved() )
569                 {
570                     continue;
571                 }
572                 for ( ArtifactConsumer consumer : consumers )
573                 {
574                     if ( consumer.accept( result.getRequest().getDependencyNode(),
575                                           Collections.<DependencyNode>emptyList() ) )
576                     {
577                         consumer.process( result.getArtifact(), session );
578                     }
579                 }
580             }
581         }
582 
583     }
584 
585 }