org.acmsl.dockerfile.maven.DockerfileMojo.java Source code

Java tutorial

Introduction

Here is the source code for org.acmsl.dockerfile.maven.DockerfileMojo.java

Source

/*
                    Dockerfile Maven Plugin
    
Copyright (C) 2014-today  Jose San Leandro Armendariz
                          chous@acm-sl.org
    
This library 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 2 of the License, or any later version.
    
This library 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 library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    
Thanks to ACM S.L. for distributing this library under the GPL license.
Contact info: jose.sanleandro@acm-sl.com
    
 ******************************************************************************
 *
 * Filename: DockerfileMojo.java
 *
 * Author: Jose San Leandro Armendariz.
 *
 * Description: Executes Dockerfile plugin.
 */
package org.acmsl.dockerfile.maven;

/*
 * Importing some ACM-SL Java Commons classes.
 */
import org.acmsl.commons.logging.UniqueLogFactory;
import org.acmsl.commons.utils.io.FileUtils;

/*
 * Importing some Maven classes.
 */
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.ArtifactUtils;
import org.apache.maven.artifact.deployer.ArtifactDeploymentException;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.repository.ArtifactRepositoryFactory;
import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugin.deploy.AbstractDeployMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.apache.maven.repository.RepositorySystem;

/*
 * Importing NotNull annotations.
 */
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/*
 * Importing some JDK classes.
 */
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/*
 * Importing checkthread.org annotations.
 */
import org.checkthread.annotations.ThreadSafe;

/**
 * Executes Dockerfile plugin.
 * @author <a href="mailto:chous@acm-sl.org">Jose San Leandro Armendariz</a>
 * Created: 2014/12/01
 */
@SuppressWarnings("unused")
@ThreadSafe
@Mojo(name = Literals.DOCKERFILE_L, defaultPhase = LifecyclePhase.GENERATE_SOURCES, threadSafe = true, executionStrategy = "once-per-session")
public class DockerfileMojo extends AbstractDeployMojo {
    /**
     * The repo syntax pattern.
     */
    private static final Pattern ALT_REPO_SYNTAX_PATTERN = Pattern.compile("(.+)::(.+)::(.+)");

    /**
     * The location of pom.properties within the jar file.
     */
    protected static final String POM_PROPERTIES_LOCATION = "META-INF/maven/org.acmsl/dockerfile-maven-plugin/pom.properties";

    /**
     * The output directory.
     */
    @Parameter(name = Literals.OUTPUT_DIR_CC, property = Literals.OUTPUT_DIR_CC, required = false, defaultValue = "${project.build.outputDirectory}/META-INF/")
    @Nullable
    private File m__OutputDir;

    /**
     * The output directory.
     */
    @Parameter(name = Literals.TEMPLATE_L, property = Literals.TEMPLATE_L, required = true)
    @NotNull
    private File m__Template;

    /**
     * The file encoding.
     */
    @Parameter(name = Literals.ENCODING_L, property = Literals.ENCODING_L, required = false, defaultValue = "${project.build.sourceEncoding}")
    @NotNull
    private String m__strEncoding;

    /**
     * Whether to deploy the Dockerfile or not.
     */
    @Parameter(name = Literals.DEPLOY_L, property = Literals.DEPLOY_L, required = false, defaultValue = "true")
    @Nullable
    private boolean m__bDeploy;

    /**
     * Whether to deploy snapshots with a unique version or not.
     */
    @Parameter(property = Literals.UNIQUE_VERSION_L, defaultValue = "true")
    private boolean m__bUniqueVersion;

    /**
     * The Dockerfile classifier.
     */
    @Parameter(name = Literals.CLASSIFIER_L, property = Literals.CLASSIFIER_L, required = false, defaultValue = "Dockerfile")
    @NotNull
    private String m__strClassifier;

    /**
     * The number of retries when deployment fails.
     */
    @Parameter(name = Literals.DEPLOYMENT_RETRIES, property = Literals.DEPLOYMENT_RETRIES, required = false, defaultValue = "1")
    private int m__iRetryFailedDeploymentCount;

    /**
     * Map that contains the layouts.
     */
    @Component(role = ArtifactRepositoryLayout.class)
    @NotNull
    private Map<String, ArtifactRepositoryLayout> repositoryLayouts;

    /**
     * The current build session instance. This is used for toolchain manager API calls.
     * @readonly
     */
    @Parameter(defaultValue = "${session}", required = true, readonly = true)
    private MavenSession session;

    /**
     * Component used to create and deploy the Dockerfile artifact.
     */
    @Component
    @NotNull
    protected RepositorySystem repositorySystem;

    /**
     * Component used to create a repository.
     */
    @Component
    ArtifactRepositoryFactory repositoryFactory;

    /**
     * Specifies an alternative repository to which the project artifacts should be deployed ( other than those
     * specified in &lt;distributionManagement&gt; ). <br/>
     * Format: id::layout::url
     * <dl>
     * <dt>id</dt>
     * <dd>The id can be used to pick up the correct credentials from the settings.xml</dd>
     * <dt>layout</dt>
     * <dd>Either <code>default</code> for the Maven2 layout or <code>legacy</code> for the Maven1 layout. Maven3 also
     * uses the <code>default</code> layout.</dd>
     * <dt>url</dt>
     * <dd>The location of the repository</dd>
     * </dl>
     */
    @Parameter(property = "altDeploymentRepository")
    @Nullable
    private String altDeploymentRepository;

    /**
     * The alternative repository to use when the project has a snapshot version.
     * @since 2.8
     */
    @Parameter(property = "altSnapshotDeploymentRepository")
    @Nullable
    private String altSnapshotDeploymentRepository;

    /**
     * The alternative repository to use when the project has a final version.
     * @since 2.8
     */
    @Parameter(property = "altReleaseDeploymentRepository")
    @Nullable
    private String altReleaseDeploymentRepository;

    /**
     * Specifies the output directory.
     * @param outputDir such directory.
     */
    protected final void immutableSetOutputDir(@NotNull final File outputDir) {
        m__OutputDir = outputDir;
    }

    /**
     * Specifies the output directory.
     * @param outputDir such directory.
     */
    public void setOutputDir(@NotNull final File outputDir) {
        immutableSetOutputDir(outputDir);
    }

    /**
     * Returns the output directory.
     * @return such directory.
     */
    @Nullable
    protected final File immutableGetOutputDir() {
        return m__OutputDir;
    }

    /**
     * Returns the output directory.
     * @return such directory.
     */
    @Nullable
    public File getOutputDir() {
        @Nullable
        final File result;

        @Nullable
        final String aux = System.getProperty(Literals.DOCKERFILE_OUTPUT_DIR);

        if (aux == null) {
            result = immutableGetOutputDir();
        } else {
            result = new File(aux);
        }

        return result;
    }

    /**
     * Specifies the template.
     * @param template such template.
     */
    protected final void immutableSetTemplate(@NotNull final File template) {
        m__Template = template;
    }

    /**
     * Specifies the template.
     * @param template such template.
     */
    public void setTemplate(@NotNull final File template) {
        immutableSetTemplate(template);
    }

    /**
     * Returns the template.
     * @return such template.
     */
    @NotNull
    protected final File immutableGetTemplate() {
        return m__Template;
    }

    /**
     * Returns the template.
     * @return such template.
     */
    @NotNull
    public File getTemplate() {
        @NotNull
        final File result;

        @Nullable
        final String aux = System.getProperty(Literals.DOCKERFILE_TEMPLATE);

        if (aux == null) {
            result = immutableGetTemplate();
        } else {
            result = new File(aux);
        }

        return result;
    }

    /**
     * Specifies the encoding.
     * @param encoding the encoding.
     */
    protected final void immutableSetEncoding(@NotNull final String encoding) {
        m__strEncoding = encoding;
    }

    /**
     * Specifies the encoding.
     * @param encoding the encoding.
     */
    public void setEncoding(@NotNull final String encoding) {
        immutableSetEncoding(encoding);
    }

    /**
     * Retrieves the encoding.
     * @return such information.
     */
    @Nullable
    protected final String immutableGetEncoding() {
        return m__strEncoding;
    }

    /**
     * Retrieves the encoding.
     * @return such information.
     */
    @Nullable
    public String getEncoding() {
        @Nullable
        String result = System.getProperty(Literals.DOCKERFILE_ENCODING);

        if (result == null) {
            result = immutableGetEncoding();
        }

        return result;
    }

    /**
     * Specifies whether to deploy the Dockerfile or not.
     * @param deploy such condition.
     */
    protected final void immutableSetDeploy(final boolean deploy) {
        m__bDeploy = deploy;
    }

    /**
     * Specifies whether to deploy the Dockerfile or not.
     * @param deploy such condition.
     */
    public void setDeploy(final boolean deploy) {
        immutableSetDeploy(deploy);
    }

    /**
     * Retrieves whether to deploy the Dockerfile or not.
     * @return such information.
     */
    protected final boolean immutableGetDeploy() {
        return m__bDeploy;
    }

    /**
     * Retrieves whether to deploy the Dockerfile or not.
     * @return such information.
     */
    public boolean getDeploy() {
        @Nullable
        final boolean result;

        @Nullable
        final String property = System.getProperty(Literals.DOCKERFILE_DEPLOY);

        if (property == null) {
            result = immutableGetDeploy();
        } else {
            result = Boolean.valueOf(property);
        }

        return result;
    }

    /**
     * Specifies whether to use unique versions when deploying the Dockerfile or not.
     * @param uniqueVersion such condition.
     */
    protected final void immutableSetUniqueVersion(final boolean uniqueVersion) {
        m__bUniqueVersion = uniqueVersion;
    }

    /**
     * Specifies whether to use unique versions when deploying the Dockerfile or not.
     * @param uniqueVersion such condition.
     */
    public void setUniqueVersion(final boolean uniqueVersion) {
        immutableSetUniqueVersion(uniqueVersion);
    }

    /**
     * Retrieves whether to use unique versions when deploying the Dockerfile or not.
     * @return such information.
     */
    protected final boolean immutableGetUniqueVersion() {
        return m__bUniqueVersion;
    }

    /**
     * Retrieves whether to use unique versions when deploying the Dockerfile or not.
     * @return such information.
     */
    public boolean getUniqueVersion() {
        @Nullable
        final boolean result;

        @Nullable
        final String property = System.getProperty(Literals.DOCKERFILE_UNIQUE_VERSION);

        if (property == null) {
            result = immutableGetUniqueVersion();
        } else {
            result = Boolean.valueOf(property);
        }

        return result;
    }

    /**
     * Specifies the classifier.
     * @param classifier the classifier.
     */
    protected final void immutableSetClassifier(@NotNull final String classifier) {
        m__strClassifier = classifier;
    }

    /**
     * Specifies the classifier.
     * @param classifier the classifier.
     */
    public void setClassifier(@NotNull final String classifier) {
        immutableSetClassifier(classifier);
    }

    /**
     * Retrieves the classifier.
     * @return such information.
     */
    @Nullable
    protected final String immutableGetClassifier() {
        return m__strClassifier;
    }

    /**
     * Retrieves the classifier.
     * @return such information.
     */
    @Nullable
    public String getClassifier() {
        @Nullable
        String result = System.getProperty(Literals.DOCKERFILE_CLASSIFIER);

        if (result == null) {
            result = immutableGetClassifier();
        }

        return result;
    }

    /**
     * Specifies how many times a failed deployment will be retried before giving up.
     * @param retryFailedDeploymentCount such count.
     */
    protected final void immutableSetDeploymentRetries(final int retryFailedDeploymentCount) {
        m__iRetryFailedDeploymentCount = retryFailedDeploymentCount;
    }

    /**
     * Specifies how many times a failed deployment will be retried before giving up.
     * @param retryFailedDeploymentCount such count.
     */
    public void setDeploymentRetries(final int retryFailedDeploymentCount) {
        immutableSetDeploymentRetries(retryFailedDeploymentCount);
    }

    /**
     * Retrieves how many times a failed deployment will be retried before giving up.
     * @return such information.
     */
    protected final int immutableGetDeploymentRetries() {
        return m__iRetryFailedDeploymentCount;
    }

    /**
     * Retrieves how many times a failed deployment will be retried before giving up.
     * @return such information.
     */
    public int getDeploymentRetries() {
        @Nullable
        final int result;

        @Nullable
        final String property = System.getProperty(Literals.DOCKERFILE_DEPLOYMENT_RETRIES);

        if (property == null) {
            result = immutableGetDeploymentRetries();
        } else {
            result = Integer.valueOf(property);
        }

        return result;
    }

    /**
     * Retrieves the layout.
     * @param id the id.
     * @return the layout.
     */
    @NotNull
    public ArtifactRepositoryLayout getLayout(@NotNull final String id) throws MojoExecutionException {
        @Nullable
        final ArtifactRepositoryLayout result = repositoryLayouts.get(id);

        if (result == null) {
            throw new MojoExecutionException("Invalid repository layout: " + id);
        }

        return result;
    }

    /**
     * Executes Dockerfile Maven plugin.
     * @throws org.apache.maven.plugin.MojoExecutionException if the process fails.
     */
    @Override
    public void execute() throws MojoExecutionException {
        execute(getLog());
    }

    /**
     * Executes Dockerfile Maven plugin.
     * @param log the Maven log.
     * @throws MojoExecutionException if the process fails.
     */
    protected void execute(@NotNull final Log log) throws MojoExecutionException {
        execute(log, retrieveOwnVersion(retrievePomProperties(log)), retrieveTargetProject(), getOutputDir(),
                getTemplate(), getEncoding(), getDeploy(), getUniqueVersion(), getClassifier(),
                getDeploymentRetries());
    }

    /**
     * Retrieves the version of Dockerfile Maven Plugin currently running.
     * @param properties the pom.properties information.
     * @return the version entry.
     */
    @NotNull
    protected String retrieveOwnVersion(@Nullable final Properties properties) {
        @NotNull
        final String result;

        if ((properties != null) && (properties.containsKey(Literals.VERSION_L))) {
            result = properties.getProperty(Literals.VERSION_L);
        } else {
            result = Literals.UNKNOWN_L;
        }

        return result;
    }

    /**
     * Retrieves the target project.
     * @return such version.
     */
    @NotNull
    protected MavenProject retrieveTargetProject() {
        return this.session.getCurrentProject();
    }

    /**
     * Executes Dockerfile Maven Plugin.
     * @param log the Maven log.
     * @param ownVersion the Dockerfile Maven Plugin version.
     * @param targetProject the target project.
     * @param outputDir the output dir.
     * @param template the template.
     * @param encoding the file encoding.
     * @param deploy whether to deploy the Dockerfile or not.
     * @param uniqueVersion whether to use unique versions when deploying the Dockerfile or not.
     * @param classifier the Dockerfile classifier.
     * @param retryFailedDeploymentCount how many times a failed deployment will be retried before giving up.
     * @throws MojoExecutionException if the process fails.
     */
    protected void execute(@NotNull final Log log, @NotNull final String ownVersion,
            @NotNull final MavenProject targetProject, @NotNull final File outputDir, @NotNull final File template,
            @NotNull final String encoding, final boolean deploy, final boolean uniqueVersions,
            @NotNull final String classifier, final int retryFailedDeploymentCount) throws MojoExecutionException {
        boolean running = false;

        boolean outputDirFine = false;
        boolean templateFine = false;

        if (outputDir != null) {
            if ((!outputDir.exists()) && (!outputDir.mkdirs())) {
                log.warn("Cannot create output folder: " + outputDir);
            } else {
                outputDirFine = true;
            }
        } else {
            log.error(Literals.OUTPUT_DIR_CC + " is null");
        }

        if (template != null) {
            if (!template.exists()) {
                log.warn("Dockerfile template does not exist: " + template);
            } else {
                templateFine = true;
            }
        } else {
            log.error(Literals.TEMPLATE_L + " is null");
        }

        @NotNull
        final Charset actualEncoding;

        if (encoding == null) {
            actualEncoding = Charset.defaultCharset();

            log.warn(Literals.ENCODING_L + " not specified. Using " + actualEncoding);
        } else {
            actualEncoding = Charset.forName(encoding);
        }

        if ((outputDirFine) && (templateFine)) {
            log.info("Running Dockerfile Maven Plugin " + ownVersion + " on " + targetProject.getGroupId() + ":"
                    + targetProject.getArtifactId() + ":" + targetProject.getVersion());

            running = true;

            @Nullable
            File dockerfile = null;

            try {
                dockerfile = generateDockerfile(outputDir, template, targetProject, ownVersion, actualEncoding,
                        FileUtils.getInstance());
            } catch (@NotNull final SecurityException securityException) {
                log.error("Not allowed to write output file in " + outputDir.getAbsolutePath(), securityException);
            } catch (@NotNull final IOException ioException) {
                log.error("Cannot write output file in " + outputDir.getAbsolutePath(), ioException);
            }

            if (deploy) {
                try {
                    @NotNull
                    final Artifact artifact = repositorySystem.createArtifactWithClassifier(
                            targetProject.getGroupId(), targetProject.getArtifactId(), targetProject.getVersion(),
                            "", classifier);

                    @NotNull
                    final ArtifactRepository repo = getDeploymentRepository(targetProject, altDeploymentRepository,
                            altReleaseDeploymentRepository, altSnapshotDeploymentRepository);

                    @NotNull
                    final ArtifactRepository deploymentRepository = repositoryFactory
                            .createDeploymentArtifactRepository(repo.getId(), repo.getUrl(), getLayout("default"),
                                    uniqueVersions);

                    deploy(dockerfile, artifact, deploymentRepository, getLocalRepository(),
                            retryFailedDeploymentCount);
                } catch (@NotNull final ArtifactDeploymentException e) {
                    throw new MojoExecutionException("Error deploying Dockerfile", e);
                } catch (@NotNull final MojoFailureException e) {
                    throw new MojoExecutionException("Error deploying Dockerfile", e);
                }
            }
        }

        if (!running) {
            log.error("NOT running Dockerfile Maven Plugin " + ownVersion);
            throw new MojoExecutionException("Dockerfile Maven Plugin could not start");
        }
    }

    /**
     * Retrieves the pom.properties bundled within the Dockerfile Maven Plugin jar.
     * @param log the Maven log.
     * @return such information.
     */
    @Nullable
    protected Properties retrievePomProperties(@NotNull final Log log) {
        @Nullable
        Properties result = null;

        try {
            @Nullable
            final InputStream pomProperties = getClass().getClassLoader()
                    .getResourceAsStream(POM_PROPERTIES_LOCATION);

            if (pomProperties != null) {
                result = new Properties();

                result.load(pomProperties);
            }
        } catch (@NotNull final IOException ioException) {
            log.warn(Literals.CANNOT_READ_MY_OWN_POM + POM_PROPERTIES_LOCATION, ioException);
        }

        return result;
    }

    /**
     * Initializes the logging.
     * @param commonsLoggingLog such log.
     */
    protected void initLogging(@NotNull final org.apache.commons.logging.Log commonsLoggingLog) {
        UniqueLogFactory.initializeInstance(commonsLoggingLog);
    }

    /**
     * Generates the dockerfile.
     * @param outputDir the output path.
     * @param template the Dockerfile.stg template.
     * @param target the target project.
     * @param ownVersion my own version.
     * @param encoding the file encoding.
     * @param fileUtils the {@link FileUtils} instance.
     * @return the generated file.
     * @throws IOException if the file cannot be written.
     * @throws SecurityException if we're not allowed to write the file.
     */
    protected File generateDockerfile(@NotNull final File outputDir, @NotNull final File template,
            @NotNull final MavenProject target, @NotNull final String ownVersion, @NotNull final Charset encoding,
            @NotNull final FileUtils fileUtils) throws IOException, SecurityException {
        @NotNull
        final File result;

        @NotNull
        final Map<String, Object> input = new HashMap<String, Object>();

        input.put(Literals.T_U, target);
        input.put(Literals.VERSION_L, ownVersion);

        @NotNull
        final DockerfileGenerator generator = new DockerfileGenerator(input, template);

        @NotNull
        final String contents = generator.generateDockerfile();

        result = new File(outputDir.getAbsolutePath() + File.separator + "Dockerfile");

        fileUtils.writeFile(result, contents, encoding);

        return result;
    }

    /**
     * Retrieves the deployment repository.
     * @param project the project.
     * @param altDeploymentRepository the deployment repository.
     * @param altReleaseDeploymentRepository the release repository.
     * @param altSnapshotDeploymentRepository the snapshot repository.
     * @return the repository.
     */
    protected ArtifactRepository getDeploymentRepository(@NotNull final MavenProject project,
            @Nullable final String altDeploymentRepository, @Nullable final String altReleaseDeploymentRepository,
            @Nullable final String altSnapshotDeploymentRepository)
            throws MojoExecutionException, MojoFailureException {
        @Nullable
        ArtifactRepository result = null;

        @Nullable
        final String altDeploymentRepo;

        if (ArtifactUtils.isSnapshot(project.getVersion()) && altSnapshotDeploymentRepository != null) {
            altDeploymentRepo = altSnapshotDeploymentRepository;
        } else if (!ArtifactUtils.isSnapshot(project.getVersion()) && altReleaseDeploymentRepository != null) {
            altDeploymentRepo = altReleaseDeploymentRepository;
        } else {
            altDeploymentRepo = altDeploymentRepository;
        }

        if (altDeploymentRepo != null) {
            getLog().info("Using alternate deployment repository " + altDeploymentRepo);

            @NotNull
            final Matcher matcher = ALT_REPO_SYNTAX_PATTERN.matcher(altDeploymentRepo);

            if (!matcher.matches()) {
                throw new MojoFailureException(altDeploymentRepo, "Invalid syntax for repository.",
                        "Invalid syntax for alternative repository. Use \"id::layout::url\".");
            } else {
                @NotNull
                final String id = matcher.group(1).trim();
                @NotNull
                final String layout = matcher.group(2).trim();
                @NotNull
                final String url = matcher.group(3).trim();

                @NotNull
                final ArtifactRepositoryLayout repoLayout = getLayout(layout);

                result = repositoryFactory.createDeploymentArtifactRepository(id, url, repoLayout, true);
            }
        }

        if (result == null) {
            result = project.getDistributionManagementArtifactRepository();
        }

        if (result == null) {
            @NotNull
            final String msg = "Deployment failed: repository element was not specified in the POM inside"
                    + " distributionManagement element or in -DaltDeploymentRepository=id::layout::url parameter";

            throw new MojoExecutionException(msg);
        }

        return result;
    }
}