org.jahia.utils.maven.plugin.support.Maven30AetherHelper.java Source code

Java tutorial

Introduction

Here is the source code for org.jahia.utils.maven.plugin.support.Maven30AetherHelper.java

Source

/**
 * ==========================================================================================
 * =                   JAHIA'S DUAL LICENSING - IMPORTANT INFORMATION                       =
 * ==========================================================================================
 *
 *                                 http://www.jahia.com
 *
 *     Copyright (C) 2002-2018 Jahia Solutions Group SA. All rights reserved.
 *
 *     THIS FILE IS AVAILABLE UNDER TWO DIFFERENT LICENSES:
 *     1/GPL OR 2/JSEL
 *
 *     1/ GPL
 *     ==================================================================================
 *
 *     IF YOU DECIDE TO CHOOSE THE GPL LICENSE, YOU MUST COMPLY WITH THE FOLLOWING TERMS:
 *
 *     This program is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *     GNU General Public License for more details.
 *
 *     You should have received a copy of the GNU General Public License
 *     along with this program. If not, see <http://www.gnu.org/licenses/>.
 *
 *
 *     2/ JSEL - Commercial and Supported Versions of the program
 *     ===================================================================================
 *
 *     IF YOU DECIDE TO CHOOSE THE JSEL LICENSE, YOU MUST COMPLY WITH THE FOLLOWING TERMS:
 *
 *     Alternatively, commercial and supported versions of the program - also known as
 *     Enterprise Distributions - must be used in accordance with the terms and conditions
 *     contained in a separate written agreement between you and Jahia Solutions Group SA.
 *
 *     If you are unsure which license is appropriate for your use,
 *     please contact the sales department at sales@jahia.com.
 */
package org.jahia.utils.maven.plugin.support;

import org.apache.commons.lang.StringUtils;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.handler.ArtifactHandler;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.project.MavenProject;
import org.jahia.utils.osgi.parsers.ParsingContext;
import org.sonatype.aether.RepositorySystem;
import org.sonatype.aether.RepositorySystemSession;
import org.sonatype.aether.collection.CollectRequest;
import org.sonatype.aether.collection.DependencyCollectionException;
import org.sonatype.aether.graph.Dependency;
import org.sonatype.aether.graph.DependencyNode;
import org.sonatype.aether.graph.DependencyVisitor;
import org.sonatype.aether.graph.Exclusion;
import org.sonatype.aether.repository.RemoteRepository;
import org.sonatype.aether.resolution.*;
import org.sonatype.aether.util.DefaultRepositorySystemSession;
import org.sonatype.aether.util.artifact.DefaultArtifact;
import org.sonatype.aether.util.graph.selector.AndDependencySelector;
import org.sonatype.aether.util.graph.selector.ExclusionDependencySelector;
import org.sonatype.aether.util.graph.selector.OptionalDependencySelector;
import org.sonatype.aether.util.graph.selector.ScopeDependencySelector;

import java.io.File;
import java.util.*;

import static org.jahia.utils.maven.plugin.support.MavenAetherHelperUtils.*;

/**
 * Artifact and dependency resolution helper which will be used for Maven 3.0.x execution environment.
 * 
 * @author Sergiy Shyrkov
 */
public class Maven30AetherHelper implements AetherHelper {

    class PackagerFinderDependencyVisitor implements DependencyVisitor {

        private List<String> dependencyTrail = null;
        private boolean excludedDependency = false;
        private Map<String, List<String>> foundPackages;
        private String packageName;
        private Deque<String> loopCheckTrail = null;

        public PackagerFinderDependencyVisitor(String packageName, Map<String, List<String>> foundPackages,
                boolean excludedDependency, List<String> dependencyTrail, Deque<String> loopCheckTrail) {
            this.packageName = packageName;
            this.foundPackages = foundPackages;
            this.excludedDependency = excludedDependency;
            this.dependencyTrail = new LinkedList<String>(dependencyTrail);
            if (loopCheckTrail == null) {
                this.loopCheckTrail = new ArrayDeque<String>();
            } else {
                this.loopCheckTrail = loopCheckTrail;
            }
        }

        @Override
        public boolean visitEnter(DependencyNode node) {
            if (!loopCheckTrail.contains(node.toString())) {
                loopCheckTrail.push(node.toString());
            } else {
                log.warn("Already visited dependency " + node.toString() + "!!!");
                loopCheckTrail.push(node.toString());
                return false;
            }
            if (node.getDependency().getArtifact().getFile() == null) {
                log.warn("No local file for artifact " + node.getDependency().getArtifact());
                return true;
            }
            if (node.getDependency().isOptional()) {
                log.debug("Processing optional file " + node.getDependency().getArtifact().getFile() + "...");
            }
            List<String> curTrail = new LinkedList<String>(dependencyTrail);
            String trailSuffix = "";
            if (!excludedDependency) {
                if (node.getDependency().isOptional()) {
                    trailSuffix = "[optional]";
                }
            } else {
                if (node.getDependency().isOptional()) {
                    trailSuffix = "[excluded+optional]";
                } else {
                    trailSuffix = "[excluded]";
                }
            }
            curTrail.add(node.toString() + trailSuffix);
            String trail = getTrail(dependencyTrail);
            if (doesJarHavePackageName(node.getDependency().getArtifact().getFile(), packageName, log)) {
                if (!excludedDependency) {
                    if (node.getDependency().isOptional()) {
                        log.info(trail + ": Found package " + packageName + " in optional artifact "
                                + node.getDependency().getArtifact().getFile());
                    } else {
                        log.info(trail + ": Found package " + packageName + " in artifact "
                                + node.getDependency().getArtifact().getFile());
                    }
                } else {
                    if (node.getDependency().isOptional()) {
                        log.warn(trail + ": Found package " + packageName + " in optional excluded artifact "
                                + node.getDependency().getArtifact().getFile());
                    } else {
                        log.warn(trail + ": Found package " + packageName + " in excluded artifact "
                                + node.getDependency().getArtifact().getFile());
                    }
                }
                foundPackages.put(packageName, curTrail);
            } else {
                for (Exclusion exclusion : node.getDependency().getExclusions()) {
                    log.debug(trail + ": Processing exclusion " + exclusion + " of artifact "
                            + node.getDependency().getArtifact());
                    DependencyNode exclusionNode = resolveExclusion(node, exclusion);
                    if (exclusionNode != null) {
                        exclusionNode.accept(new PackagerFinderDependencyVisitor(packageName, foundPackages, true,
                                new LinkedList<String>(curTrail), loopCheckTrail));
                    }
                }
            }
            return true;
        }

        @Override
        public boolean visitLeave(DependencyNode node) {
            loopCheckTrail.pop();
            return true;
        }
    }

    class PackageCollectorDependencyVisitor implements DependencyVisitor {

        private ArtifactProcessor artifactProcessor;
        private ArtifactHandler artifactHandler;
        private Deque<String> curTrail = null;
        private Deque<Artifact> artifactStack = new ArrayDeque<Artifact>();
        private Deque<ParsingContext> parsingContextStack = new ArrayDeque<ParsingContext>();
        private Deque<String> loopCheckTrail = new ArrayDeque<String>();
        private int depth = 0;

        public PackageCollectorDependencyVisitor(ArtifactProcessor artifactProcessor,
                ArtifactHandler artifactHandler, List<String> dependencyTrail, ParsingContext rootParsingContext) {
            this.artifactProcessor = artifactProcessor;
            this.artifactHandler = artifactHandler;
            this.curTrail = new ArrayDeque<String>(dependencyTrail);
            this.parsingContextStack.push(rootParsingContext);
        }

        @Override
        public boolean visitEnter(DependencyNode node) {
            depth++;
            Artifact mavenArtifact = new org.apache.maven.artifact.DefaultArtifact(
                    node.getDependency().getArtifact().getGroupId(),
                    node.getDependency().getArtifact().getArtifactId(),
                    node.getDependency().getArtifact().getVersion(), node.getDependency().getScope(),
                    node.getDependency().getArtifact().getExtension(),
                    StringUtils.isBlank(node.getDependency().getArtifact().getClassifier()) ? null
                            : node.getDependency().getArtifact().getClassifier(),
                    artifactHandler);
            boolean visitChildren = true;
            String trailSuffix = "";
            if (node.getDependency().isOptional()) {
                trailSuffix = "[optional]";
            }
            curTrail.push(node.toString() + trailSuffix);
            String trail = getTrailPadding(curTrail);
            if (!loopCheckTrail.contains(node.toString())) {
                loopCheckTrail.push(node.toString());
            } else {
                log.warn("Already visited dependency " + node.toString() + "!!!");
                visitChildren = false;
            }
            // log.debug(trail + "Starting visit of artifact " + node.getDependency().getArtifact() + "...");
            if (node.getDependency().getArtifact().getFile() == null) {
                log.warn(trail + "No local file for artifact " + node.getDependency().getArtifact());
                log.info(trail + "Resolving artifact " + node.getDependency().getArtifact() + "...");
                ArtifactRequest request = new ArtifactRequest();
                request.setArtifact(node.getDependency().getArtifact());
                request.setRepositories(remoteRepos);
                try {
                    ArtifactResult artifactResult = repoSystem.resolveArtifact(moreDependenciesSession, request);
                    node.getDependency().getArtifact().setFile(artifactResult.getArtifact().getFile());
                } catch (ArtifactResolutionException e) {
                    log.warn(trail + "Error resolving artifact " + node.getDependency().getArtifact() + ": "
                            + e.getMessage());
                    visitChildren = false;
                }
            }
            mavenArtifact.setFile(node.getDependency().getArtifact().getFile());
            mavenArtifact.setOptional(node.getDependency().isOptional());
            artifactStack.push(mavenArtifact);
            ParsingContext parsingContext = null;
            ParsingContext parentParsingContext = null;
            if (parsingContextStack.size() > 0) {
                parentParsingContext = parsingContextStack.peek();
            }
            if (mavenArtifact.getFile() != null) {
                boolean external = artifactProcessor.isExternal(mavenArtifact);
                try {
                    parsingContext = artifactProcessor.enterArtifact(mavenArtifact,
                            node.getDependency().isOptional(), external, parentParsingContext, trail, depth);
                } catch (MojoExecutionException e) {
                    e.printStackTrace();
                    visitChildren = false;
                }
                parsingContextStack.push(parsingContext);
            }
            return visitChildren;
        }

        @Override
        public boolean visitLeave(DependencyNode node) {
            depth--;
            boolean visitSiblings = true;
            String trail = getTrailPadding(curTrail);
            // log.debug(trail + "Ending visit of artifact " + node.getDependency().getArtifact() + "...");
            Artifact mavenArtifact = null;
            if (node.getDependency().getArtifact().getArtifactId().equals(artifactStack.peek().getArtifactId())) {
                mavenArtifact = artifactStack.pop();
            } else {
                log.warn(trail + "Expected artifact " + node.getDependency().getArtifact().getArtifactId()
                        + " on artifact stack but got " + artifactStack.peek().getArtifactId());
            }
            if (mavenArtifact != null && mavenArtifact.getFile() != null) {
                try {
                    boolean external = false;
                    if (Artifact.SCOPE_PROVIDED.equals(node.getDependency().getScope())) {
                        external = true;
                    }
                    artifactProcessor.exitArtifact(mavenArtifact, node.getDependency().isOptional(), external,
                            trail, parsingContextStack.peek(), depth);
                } catch (MojoExecutionException e) {
                    e.printStackTrace();
                }
                parsingContextStack.pop();
            }
            curTrail.pop();
            loopCheckTrail.pop();
            return visitSiblings;
        }
    }

    private Log log;

    private List<RemoteRepository> remoteRepos;

    // private RepositorySystemSession repoSession;

    private RepositorySystem repoSystem;

    private Map<String, DependencyNode> resolvedDependencyNodes = new HashMap<String, DependencyNode>();

    DefaultRepositorySystemSession moreDependenciesSession;

    public Maven30AetherHelper(RepositorySystem repoSystem, RepositorySystemSession repoSession,
            List<RemoteRepository> remoteRepos, Log log) {
        this.repoSystem = repoSystem;
        // this.repoSession = repoSession;
        // we build our own custom session to re-introduce the collection of "provided" dependencies that are excluded by the default Maven session.
        this.moreDependenciesSession = new DefaultRepositorySystemSession(repoSession);
        AndDependencySelector andDependencySelector = new AndDependencySelector(new ScopeDependencySelector("test"),
                new OptionalDependencySelector(), new ExclusionDependencySelector());
        this.moreDependenciesSession.setDependencySelector(andDependencySelector);
        this.remoteRepos = remoteRepos;
        this.log = log;
    }

    private Set<String> findInWarDependencies(Artifact warArtifact, final String artifactId) {
        final Set<String> versions = new LinkedHashSet<String>();
        String artifactCoords = warArtifact.getGroupId() + ":" + warArtifact.getArtifactId() + ":"
                + warArtifact.getType() + ":" + warArtifact.getBaseVersion();
        DependencyNode node = null;
        if (resolvedDependencyNodes.containsKey(artifactCoords)) {
            node = resolvedDependencyNodes.get(artifactCoords);
        }

        if (node == null) {
            try {

                log.info("Resolving artifact " + artifactCoords + "...");
                ArtifactRequest request = new ArtifactRequest();
                request.setArtifact(new DefaultArtifact(artifactCoords));
                request.setRepositories(remoteRepos);

                Dependency dependency = new Dependency(new DefaultArtifact(artifactCoords), "compile");

                CollectRequest collectRequest = new CollectRequest();
                collectRequest.setRoot(dependency);
                collectRequest.setRepositories(remoteRepos);

                node = repoSystem.collectDependencies(moreDependenciesSession, collectRequest).getRoot();

                DependencyRequest dependencyRequest = new DependencyRequest(node, null);

                repoSystem.resolveDependencies(moreDependenciesSession, dependencyRequest);

                resolvedDependencyNodes.put(artifactCoords, node);

            } catch (DependencyCollectionException e) {
                log.error(e);
            } catch (DependencyResolutionException e) {
                log.error(e);
            }
        }

        if (node != null) {
            node.accept(new DependencyVisitor() {
                @Override
                public boolean visitEnter(DependencyNode node) {
                    if (node.getDependency().getArtifact().getFile().getName().equals(artifactId)) {
                        versions.add(node.getDependency().getArtifact().getBaseVersion());
                    }
                    return true;
                }

                @Override
                public boolean visitLeave(DependencyNode node) {
                    return true;
                }
            });

        }
        return versions;

    }

    @Override
    public Map<String, List<String>> findPackages(MavenProject project, List<String> packageNames)
            throws MojoExecutionException {
        final Map<String, List<String>> foundPackages = new HashMap<String, List<String>>();
        for (Artifact artifact : project.getArtifacts()) {
            if (artifact.isOptional()) {
                log.debug("Processing optional dependency " + artifact + "...");
            }
            if (!artifact.getType().equals("jar")) {
                log.warn("Found non JAR artifact " + artifact);
            }
            for (String packageName : packageNames) {
                if (doesJarHavePackageName(artifact.getFile(), packageName, log)) {
                    List<String> trail = new LinkedList<String>(artifact.getDependencyTrail());
                    if (artifact.isOptional()) {
                        trail.add("[optional]");
                    }
                    log.info("Found package " + packageName + " in " + getTrail(trail));
                    foundPackages.put(packageName, trail);
                }
            }
        }
        for (String packageName : packageNames) {
            if (!foundPackages.containsKey(packageName)) {
                log.warn("Couldn't find " + packageName
                        + " in normal project dependencies, will now search optional (and excluded) dependencies");
                for (Artifact artifact : project.getArtifacts()) {
                    if (artifact.isOptional()) {
                        log.debug("Processing optional artifact " + artifact + "...");
                    }
                    DependencyNode dependencyNode = getDependencyNode(getCoords(artifact));
                    if (dependencyNode != null) {
                        List<String> trail = new LinkedList<String>(artifact.getDependencyTrail());
                        dependencyNode.accept(new PackagerFinderDependencyVisitor(packageName, foundPackages, false,
                                trail, null));
                    }
                }
            }
        }
        return foundPackages;
    }

    private DependencyNode getDependencyNode(String artifactCoords) {
        ArtifactRequest request = new ArtifactRequest();
        DefaultArtifact aetherArtifact = new DefaultArtifact(artifactCoords);
        request.setArtifact(aetherArtifact);
        request.setRepositories(remoteRepos);

        Dependency dependency = new Dependency(aetherArtifact, "compile");

        CollectRequest collectRequest = new CollectRequest();
        collectRequest.setRoot(dependency);
        collectRequest.setRepositories(remoteRepos);

        DependencyNode dependencyNode = null;
        try {
            dependencyNode = repoSystem.collectDependencies(moreDependenciesSession, collectRequest).getRoot();
            DependencyRequest dependencyRequest = new DependencyRequest(dependencyNode, null);

            DependencyResult dependencyResult = repoSystem.resolveDependencies(moreDependenciesSession,
                    dependencyRequest);

        } catch (DependencyCollectionException e) {
            log.error("Error collecting dependencies for " + artifactCoords + ": " + e.getMessage());
        } catch (DependencyResolutionException e) {
            log.error("Error resolving dependencies for " + artifactCoords + ": " + e.getMessage());
        }
        return dependencyNode;
    }

    @Override
    public List<String> getDependencyVersion(MavenProject project, String artifactFileName)
            throws MojoExecutionException {
        if (project == null || StringUtils.isEmpty(artifactFileName)) {
            return Collections.emptyList();
        }

        Set<String> versions = new LinkedHashSet<String>();

        Set<Artifact> artifacts = project.getArtifacts();
        for (Artifact artifact : artifacts) {
            if (artifact.getType().equals("war")) {
                // we have a WAR dependency, we will look in that project dependencies seperately since it is not
                // directly transitive.
                versions.addAll(findInWarDependencies(artifact, artifactFileName));
            } else if (artifact.getFile().getName().equals(artifactFileName)) {
                versions.add(artifact.getBaseVersion());
            }
        }

        return new LinkedList<String>(versions);
    }

    @Override
    public File resolveArtifactFile(String coords) throws MojoExecutionException {
        ArtifactRequest request = new ArtifactRequest();
        request.setArtifact(new DefaultArtifact(coords));
        request.setRepositories(remoteRepos);
        org.sonatype.aether.artifact.Artifact artifact = null;
        try {
            artifact = repoSystem.resolveArtifact(moreDependenciesSession, request).getArtifact();
        } catch (ArtifactResolutionException e) {
            throw new MojoExecutionException(e.getMessage(), e);
        }

        return artifact != null ? artifact.getFile() : null;
    }

    @Override
    public void processArtifactAndDependencies(Artifact artifact, boolean optional,
            ArtifactProcessor artifactProcessor, ArtifactHandler artifactHandler,
            ParsingContext rootParsingContext) {
        DependencyNode dependencyNode = getDependencyNode(getCoords(artifact));
        if (dependencyNode != null) {
            List<String> trail = new LinkedList<String>(artifact.getDependencyTrail());
            dependencyNode.setScope(artifact.getScope()); // copy the scope from the artifact for the root node.
            dependencyNode.accept(new PackageCollectorDependencyVisitor(artifactProcessor, artifactHandler, trail,
                    rootParsingContext));
        }
    }

    private DependencyNode resolveExclusion(DependencyNode dependencyNode, Exclusion exclusion) {
        if (dependencyNode.getDependency().getArtifact().getGroupId().equals(exclusion.getGroupId())
                && dependencyNode.getDependency().getArtifact().getArtifactId().equals(exclusion.getArtifactId())) {
            return dependencyNode;
        }
        for (DependencyNode childNode : dependencyNode.getChildren()) {
            DependencyNode childDependency = resolveExclusion(childNode, exclusion);
            if (childDependency != null) {
                return childDependency;
            }
        }
        return null;
    }

}