org.codehaus.mojo.unix.maven.MojoHelper.java Source code

Java tutorial

Introduction

Here is the source code for org.codehaus.mojo.unix.maven.MojoHelper.java

Source

package org.codehaus.mojo.unix.maven;

/*
 * The MIT License
 *
 * Copyright 2009 The Codehaus.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is furnished to do
 * so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

import fj.*;
import static fj.Function.*;
import static fj.Ord.*;
import static fj.P.*;
import fj.data.List;
import static fj.data.List.join;
import static fj.data.List.*;
import static fj.data.List.single;
import fj.data.*;
import static fj.data.Option.*;
import fj.data.Set;
import static fj.data.Set.*;
import static java.lang.String.*;
import org.apache.maven.artifact.*;
import org.apache.maven.plugin.*;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.project.*;
import org.codehaus.mojo.unix.*;
import static org.codehaus.mojo.unix.PackageParameters.*;
import org.codehaus.mojo.unix.core.*;
import org.codehaus.mojo.unix.io.fs.*;
import org.codehaus.mojo.unix.java.*;
import static org.codehaus.mojo.unix.java.StringF.*;
import org.codehaus.mojo.unix.maven.plugin.*;
import org.codehaus.mojo.unix.maven.plugin.Package;
import static org.codehaus.mojo.unix.util.FileModulator.*;
import org.codehaus.mojo.unix.util.*;
import org.codehaus.mojo.unix.util.line.*;

import java.io.*;
import java.text.*;
import java.util.*;
import java.util.TreeMap;

/**
 * Utility class encapsulating how to create a package. Used by all packaging Mojos.
 *
 * @author <a href="mailto:trygvis@inamo.no">Trygve Laugst&oslash;l</a>
 */
public abstract class MojoHelper {
    public static final String ATTACHED_NO_ARTIFACTS_CONFIGURED = "When running in attached mode at least one package has to be configured.";
    public static final String DUPLICATE_CLASSIFIER = "Duplicate package classifier: '%s'.";
    public static final String DUPLICATE_UNCLASSIFIED = "There can only be one package without an classifier.";

    public static <UP extends UnixPackage<UP, PP>, PP extends UnixPackage.PreparedPackage> Execution create(
            Map platforms, String platformType, String formatType, MavenProjectWrapper project, boolean debug,
            boolean attachedMode, F<UP, UP> validateMojoSettingsAndApplyFormatSpecificSettingsToPackage,
            PackagingMojoParameters mojoParameters, final Log log)
            throws MojoFailureException, MojoExecutionException {
        PackagingFormat<UP> format = PackagingFormat.lookup(formatType);

        if (format == null) {
            throw new MojoFailureException("INTERNAL ERROR: could not find format: '" + formatType + "'.");
        }

        UnixPlatform platform = (UnixPlatform) platforms.get(platformType);

        if (platform == null) {
            throw new MojoFailureException("INTERNAL ERROR: could not find platform: '" + platformType + "'.");
        }

        /*
        // TODO: This is using a private Maven API that might change. Perhaps use some reflection magic here.
        String timestamp = snapshotTransformation.getDeploymentTimestamp();
        */

        // This chunk replaces the above getDeploymentTimestamp. However, it not ensure that all files get the
        // same timestamp. Need to look into how this is done with Maven 3
        DateFormat utcDateFormatter = new SimpleDateFormat("yyyyMMdd.HHmmss");
        utcDateFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
        String timestamp = utcDateFormatter.format(new Date());

        LocalFs buildDirectory = new LocalFs(project.buildDirectory);

        PackageVersion version = PackageVersion.packageVersion(project.version, timestamp,
                project.artifact.isSnapshot(), mojoParameters.revision);

        List<P3<UP, Package, List<AssemblyOperation>>> packages = nil();

        for (Package pakke : validatePackages(mojoParameters.packages, attachedMode)) {
            try {
                String name = "unix/root-" + formatType + pakke.classifier.map(dashString).orSome("");

                LocalFs packageRoot = buildDirectory.resolve(name);
                packageRoot.mkdir();

                PackageParameters parameters = calculatePackageParameters(project, version, platform,
                        mojoParameters, pakke);

                UP unixPackage = format.start(log).parameters(parameters).setVersion(version). // TODO: This should go away
                        workingDirectory(packageRoot).debug(debug).basedir(project.basedir);

                // -----------------------------------------------------------------------
                // Let the implementation add its metadata
                // -----------------------------------------------------------------------

                unixPackage = validateMojoSettingsAndApplyFormatSpecificSettingsToPackage.f(unixPackage);

                // TODO: here the logic should be different if many packages are to be created.
                // Example: name should be taken from mojoParameters if there is only a single package, if not
                //          it should come from the Pakke object. This should also be validated, at least for
                //          name

                List<AssemblyOperation> assemblyOperations = createAssemblyOperations(project, parameters,
                        unixPackage, project.basedir, buildDirectory, mojoParameters.assembly, pakke.assembly);

                // -----------------------------------------------------------------------
                // Dump the execution
                // -----------------------------------------------------------------------

                if (debug) {
                    log.info("=======================================================================");
                    log.info("Package parameters: " + parameters.id);
                    log.info("Default file attributes: ");
                    log.info(" File      : " + parameters.defaultFileAttributes);
                    log.info(" Directory : " + parameters.defaultDirectoryAttributes);

                    log.info("Assembly operations: ");
                    for (AssemblyOperation operation : assemblyOperations) {
                        operation.streamTo(new AbstractLineStreamWriter() {
                            protected void onLine(String line) {
                                log.info(line);
                            }
                        });
                    }
                }

                packages = packages.cons(p(unixPackage, pakke, assemblyOperations));
            } catch (UnknownArtifactException e) {
                Map map = new TreeMap<String, Artifact>(e.artifactMap);

                // TODO: Do not log here, throw a CouldNotFindArtifactException with the map as an argument
                log.warn("Could not find artifact:" + e.artifact);
                log.warn("Available artifacts:");
                for (Object o : map.keySet()) {
                    log.warn(o.toString());
                }

                throw new MojoFailureException(
                        "Unable to find artifact: '" + e.artifact + "'. See log for available artifacts.");
            } catch (MissingSettingException e) {
                String msg = "Missing required setting '" + e.getSetting() + "'";
                if (!pakke.classifier.isNone()) {
                    msg += ", for '" + pakke.classifier.some() + "'";
                }
                msg += ", format '" + formatType + "'.";
                throw new MojoFailureException(msg);
            } catch (IOException e) {
                throw new MojoExecutionException("Error creating package "
                        + (pakke.classifier.isSome() ? "classifier '" + pakke.classifier + "'" : "") + ", format '"
                        + formatType + "'.", e);
            }
        }

        return new Execution<UP>(packages, project, formatType, attachedMode);
    }

    public static class Execution<UP extends UnixPackage> {
        private final List<P3<UP, Package, List<AssemblyOperation>>> packages;

        private final MavenProjectWrapper project;

        private final String formatType;

        private final boolean attachedMode;

        public Execution(List<P3<UP, Package, List<AssemblyOperation>>> packages, MavenProjectWrapper project,
                String formatType, boolean attachedMode) {
            this.packages = packages;
            this.project = project;
            this.formatType = formatType;
            this.attachedMode = attachedMode;
        }

        public void execute(String artifactType, MavenProject mavenProject, MavenProjectHelper mavenProjectHelper,
                ScriptUtil.Strategy strategy) throws MojoExecutionException, MojoFailureException {
            for (P3<UP, Package, List<AssemblyOperation>> p : packages) {
                UnixPackage unixPackage = p._1();
                Package pakke = p._2();

                try {
                    // -----------------------------------------------------------------------
                    // Assemble all the files
                    // -----------------------------------------------------------------------

                    for (AssemblyOperation assemblyOperation : p._3()) {
                        assemblyOperation.perform(unixPackage);
                    }

                    // -----------------------------------------------------------------------
                    // Package the stuff
                    // -----------------------------------------------------------------------

                    String name = project.artifactId + pakke.classifier.map(dashString).orSome("") + "-"
                            + unixPackage.getVersion().getMavenVersion() + "."
                            + unixPackage.getPackageFileExtension();

                    File packageFile = new File(project.buildDirectory, name);

                    unixPackage.prepare(strategy).packageToFile(packageFile);

                    attach(pakke.classifier, artifactType, packageFile, mavenProject, mavenProjectHelper,
                            attachedMode);
                } catch (MojoExecutionException e) {
                    throw e;
                } catch (MojoFailureException e) {
                    throw e;
                } catch (Exception e) {
                    throw new MojoExecutionException("Unable to create package.", e);
                }
            }
        }

        private void attach(Option<String> classifier, String artifactType, File packageFile, MavenProject project,
                MavenProjectHelper mavenProjectHelper, boolean attachedMode) {
            if (attachedMode) {
                // In attached mode all the packages are required to have an classifier - this used to be correct - trygve
                // For some reason it is allowed to have attached artifacts without classifier as long as the types differ

                if (classifier.isSome()) {
                    mavenProjectHelper.attachArtifact(project, artifactType, classifier.some(), packageFile);
                } else {
                    mavenProjectHelper.attachArtifact(project, artifactType, null, packageFile);
                }
            } else {
                if (classifier.isNone()) {
                    project.getArtifact().setFile(packageFile);
                } else {
                    mavenProjectHelper.attachArtifact(project, formatType, classifier.some(), packageFile);
                }
            }
        }
    }

    public static PackageParameters calculatePackageParameters(final MavenProjectWrapper project,
            PackageVersion version, UnixPlatform platform, PackagingMojoParameters mojoParameters,
            final Package pakke) {
        String id = pakke.id.orSome(new P1<String>() {
            public String _1() {
                // This used to be ${groupId}-${artifactId}, but it was too long for pkg so this is a more sane default
                return project.artifactId + pakke.classifier.map(dashString).orSome("");
            }
        });

        P2<FileAttributes, FileAttributes> defaultFileAttributes = calculateDefaultFileAttributes(platform,
                mojoParameters.defaults, pakke.defaults);

        String name = pakke.name.orElse(mojoParameters.name).orSome(project.name);
        return packageParameters(project.groupId, project.artifactId, version, id, name, pakke.classifier,
                defaultFileAttributes._1(), defaultFileAttributes._2())
                        .description(
                                pakke.description.orElse(mojoParameters.description).orElse(project.description))
                        .contact(mojoParameters.contact).contactEmail(mojoParameters.contactEmail)
                        .license(getLicense(project))
                        .architecture(pakke.architecture.orElse(mojoParameters.architecture));
    }

    public static P2<FileAttributes, FileAttributes> calculateDefaultFileAttributes(UnixPlatform platform,
            Defaults mojo, Defaults pakke) {
        return p(
                calculateFileAttributes(platform.getDefaultFileAttributes(), mojo.fileAttributes.create(),
                        pakke.fileAttributes.create()),
                calculateFileAttributes(platform.getDefaultDirectoryAttributes(), mojo.directoryAttributes.create(),
                        pakke.directoryAttributes.create()));
    }

    public static FileAttributes calculateFileAttributes(FileAttributes platform, FileAttributes mojo,
            FileAttributes pakke) {
        // Calculate default file and directory attributes.
        // Priority order (last one wins): platform -> mojo defaults -> package defaults

        return platform.useAsDefaultsFor(mojo).useAsDefaultsFor(pakke);
    }

    public static List<AssemblyOperation> createAssemblyOperations(MavenProjectWrapper project,
            PackageParameters parameters, UnixPackage unixPackage, File basedir, LocalFs buildDirectory,
            List<AssemblyOp> mojoAssembly, List<AssemblyOp> packageAssembly)
            throws IOException, MojoFailureException, UnknownArtifactException {
        unixPackage.beforeAssembly(parameters.defaultDirectoryAttributes, project.timestamp);

        // Create the default set of assembly operations
        String unix = new File(basedir, "src/main/unix/files").getAbsolutePath();

        String classifierOrDefault = parameters.classifier.orSome("default");

        List<AssemblyOp> defaultAssemblyOp = nil();

        // It would be possible to use the AssemblyOperations here but this just make it easier to document
        // as it has a one-to-one relationship with what the user would configure in a POM
        for (String s : modulatePath(classifierOrDefault, unixPackage.getPackageFileExtension(), unix)) {
            File file = new File(s);

            if (!file.isDirectory()) {
                continue;
            }

            CopyDirectory op = new CopyDirectory();
            op.setFrom(file);
            defaultAssemblyOp = defaultAssemblyOp.cons(op);
        }

        // Create the complete list of assembly operations.
        // Order: defaults -> mojo -> pakke
        List<List<AssemblyOp>> list = single(defaultAssemblyOp.reverse()).conss(mojoAssembly)
                .conss(packageAssembly);

        List<AssemblyOp> assemblyOps = join(list.reverse());

        List<AssemblyOperation> operations = nil();

        AssemblyOp.CreateOperationContext context = new AssemblyOp.CreateOperationContext(buildDirectory,
                parameters.defaultFileAttributes, parameters.defaultDirectoryAttributes, project);

        for (AssemblyOp assemblyOp : assemblyOps) {
            operations = operations.cons(assemblyOp.createOperation(context));
        }

        return operations.reverse();
    }

    public static List<Package> validatePackages(List<Package> packages, boolean attachedMode)
            throws MojoFailureException {
        if (packages.isEmpty()) {
            packages = single(new Package());
        }

        Set<String> names = empty(stringOrd);
        List<Package> outPackages = nil();

        Option<Package> defaultPackage = none();

        for (Package pakke : packages) {
            if (pakke.classifier.exists(curry(StringF.equals, "default"))) {
                pakke.classifier = none();
            }

            if (pakke.classifier.isNone()) {
                if (defaultPackage.isSome()) {
                    throw new MojoFailureException(DUPLICATE_UNCLASSIFIED);
                }

                defaultPackage = some(pakke);
            } else {
                if (names.member(pakke.classifier.some())) {
                    throw new MojoFailureException(format(DUPLICATE_CLASSIFIER, pakke.classifier));
                }

                names = names.insert(pakke.classifier.some());
                outPackages = outPackages.cons(pakke);
            }
        }

        if (attachedMode) {
            outPackages = defaultPackage.toList().append(outPackages);

            if (outPackages.isEmpty()) {
                throw new MojoFailureException(ATTACHED_NO_ARTIFACTS_CONFIGURED);
            }

            return outPackages;
        }

        if (defaultPackage.isNone()) {
            throw new MojoFailureException(
                    "When running in 'primary artifact mode' either one package has to have 'default' as classifier or there has to be one without any classifier.");
        }

        return defaultPackage.toList().append(outPackages);
    }

    private static Option<String> getLicense(MavenProjectWrapper project) {
        if (project.licenses.size() == 0) {
            return none();
        }

        return some(project.licenses.get(0).getName());
    }

    static F<String, String> dashString = curry(concat, "-");
}