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.examples.util;
20  
21  import java.io.IOException;
22  import java.io.UncheckedIOException;
23  import java.nio.charset.StandardCharsets;
24  import java.nio.file.Files;
25  import java.nio.file.Path;
26  import java.util.ListIterator;
27  import java.util.Objects;
28  
29  import org.eclipse.aether.AbstractRepositoryListener;
30  import org.eclipse.aether.RepositoryEvent;
31  import org.eclipse.aether.RequestTrace;
32  import org.eclipse.aether.artifact.Artifact;
33  import org.eclipse.aether.collection.CollectStepData;
34  import org.eclipse.aether.graph.Dependency;
35  import org.eclipse.aether.graph.DependencyNode;
36  
37  import static java.util.Objects.requireNonNull;
38  
39  /**
40   * A demo class building reverse tree using {@link CollectStepData} trace data provided in {@link RepositoryEvent}
41   * events fired during collection.
42   */
43  public class ReverseTreeRepositoryListener extends AbstractRepositoryListener {
44      private static final String EOL = System.lineSeparator();
45  
46      @Override
47      public void artifactResolved(RepositoryEvent event) {
48          requireNonNull(event, "event cannot be null");
49  
50          RequestTrace trace = event.getTrace();
51          CollectStepData collectStepTrace = null;
52          while (trace != null) {
53              if (trace.getData() instanceof CollectStepData) {
54                  collectStepTrace = (CollectStepData) trace.getData();
55                  break;
56              }
57              trace = trace.getParent();
58          }
59  
60          if (collectStepTrace == null) {
61              return;
62          }
63  
64          Artifact resolvedArtifact = event.getArtifact();
65          Artifact nodeArtifact = collectStepTrace.getNode().getArtifact();
66  
67          if (isInScope(resolvedArtifact, nodeArtifact)) {
68              Dependency node = collectStepTrace.getNode();
69              String trackingData = node + " (" + collectStepTrace.getContext() + ")" + EOL;
70              String indent = "";
71              ListIterator<DependencyNode> iter = collectStepTrace
72                      .getPath()
73                      .listIterator(collectStepTrace.getPath().size());
74              while (iter.hasPrevious()) {
75                  DependencyNode curr = iter.previous();
76                  indent += "  ";
77                  trackingData += indent + curr + " (" + collectStepTrace.getContext() + ")" + EOL;
78              }
79              try {
80                  Path trackingDir =
81                          resolvedArtifact.getFile().getParentFile().toPath().resolve(".tracking");
82                  Files.createDirectories(trackingDir);
83                  Path trackingFile = trackingDir.resolve(collectStepTrace
84                          .getPath()
85                          .get(0)
86                          .getArtifact()
87                          .toString()
88                          .replace(":", "_"));
89                  Files.write(trackingFile, trackingData.getBytes(StandardCharsets.UTF_8));
90                  System.out.println(trackingData);
91              } catch (IOException e) {
92                  throw new UncheckedIOException(e);
93              }
94          }
95      }
96  
97      /**
98       * The event "artifact resolved" if fired WHENEVER an artifact is resolved, BUT it happens also when an artifact
99       * descriptor (model, the POM) is being built, and parent (and parent of parent...) is being asked for. Hence, this
100      * method "filters" out in WHICH artifact are we interested in, but it intentionally neglects extension as
101      * ArtifactDescriptorReader modifies extension to "pom" during collect. So all we have to rely on is GAV only.
102      */
103     private boolean isInScope(Artifact artifact, Artifact nodeArtifact) {
104         return Objects.equals(artifact.getGroupId(), nodeArtifact.getGroupId())
105                 && Objects.equals(artifact.getArtifactId(), nodeArtifact.getArtifactId())
106                 && Objects.equals(artifact.getVersion(), nodeArtifact.getVersion());
107     }
108 }