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

com.salesforce.servicelibs.canteen.BootstrapMojo Maven / Gradle / Ivy

/*
 *  Copyright (c) 2019, Salesforce.com, Inc.
 *  All rights reserved.
 *  Licensed under the BSD 3-Clause license.
 *  For full license text, see LICENSE.txt file in the repo root  or https://opensource.org/licenses/BSD-3-Clause
 */

package com.salesforce.servicelibs.canteen;

import com.google.common.io.ByteStreams;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.resolver.ArtifactResolutionException;
import org.apache.maven.artifact.resolver.ArtifactResolutionRequest;
import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
import org.apache.maven.artifact.resolver.ResolutionErrorHandler;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.Dependency;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.descriptor.PluginDescriptor;
import org.apache.maven.plugins.annotations.*;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectHelper;
import org.apache.maven.repository.RepositorySystem;
import org.apache.maven.shared.utils.Os;
import org.apache.maven.shared.utils.io.FileUtils;

import java.io.*;
import java.util.Arrays;
import java.util.List;
import java.util.Set;

import static java.util.Collections.emptyMap;
import static java.util.Collections.singleton;

@Mojo(name = "bootstrap", defaultPhase = LifecyclePhase.PACKAGE, requiresDependencyResolution = ResolutionScope.COMPILE, threadSafe = true)
public class BootstrapMojo extends AbstractMojo {
    private static final String CANTEEN_BOOTSTRAP = "canteen-bootstrap";
    private static final List PLATFORMS = Arrays.asList("osx-x86_64", "linux-x86_64", "windows-x86_64");

    @Component
    private RepositorySystem repositorySystem;

    @Component
    private ResolutionErrorHandler resolutionErrorHandler;

    @Component
    private MavenProjectHelper projectHelper;

    @Parameter(defaultValue = "${plugin}", readonly = true)
    private PluginDescriptor plugin;

    @Parameter(defaultValue = "${project}", readonly = true, required = true)
    private MavenProject project;

    @Parameter(defaultValue = "${session}", readonly = true, required = true)
    private MavenSession session;

    @Parameter(required = true, readonly = true, property = "localRepository")
    private ArtifactRepository localRepository;

    @Parameter(required = true, readonly = true, defaultValue = "${project.remoteArtifactRepositories}")
    private List remoteRepositories;

    @Parameter(defaultValue = "${project.build.directory}", required = true)
    private File outputDirectory;

    @Parameter(defaultValue = "${project.build.finalName}", readonly = true)
    private String finalName;

    /**
     * The directory (typically under /target) where Canteen bootstrap binaries are temporarily staged.
     */
    @Parameter(required = true, defaultValue = "${project.build.directory}/" + CANTEEN_BOOTSTRAP)
    private File canteenBootstrapDirectory;

    /**
     * Classifier to use when locating the artifact to bootstrap. If no classifier is provided, the module default
     * artifact will be used.
     */
    @Parameter(required = false)
    private String classifier;

    @Override
    public void execute() throws MojoExecutionException, MojoFailureException {
        for (String platform : PLATFORMS) {
            // Locate (and maybe download) the bootstrap artifact
            Artifact bootstrapArtifact = createDependencyArtifact(plugin.getGroupId(), CANTEEN_BOOTSTRAP, plugin.getVersion(), "exe", platform);
            File bootstrapFile = resolveBinaryArtifact(bootstrapArtifact);

            // Locate the source artifact
            Artifact sourceArtifact = getSourceArtifact();

            // Build the bootstrap file
            File targetFile = getTargetFile(platform);
            try (OutputStream targetStream = new FileOutputStream(targetFile)) {
                try (InputStream bootstrapStream = new FileInputStream(bootstrapFile)) {
                    ByteStreams.copy(bootstrapStream, targetStream);
                }
                try (InputStream sourceStream = new FileInputStream(sourceArtifact.getFile())) {
                    ByteStreams.copy(sourceStream, targetStream);
                }
                targetStream.flush();
            } catch (IOException ex) {
                throw new MojoFailureException("Failed to write bootstrapped jar", ex);
            }
            if (!Os.isFamily(Os.FAMILY_WINDOWS)) {
                targetFile.setExecutable(true);
            }

            // Attach the new artifact
            projectHelper.attachArtifact(project, "exe", platform, targetFile);
        }
    }

    /**
     * Creates a dependency artifact from a specification in
     * {@code groupId:artifactId:version[:type[:classifier]]} format.
     *
     * @return artifact object instance.
     */
    private Artifact createDependencyArtifact(String groupId, String artifactId, String version, String type, String classifier) {
        Dependency dependency = new Dependency();
        dependency.setGroupId(groupId);
        dependency.setArtifactId(artifactId);
        dependency.setVersion(version);
        dependency.setType(type);
        dependency.setClassifier(classifier);
        dependency.setScope(Artifact.SCOPE_RUNTIME);
        return repositorySystem.createDependencyArtifact(dependency);
    }

    /**
     * Downloads a binary artifact and installs it in the canteen bootstrap directory.
     * @param artifact the artifact to download.
     * @return a handle to the downloaded file.
     */
    private File resolveBinaryArtifact(final Artifact artifact) throws MojoExecutionException {
        final ArtifactResolutionResult result;
        try {
            final ArtifactResolutionRequest request = new ArtifactResolutionRequest()
                    .setArtifact(project.getArtifact())
                    .setResolveRoot(false)
                    .setResolveTransitively(false)
                    .setArtifactDependencies(singleton(artifact))
                    .setManagedVersionMap(emptyMap())
                    .setLocalRepository(localRepository)
                    .setRemoteRepositories(remoteRepositories)
                    .setOffline(session.isOffline())
                    .setForceUpdate(session.getRequest().isUpdateSnapshots())
                    .setServers(session.getRequest().getServers())
                    .setMirrors(session.getRequest().getMirrors())
                    .setProxies(session.getRequest().getProxies());

            result = repositorySystem.resolve(request);

            resolutionErrorHandler.throwErrors(request, result);
        } catch (final ArtifactResolutionException e) {
            throw new MojoExecutionException("Unable to resolve artifact: " + e.getMessage(), e);
        }

        final Set artifacts = result.getArtifacts();

        if (artifacts == null || artifacts.isEmpty()) {
            throw new MojoExecutionException("Unable to resolve artifact");
        }

        final Artifact resolvedBinaryArtifact = artifacts.iterator().next();
        getLog().debug("Resolved artifact: " + resolvedBinaryArtifact);

        // Copy the file to the project build directory and make it executable
        final File sourceFile = resolvedBinaryArtifact.getFile();
        final String sourceFileName = sourceFile.getName();
        final String targetFileName;
        if (Os.isFamily(Os.FAMILY_WINDOWS) && !sourceFileName.endsWith(".exe")) {
            targetFileName = sourceFileName + ".exe";
        } else {
            targetFileName = sourceFileName;
        }
        final File targetFile = new File(canteenBootstrapDirectory, targetFileName);
        if (targetFile.exists()) {
            // The file must have already been copied in a prior plugin execution/invocation
            getLog().debug("Executable file already exists: " + targetFile.getAbsolutePath());
            return targetFile;
        }
        try {
            FileUtils.forceMkdir(canteenBootstrapDirectory);
        } catch (final IOException e) {
            throw new MojoExecutionException("Unable to create directory " + canteenBootstrapDirectory, e);
        }
        try {
            FileUtils.copyFile(sourceFile, targetFile);
        } catch (final IOException e) {
            throw new MojoExecutionException("Unable to copy the file to " + canteenBootstrapDirectory, e);
        }

        getLog().debug("Executable file resolved: " + targetFile.getAbsolutePath());
        return targetFile;
    }

    /**
     * Return the source {@link Artifact} to repackage. If a classifier is specified and
     * an artifact with that classifier exists, it is used. Otherwise, the main artifact
     * is used.
     * @return the source artifact to repackage
     */
    private Artifact getSourceArtifact() {
        Artifact sourceArtifact = getArtifact(this.classifier);
        return (sourceArtifact != null) ? sourceArtifact : this.project.getArtifact();
    }

    private Artifact getArtifact(String classifier) {
        if (classifier != null) {
            for (Artifact attachedArtifact : this.project.getAttachedArtifacts()) {
                if (classifier.equals(attachedArtifact.getClassifier()) && attachedArtifact.getFile() != null &&
                        attachedArtifact.getFile().isFile()) {
                    return attachedArtifact;
                }
            }
        }
        return null;
    }

    /**
     * @return A file to write the bootstrapped artifact to
     */
    private File getTargetFile(String classifier) {
        if (!this.outputDirectory.exists()) {
            this.outputDirectory.mkdirs();
        }
        return new File(this.outputDirectory,
                this.finalName + "-" + classifier + ".exe");
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy