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 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.length() > 0) {
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 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         public void validate() {
229             if (refid == null) {
230                 throw new BuildException("You must specify the 'refid' for the path");
231             }
232         }
233 
234         public void process(Artifact artifact, RepositorySystemSession session) {
235             if (path == null) {
236                 path = new org.apache.tools.ant.types.Path(getProject());
237                 getProject().addReference(refid, path);
238             }
239             File file = artifact.getFile();
240             path.add(new FileResource(file.getParentFile(), file.getName()));
241         }
242     }
243 
244     /**
245      */
246     public class Files extends ArtifactConsumer {
247 
248         private static final String DEFAULT_LAYOUT = Layout.GID_DIRS + "/" + Layout.AID + "/" + Layout.BVER + "/"
249                 + Layout.AID + "-" + Layout.VER + "-" + Layout.CLS + "." + Layout.EXT;
250 
251         private String refid;
252 
253         private String classifier;
254 
255         private File dir;
256 
257         private Layout layout;
258 
259         private FileSet fileset;
260 
261         private Resources resources;
262 
263         public void setRefId(String refId) {
264             this.refid = refId;
265         }
266 
267         public String getClassifier() {
268             return classifier;
269         }
270 
271         public void setAttachments(String attachments) {
272             if ("sources".equals(attachments)) {
273                 classifier = "*-sources";
274             } else if ("javadoc".equals(attachments)) {
275                 classifier = "*-javadoc";
276             } else {
277                 throw new BuildException("The attachment type '" + attachments
278                         + "' is not defined, must be one of 'sources' or 'javadoc'");
279             }
280         }
281 
282         public void setDir(File dir) {
283             this.dir = dir;
284             if (dir != null && layout == null) {
285                 layout = new Layout(DEFAULT_LAYOUT);
286             }
287         }
288 
289         public void setLayout(String layout) {
290             this.layout = new Layout(layout);
291         }
292 
293         public void validate() {
294             if (refid == null && dir == null) {
295                 throw new BuildException("You must either specify the 'refid' for the resource collection"
296                         + " or a 'dir' to copy the files to");
297             }
298             if (dir == null && layout != null) {
299                 throw new BuildException("You must not specify a 'layout' unless 'dir' is also specified");
300             }
301         }
302 
303         public void process(Artifact artifact, RepositorySystemSession session) {
304             if (dir != null) {
305                 if (refid != null && fileset == null) {
306                     fileset = new FileSet();
307                     fileset.setProject(getProject());
308                     fileset.setDir(dir);
309                     getProject().addReference(refid, fileset);
310                 }
311 
312                 String path = layout.getPath(artifact);
313 
314                 if (fileset != null) {
315                     fileset.createInclude().setName(path);
316                 }
317 
318                 File src = artifact.getFile();
319                 File dst = new File(dir, path);
320 
321                 if (src.lastModified() != dst.lastModified() || src.length() != dst.length()) {
322                     try {
323                         Resolve.this.log("Copy " + src + " to " + dst, Project.MSG_VERBOSE);
324                         FileUtils.getFileUtils().copyFile(src, dst, null, true, true);
325                     } catch (IOException e) {
326                         throw new BuildException(
327                                 "Failed to copy artifact file " + src + " to " + dst + ": " + e.getMessage(), e);
328                     }
329                 } else {
330                     Resolve.this.log("Omit to copy " + src + " to " + dst + ", seems unchanged", Project.MSG_VERBOSE);
331                 }
332             } else {
333                 if (resources == null) {
334                     resources = new Resources();
335                     resources.setProject(getProject());
336                     getProject().addReference(refid, resources);
337                 }
338 
339                 FileResource resource = new FileResource(artifact.getFile());
340                 resource.setBaseDir(session.getLocalRepository().getBasedir());
341                 resource.setProject(getProject());
342                 resources.add(resource);
343             }
344         }
345     }
346 
347     /**
348      */
349     public class Props extends ArtifactConsumer {
350 
351         private String prefix;
352 
353         private String classifier;
354 
355         public void setPrefix(String prefix) {
356             this.prefix = prefix;
357         }
358 
359         public String getClassifier() {
360             return classifier;
361         }
362 
363         public void setAttachments(String attachments) {
364             if ("sources".equals(attachments)) {
365                 classifier = "*-sources";
366             } else if ("javadoc".equals(attachments)) {
367                 classifier = "*-javadoc";
368             } else {
369                 throw new BuildException("The attachment type '" + attachments
370                         + "' is not defined, must be one of 'sources' or 'javadoc'");
371             }
372         }
373 
374         public void process(Artifact artifact, RepositorySystemSession session) {
375             StringBuilder buffer = new StringBuilder(256);
376             if (prefix != null && prefix.length() > 0) {
377                 buffer.append(prefix);
378                 if (!prefix.endsWith(".")) {
379                     buffer.append('.');
380                 }
381             }
382             buffer.append(artifact.getGroupId());
383             buffer.append(':');
384             buffer.append(artifact.getArtifactId());
385             buffer.append(':');
386             buffer.append(artifact.getExtension());
387             if (artifact.getClassifier().length() > 0) {
388                 buffer.append(':');
389                 buffer.append(artifact.getClassifier());
390             }
391 
392             String path = artifact.getFile().getAbsolutePath();
393 
394             getProject().setProperty(buffer.toString(), path);
395         }
396     }
397 
398     private static class Group {
399 
400         private String classifier;
401 
402         private List<ArtifactConsumer> consumers = new ArrayList<>();
403 
404         private List<ArtifactRequest> requests = new ArrayList<>();
405 
406         Group(String classifier) {
407             this.classifier = classifier;
408         }
409 
410         public boolean isAttachments() {
411             return classifier != null;
412         }
413 
414         public void add(ArtifactConsumer consumer) {
415             consumers.add(consumer);
416         }
417 
418         public void createRequests(DependencyNode node) {
419             createRequests(node, new LinkedList<>());
420         }
421 
422         private void createRequests(DependencyNode node, LinkedList<DependencyNode> parents) {
423             if (node.getDependency() != null) {
424                 for (ArtifactConsumer consumer : consumers) {
425                     if (consumer.accept(node, parents)) {
426                         ArtifactRequest request = new ArtifactRequest(node);
427                         if (classifier != null) {
428                             request.setArtifact(new SubArtifact(request.getArtifact(), classifier, "jar"));
429                         }
430                         requests.add(request);
431                         break;
432                     }
433                 }
434             }
435 
436             parents.addFirst(node);
437 
438             for (DependencyNode child : node.getChildren()) {
439                 createRequests(child, parents);
440             }
441 
442             parents.removeFirst();
443         }
444 
445         public List<ArtifactRequest> getRequests() {
446             return requests;
447         }
448 
449         public void processResults(List<ArtifactResult> results, RepositorySystemSession session) {
450             for (ArtifactResult result : results) {
451                 if (!result.isResolved()) {
452                     continue;
453                 }
454                 for (ArtifactConsumer consumer : consumers) {
455                     if (consumer.accept(
456                             result.getRequest().getDependencyNode(), Collections.<DependencyNode>emptyList())) {
457                         consumer.process(result.getArtifact(), session);
458                     }
459                 }
460             }
461         }
462     }
463 }