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