001package org.apache.maven.resolver.examples.util;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *  http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.io.IOException;
023import java.io.UncheckedIOException;
024import java.nio.charset.StandardCharsets;
025import java.nio.file.Files;
026import java.nio.file.Path;
027import java.util.ListIterator;
028import java.util.Objects;
029
030import org.eclipse.aether.AbstractRepositoryListener;
031import org.eclipse.aether.RepositoryEvent;
032import org.eclipse.aether.RequestTrace;
033import org.eclipse.aether.artifact.Artifact;
034import org.eclipse.aether.collection.CollectStepData;
035import org.eclipse.aether.graph.Dependency;
036import org.eclipse.aether.graph.DependencyNode;
037
038import static java.util.Objects.requireNonNull;
039
040/**
041 * A demo class building reverse tree using {@link CollectStepData} trace data provided in {@link RepositoryEvent}
042 * events fired during collection.
043 */
044public class ReverseTreeRepositoryListener
045        extends AbstractRepositoryListener
046{
047    private static final String EOL = System.lineSeparator();
048
049    @Override
050    public void artifactResolved( RepositoryEvent event )
051    {
052        requireNonNull( event, "event cannot be null" );
053
054        RequestTrace trace = event.getTrace();
055        CollectStepData collectStepTrace = null;
056        while ( trace != null )
057        {
058            if ( trace.getData() instanceof CollectStepData )
059            {
060                collectStepTrace = (CollectStepData) trace.getData();
061                break;
062            }
063            trace = trace.getParent();
064        }
065
066        if ( collectStepTrace == null )
067        {
068            return;
069        }
070
071        Artifact resolvedArtifact = event.getArtifact();
072        Artifact nodeArtifact = collectStepTrace.getNode().getArtifact();
073
074        if ( isInScope( resolvedArtifact, nodeArtifact ) )
075        {
076            Dependency node = collectStepTrace.getNode();
077            String trackingData = node + " (" + collectStepTrace.getContext() + ")" + EOL;
078            String indent = "";
079            ListIterator<DependencyNode> iter = collectStepTrace.getPath()
080                    .listIterator( collectStepTrace.getPath().size() );
081            while ( iter.hasPrevious() )
082            {
083                DependencyNode curr = iter.previous();
084                indent += "  ";
085                trackingData += indent + curr + " (" + collectStepTrace.getContext() + ")" + EOL;
086            }
087            try
088            {
089                Path trackingDir = resolvedArtifact.getFile().getParentFile().toPath().resolve( ".tracking" );
090                Files.createDirectories( trackingDir );
091                Path trackingFile = trackingDir.resolve( collectStepTrace.getPath().get( 0 )
092                        .getArtifact().toString().replace( ":", "_" ) );
093                Files.write( trackingFile, trackingData.getBytes( StandardCharsets.UTF_8 ) );
094                System.out.println( trackingData );
095            }
096            catch ( IOException e )
097            {
098                throw new UncheckedIOException( e );
099            }
100        }
101    }
102
103    /**
104     * The event "artifact resolved" if fired WHENEVER an artifact is resolved, BUT it happens also when an artifact
105     * descriptor (model, the POM) is being built, and parent (and parent of parent...) is being asked for. Hence, this
106     * method "filters" out in WHICH artifact are we interested in, but it intentionally neglects extension as
107     * ArtifactDescriptorReader modifies extension to "pom" during collect. So all we have to rely on is GAV only.
108     */
109    private boolean isInScope( Artifact artifact, Artifact nodeArtifact )
110    {
111        return Objects.equals( artifact.getGroupId(), nodeArtifact.getGroupId() )
112                && Objects.equals( artifact.getArtifactId(), nodeArtifact.getArtifactId() )
113                && Objects.equals( artifact.getVersion(), nodeArtifact.getVersion() );
114    }
115}