All Downloads are FREE. Search and download functionalities are using the official Maven repository.

tel.panfilov.maven.extensions.imh.IMHPlaceholderResolver Maven / Gradle / Ivy

package tel.panfilov.maven.extensions.imh;

import org.apache.maven.RepositoryUtils;
import org.apache.maven.artifact.handler.ArtifactHandler;
import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
import org.apache.maven.model.DependencyManagement;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.PluginExecution;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.ProjectBuildingException;
import org.apache.maven.project.ProjectBuildingRequest;
import org.apache.maven.project.ProjectBuildingResult;
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.component.annotations.Requirement;
import org.codehaus.plexus.logging.Logger;
import org.codehaus.plexus.util.StringUtils;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.ArtifactTypeRegistry;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.collection.CollectRequest;
import org.eclipse.aether.graph.Dependency;
import org.eclipse.aether.graph.DependencyNode;
import org.eclipse.aether.graph.DependencyVisitor;
import org.eclipse.aether.resolution.DependencyRequest;
import org.eclipse.aether.resolution.DependencyResolutionException;
import org.eclipse.aether.resolution.DependencyResult;
import org.eclipse.aether.util.artifact.ArtifactIdUtils;

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;

@Component(role = IMHPlaceholderResolver.class, hint = "imh")
public class IMHPlaceholderResolver {

    public static final String PLACEHOLDER_RESOLUTION_ENABLED_FLAG = "imh.placeholders";

    public static final String PLACEHOLDER_START = "$D{";

    public static final String PLACEHOLDER_END = "}";

    @Requirement
    protected ArtifactHandlerManager artifactHandlerManager;

    @Requirement
    private RepositorySystem repositorySystem;

    @Requirement
    protected IMHWorkspaceReader workspaceReader;

    @Requirement
    private Logger logger;

    public void resolvePlaceholders(ProjectBuildingRequest request, ProjectBuildingResult result) throws ProjectBuildingException {
        if (!isPlaceholderResolutionEnabled(request, result)) {
            return;
        }

        MavenProject project = result.getProject();
        if (project == null) {
            return;
        }

        for (Plugin plugin : project.getBuildPlugins()) {
            traverseDom(request, project, (Xpp3Dom) plugin.getConfiguration());
            for (PluginExecution execution : plugin.getExecutions()) {
                traverseDom(request, project, (Xpp3Dom) execution.getConfiguration());
            }
        }
    }

    protected void traverseDom(ProjectBuildingRequest request, MavenProject project, Xpp3Dom dom) throws ProjectBuildingException {
        if (dom == null) {
            return;
        }

        String value = dom.getValue();
        if (value == null) {
            for (Xpp3Dom child : dom.getChildren()) {
                traverseDom(request, project, child);
            }
            return;
        }

        int index = value.indexOf(PLACEHOLDER_START);
        while (index > -1) {
            int end = value.indexOf(PLACEHOLDER_END, index);
            if (end < 0) {
                break;
            }
            String dependency = value.substring(index + PLACEHOLDER_START.length(), end);
            String paths = resolveDependencies(request, project, dependency);
            if (paths != null) {
                value = value.substring(0, index) + paths + value.substring(end + 1);
            }
            index = value.indexOf(PLACEHOLDER_START, end);
        }
        dom.setValue(value);
    }

    protected String resolveDependencies(ProjectBuildingRequest request, MavenProject project, String dependency) throws ProjectBuildingException {
        char separator = dependency.charAt(dependency.length() - 1);
        boolean transitive = isPathSeparator(separator);
        if (transitive) {
            dependency = dependency.substring(0, dependency.length() - 1);
        }

        RepositorySystemSession session = request.getRepositorySession();
        Artifact artifact = toArtifact(project, dependency);
        CollectRequest collectRequest = new CollectRequest();
        collectRequest.setRoot(new Dependency(artifact, null));
        collectRequest.setRepositories(RepositoryUtils.toRepos(request.getRemoteRepositories()));
        ArtifactTypeRegistry typeRegistry = RepositoryUtils.newArtifactTypeRegistry(artifactHandlerManager);
        DependencyManagement dependencyManagement = project.getDependencyManagement();
        if (dependencyManagement != null) {
            List managed = dependencyManagement.getDependencies()
                    .stream()
                    .map(d -> RepositoryUtils.toDependency(d, typeRegistry))
                    .collect(Collectors.toList());
            collectRequest.setManagedDependencies(managed);
        }

        try {
            DependencyRequest depRequest = new DependencyRequest(collectRequest, null);
            DependencyResult result = repositorySystem.resolveDependencies(session, depRequest);
            return extractArtifacts(result, transitive).stream()
                    .map(Artifact::getFile)
                    .map(File::getAbsolutePath)
                    .collect(Collectors.joining(String.valueOf(separator)));
        } catch (DependencyResolutionException ex) {
            throw new PlaceholderResolutionException(
                    project.getId(),
                    "Failed to resolve dependency " + dependency,
                    ex
            );
        }
    }

    protected boolean isPathSeparator(char separator) {
        return ',' == separator || ';' == separator || ':' == separator;
    }

    protected boolean isPlaceholderResolutionEnabled(ProjectBuildingRequest request, ProjectBuildingResult result) {
        Properties properties = request.getUserProperties();
        if ("true".equals(properties.get(PLACEHOLDER_RESOLUTION_ENABLED_FLAG))) {
            return true;
        }

        MavenProject project = result.getProject();
        if (project == null) {
            return false;
        }

        properties = project.getProperties();
        return "true".equals(properties.get(PLACEHOLDER_RESOLUTION_ENABLED_FLAG));
    }

    protected Artifact toArtifact(MavenProject project, String dependency) throws ProjectBuildingException {
        // groupId:artifactId[:version[:packaging[:classifier]]]
        boolean hasVersion = false;
        String[] tokens = StringUtils.split(dependency, ":");
        if (tokens.length < 2) {
            throw new PlaceholderResolutionException(
                    project.getId(),
                    "Invalid dependency specified: " + dependency,
                    null
            );
        }
        String groupId = tokens[0];
        String artifactId = tokens[1];
        String version = null;
        if (tokens.length >= 3) {
            hasVersion = true;
            version = tokens[2];
        }
        String packaging = "jar";
        if (tokens.length >= 4) {
            packaging = tokens[3];
        }
        String classifier = null;
        if (tokens.length >= 5) {
            classifier = tokens[4];
        }

        ArtifactHandler artifactHandler = artifactHandlerManager.getArtifactHandler(packaging);
        Artifact artifact = new DefaultArtifact(groupId, artifactId, classifier, artifactHandler.getExtension(), version);
        if (!hasVersion) {
            String key = ArtifactIdUtils.toVersionlessId(artifact);
            artifact = Optional.ofNullable(project.getManagedVersionMap().get(key))
                    .map(RepositoryUtils::toArtifact)
                    .orElseThrow(() -> new PlaceholderResolutionException(
                            project.getId(),
                            "No version was specified for artifact " + dependency,
                            null
                    ));
        }

        if (workspaceReader.isReactorArtifact(artifact)) {
            throw new PlaceholderResolutionException(
                    project.getId(),
                    "Artifact " + dependency + " is reactor artifact, IMH extension does not support such configurations",
                    null
            );
        }

        return artifact;
    }

    protected List extractArtifacts(DependencyResult dependencyResult, boolean transitive) {
        List artifacts = new ArrayList<>();
        DependencyNode dependencyNode = dependencyResult.getRoot();
        Artifact rootArtifact = dependencyResult.getRoot().getArtifact();
        artifacts.add(rootArtifact);
        if (!transitive) {
            return artifacts;
        }
        Set seen = new HashSet<>();
        CollectAllDependenciesVisitor visitor = new CollectAllDependenciesVisitor();
        dependencyNode.accept(visitor);
        for (Artifact artifact : visitor.getArtifacts()) {
            if (seen.add(ArtifactIdUtils.toId(artifact))) {
                artifacts.add(artifact);
            }
        }
        return artifacts;
    }

    static class CollectAllDependenciesVisitor implements DependencyVisitor {

        private boolean root = true;

        private final Set artifacts = new HashSet<>();

        @Override
        public boolean visitEnter(DependencyNode node) {
            if (root) {
                root = false;
                return true;
            }
            return artifacts.add(node.getArtifact());
        }

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

        public Set getArtifacts() {
            return artifacts;
        }
    }

    static class PlaceholderResolutionException extends ProjectBuildingException {

        public PlaceholderResolutionException(String projectId, String message, Throwable cause) {
            super(projectId, message, cause);
        }

        @Override
        public List getResults() {
            return Collections.emptyList();
        }

    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy