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

org.apache.sling.maven.projectsupport.PreparePackageMojo Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.sling.maven.projectsupport;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Properties;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
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.plugins.annotations.ResolutionScope;
import org.apache.sling.maven.projectsupport.bundlelist.v1_0_0.BundleList;
import org.codehaus.plexus.archiver.Archiver;
import org.codehaus.plexus.archiver.ArchiverException;
import org.codehaus.plexus.archiver.UnArchiver;
import org.codehaus.plexus.archiver.jar.JarArchiver;
import org.codehaus.plexus.archiver.manager.ArchiverManager;
import org.codehaus.plexus.archiver.manager.NoSuchArchiverException;
import org.codehaus.plexus.util.FileUtils;

/**
 * Initialize a Sling application project by extracting bundles into the correct
 * locations.
 */
@Mojo(name = "prepare-package", defaultPhase = LifecyclePhase.PREPARE_PACKAGE, requiresDependencyResolution = ResolutionScope.TEST)
public class PreparePackageMojo extends AbstractLaunchpadFrameworkMojo {

    /**
     * The output directory for the default bundles in a WAR-packaged project,
     * the base JAR (in the subdirectory named in the baseDestination
     * parameter), and any additional bundles.
     */
    @Parameter(defaultValue = "${project.build.directory}/launchpad-bundles")
    private File warOutputDirectory;

    /**
     * The project's packaging type.
     */
    @Parameter(property = "project.packaging")
    private String packaging;

    /**
     * The definition of the base JAR.
     */
    @Parameter
    private ArtifactDefinition base;

    /**
     * The definition of the package to be included to provide web support for
     * JAR-packaged projects (i.e. pax-web).
     */
    @Parameter
    private ArtifactDefinition jarWebSupport;

    /**
     * The project's build output directory (i.e. target/classes).
     */
    @Parameter(property = "project.build.outputDirectory", readonly = true)
    private File buildOutputDirectory;

    /**
     * The temp directory (i.e. target/maven-launchpad-plugintmp).
     */
    @Parameter(defaultValue = "${project.build.outputDirectory}/maven-launchpad-plugintmp", readonly = true)
    private File tempDirectory;

    /**
     * To look up Archiver/UnArchiver implementations
     */
    @Component
    private ArchiverManager archiverManager;

    /**
     * The Jar archiver.
     */
    @Component(role = Archiver.class, hint = "jar")
    private JarArchiver jarArchiver;

    @Override
    public void executeWithArtifacts() throws MojoExecutionException, MojoFailureException {
        copyBaseArtifact();
        copyBundles(getInitializedBundleList(), getOutputDirectory());
        copyConfigurationFiles();
        if (JAR.equals(packaging)) {
            unpackBaseArtifact();
        }
    }

    @Override
    protected void initArtifactDefinitions(Properties dependencies) {
        if (base == null) {
            base = new ArtifactDefinition();
        }
        base.initDefaults(dependencies.getProperty("base"));

        if (jarWebSupport == null) {
            jarWebSupport = new ArtifactDefinition();
        }
        jarWebSupport.initDefaults(dependencies.getProperty("jarWebSupport"));
    }

    /**
     * Add the JAR Web Support bundle to the bundle list.
     */
    @Override
    protected void initBundleList(BundleList bundleList) {
        if (packaging.equals(JAR)) {
            bundleList.add(jarWebSupport.toBundleList());
        }
    }

    /**
     * Patch the sling properties
     */
    private void patchSlingProperties(final File dest, final Properties additionalProps)
    throws MojoExecutionException {
        final File origSlingProps = new File(dest, "sling.properties");
        if ( !origSlingProps.exists() ) {
            throw new MojoExecutionException("sling.properties not found at " + origSlingProps);
        }

        // read original properties
        final Properties orig = new Properties();
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(origSlingProps);
            orig.load(fis);
        } catch (final IOException ioe) {
            throw new MojoExecutionException("Unable to read " + origSlingProps, ioe);
        } finally {
            if ( fis != null ) {
                try { fis.close(); } catch (final IOException ignore) {}
            }
        }

        // patch
        final Enumeration keys = additionalProps.keys();
        if ( keys.hasMoreElements() ) {
            getLog().info("Patching sling.properties");
        }
        while ( keys.hasMoreElements() ) {
            final Object key = keys.nextElement();
            orig.put(key, additionalProps.get(key));
        }

        /// and save
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(origSlingProps);
            orig.store(fos, null);
        } catch (final IOException ioe) {
            throw new MojoExecutionException("Unable to save " + origSlingProps, ioe);
        } finally {
            if ( fis != null ) {
                try { fis.close(); } catch (final IOException ignore) {}
            }
        }
    }

    /**
     * Patch the sling bootstrap command file
     */
    private void patchSlingBootstrap(final File dest, final String additionalCmd)
    throws MojoExecutionException {
        getLog().info("Patching sling_bootstrap.txt");
        final File origSlingCmd = new File(dest, "sling_bootstrap.txt");
        FileWriter writer = null;

        /// and write
        try {
            writer = new FileWriter(origSlingCmd);

            writer.write(additionalCmd);
        } catch (final IOException ioe) {
            throw new MojoExecutionException("Unable to save " + origSlingCmd, ioe);
        } finally {
            if ( writer != null ) {
                try { writer.close(); } catch (final IOException ignore) {}
            }
        }
    }

    private void copyBaseArtifact() throws MojoExecutionException {
        Artifact artifact = getBaseArtifact();
        if (artifact == null) {
            throw new MojoExecutionException(
                    String.format("Project doesn't have a base dependency of groupId %s and artifactId %s",
                                    base.getGroupId(), base.getArtifactId()));
        }
        File destinationDir = new File(getOutputDirectory(), baseDestination);
        File destinationFile = new File(destinationDir, artifact
                .getArtifactId()
                + "." + artifact.getArtifactHandler().getExtension());

        // check if custom sling.properties file or bootstrap command exists
        final Properties additionalProps = this.getSlingProperties(JAR.equals(this.packaging));
        final String bootstrapCmd = this.getSlingBootstrap(JAR.equals(this.packaging));
        if ( additionalProps != null || bootstrapCmd != null ) {
            // unpack to a temp destination
            final File dest = new File(this.tempDirectory, "basejar");
            try {
                unpack(artifact.getFile(), dest);

                // patch sling properties
                if ( additionalProps != null ) {
                    this.patchSlingProperties(dest, additionalProps);
                }

                // patch bootstrap command
                if  ( bootstrapCmd != null ) {
                    this.patchSlingBootstrap(dest, bootstrapCmd);
                }

                // and repack again
                pack(dest, destinationFile);
            } finally {
                this.tempDirectory.delete();
            }
        } else {
            // we can just copy
            if (shouldCopy(artifact.getFile(), destinationFile)) {
                try {
                    getLog().info(
                            String.format("Copying base artifact from %s to %s.",
                                    artifact.getFile(), destinationFile));
                    FileUtils.copyFile(artifact.getFile(), destinationFile);
                } catch (IOException e) {
                    throw new MojoExecutionException(
                            "Unable to copy base artifact.", e);
                }
            } else {
                getLog().debug(
                        String.format("Skipping copy of base artifact from %s.",
                                artifact.getFile()));
            }
        }
    }

    private Artifact getBaseArtifact() throws MojoExecutionException {
        Artifact baseDependency = getBaseDependency();
        if (baseDependency == null) {
            return null;
        }

        return getArtifact(base.getGroupId(), base.getArtifactId(),
                baseDependency.getVersion(), base.getType(), base
                        .getClassifier());

    }

    private Artifact getBaseDependency() {
        return project.getArtifactMap().get(
                base.getGroupId() + ":" + base.getArtifactId());
    }

    protected File getOutputDirectory() {
        if (WAR.equals(packaging)) {
            return warOutputDirectory;
        }
        return buildOutputDirectory;
    }

    protected void unpackBaseArtifact() throws MojoExecutionException {
        Artifact artifact = getBaseDependency();
        if (artifact == null) {
            throw new MojoExecutionException(
                    String
                            .format(
                                    "Project doesn't have a base dependency of groupId %s and artifactId %s",
                                    base.getGroupId(), base.getArtifactId()));
        }
        unpack(artifact.getFile(), buildOutputDirectory);
    }

    private void copyConfigurationFiles() throws MojoExecutionException {
        final File configDir = this.getConfigDirectory();
        if (configDir.exists() ) {
            try {
                copyDirectory(configDir, new File(getOutputDirectory(), CONFIG_PATH_PREFIX), null, FileUtils.getDefaultExcludes());
            } catch (IOException e) {
                throw new MojoExecutionException("Unable to copy configuration files", e);
            }
        }
    }

    private void unpack(File source, File destination)
            throws MojoExecutionException {
        getLog().info("Unpacking " + source.getPath() + " to\n  " + destination.getPath());
        try {
            destination.mkdirs();

            UnArchiver unArchiver = archiverManager.getUnArchiver(source);

            unArchiver.setSourceFile(source);
            unArchiver.setDestDirectory(destination);

            unArchiver.extract();
        } catch (NoSuchArchiverException e) {
            throw new MojoExecutionException("Unable to find archiver for " + source.getPath(), e);
        } catch (ArchiverException e) {
            throw new MojoExecutionException("Unable to unpack " + source.getPath(), e);
        }
    }

    private void pack(File sourceDir, File destination)
    throws MojoExecutionException {
        getLog().info("Packing " + sourceDir.getPath() + " to\n  " + destination.getPath());
        try {
            destination.getParentFile().mkdirs();

            jarArchiver.setDestFile(destination);
            jarArchiver.addDirectory(sourceDir);
            jarArchiver.setManifest(new File(sourceDir, "META-INF/MANIFEST.MF".replace('/', File.separatorChar)));
            jarArchiver.createArchive();
        } catch (IOException e) {
            throw new MojoExecutionException("Unable to pack " + sourceDir.getPath(), e);
        } catch (ArchiverException e) {
            throw new MojoExecutionException("Unable to pack " + sourceDir.getPath(), e);
        }
    }
}