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

com.cisco.device.packages.mojos.CombineSDUMojo Maven / Gradle / Ivy

/******************************************************************************
 * Copyright (c) 2016-2018 by Cisco Systems, Inc. and/or its affiliates.
 * All rights reserved.
 *
 * This software is made available under the CISCO SAMPLE CODE LICENSE
 * Version 1.1. See LICENSE.TXT at the root of this project for more information.
 *
 ********************************************************************************/
package com.cisco.device.packages.mojos;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.jar.Attributes;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.zip.CRC32;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.apache.maven.plugin.AbstractMojo;
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.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectHelper;

import com.cisco.device.packages.constants.PackagingConstants;
import com.cisco.device.packages.internal.sdu.SduCreator;

/**
 * Combines one or more Single Deployable Unit (SDU) files built by other
 * projects into a single SDU. When invoked on a project with 2 or more SDU
 * files listed as dependencies, will combine them into a consolidated SDU file.
 * This goal will automatically remove older versions if multiple versions of
 * the same artifact are present across the various SDU files. This goal
 * requires all project dependencies to be downloaded, and is advised to be
 * configured in a stand-alone project with only SDU dependencies for the best
 * build performance. The resulting SDU file will be attached to the project
 * using the SDU classifier/extension.
 *
 * @author Daniel Johnson - [email protected]
 * @since 1.0.0
 */
@Mojo(name = "combine-sdu", requiresProject = true, requiresDependencyResolution = ResolutionScope.TEST, threadSafe = true)
public class CombineSDUMojo extends AbstractMojo {

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

    @Component
    public MavenProjectHelper mavenProjectHelper;

    /**
     * The name of the resulting SDU file.
     */
    @Parameter(defaultValue = "${project.artifactId}-${project.version}.sdu")
    private String sduName;

    /**
     * The name of the resulting SDU file.
     */
    @Parameter(defaultValue = "${project.build.directory}/dependency")
    private File sduDirectory;

    /**
     * Whether or not the build should fail if the resulting SDU is empty, or no
     * SDU dependencies are found in the project.
     */
    @Parameter(defaultValue = "true")
    private boolean failOnEmpty;

    public void execute() throws MojoExecutionException, MojoFailureException {

        if (CollectionUtils.isEmpty(project.getDependencies())) {
            if (failOnEmpty) {
                throw new MojoExecutionException(
                        "Project has no dependencies configured. Please add one or more SDU dependencies to use this goal, or use failOnEmpty=false to ignore this failure.");
            } else {
                getLog().warn("Project has no SDU dependencies, skipping creation of combined SDU.");
            }
        }

        Manifest sduManifest = createProjectManifest();

        Map artifactMap = new HashMap();
        if (sduDirectory.exists()) {
            for (File sdu : sduDirectory
                    .listFiles(file -> FilenameUtils.isExtension(file.getName(), PackagingConstants.SDU_EXT))) {
                getLog().info("Processing SDU: " + sdu.getAbsolutePath());
                Manifest manifest = null;
                try (ZipFile sduZip = new ZipFile(sdu)) {
                    try {
                        ZipEntry manifestEntry = sduZip.getEntry("META-INF/MANIFEST.MF");
                        if (manifestEntry == null) {
                            throw new MojoExecutionException("SDU is missing MANIFEST: " + sdu.getAbsolutePath() + ".");
                        }
                        manifest = new Manifest(sduZip.getInputStream(manifestEntry));
                    } catch (Exception e) {
                        throw new MojoExecutionException(
                                "An error occurred loading MANIFEST from: " + sdu.getAbsolutePath() + ".", e);
                    }
                    Map entries = manifest.getEntries();
                    int i = 0;
                    Attributes attr = entries.get(SduCreator.SDU_MANIFEST_ATTR_LOAD_ORDER + i++);
                    while (attr != null) {
                        String groupId = attr.getValue(SduCreator.SDU_MANIFEST_ATTR_GROUP_ID);
                        String artifactId = attr.getValue(SduCreator.SDU_MANIFEST_ATTR_ARTIFACT_ID);
                        String versionValue = attr.getValue(SduCreator.SDU_MANIFEST_ATTR_VERSION);
                        attr = entries.get(SduCreator.SDU_MANIFEST_ATTR_LOAD_ORDER + i++);

                        ZipEntry entry = sduZip.getEntry(artifactId + PackagingConstants.DASH + versionValue
                                + PackagingConstants.DOT + PackagingConstants.DAR_EXT);
                        try {
                            addArtifactToMap(groupId, artifactId, versionValue, entry, sduZip.getInputStream(entry),
                                    artifactMap, sduManifest);
                        } catch (Exception e) {
                            throw new MojoExecutionException("An error occurred getting input stream from: "
                                    + sdu.getAbsolutePath() + " for: " + entry.getName() + ".", e);
                        }
                    }
                    ZipEntry entry = null;
                    Enumeration enumerator = sduZip.entries();
                    while (enumerator.hasMoreElements()) {
                        entry = enumerator.nextElement();
                        if (entry.isDirectory()) {
                            continue;
                        }
                        if (entry.getName().endsWith(PackagingConstants.FEATURE_EXT)
                                || entry.getName().endsWith(PackagingConstants.XDE_EXT)) {
                            // Split into groupId, artifactId, version
                            int lastSlash = entry.getName().lastIndexOf('/');
                            String full = entry.getName().substring(0, lastSlash);
                            lastSlash = full.lastIndexOf('/');
                            String versionValue = full.substring(lastSlash + 1);
                            full = full.substring(0, lastSlash);
                            lastSlash = full.lastIndexOf('/');
                            String artifactId = full.substring(lastSlash + 1);
                            full = full.substring(0, lastSlash);
                            String groupId = full.replaceAll("/", ".");
                            try {
                                addArtifactToMap(groupId, artifactId, versionValue, entry, sduZip.getInputStream(entry),
                                        artifactMap, sduManifest);
                            } catch (Exception e) {
                                throw new MojoExecutionException("An error occurred getting input stream from: "
                                        + sdu.getAbsolutePath() + " for: " + entry.getName() + ".", e);
                            }
                        }
                    }
                } catch (Exception e) {
                    throw new MojoExecutionException(
                            "An error occurred loading MANIFEST from: " + sdu.getAbsolutePath() + ".", e);
                }
            }
        }

        if (artifactMap.isEmpty()) {
            if (failOnEmpty) {
                throw new MojoExecutionException("No SDU files were found at " + sduDirectory.getAbsolutePath()
                        + ". Please ensure the maven-dependencies-plugin is configured with copy-dependencies goal.");
            } else {

            }
        }

        File sduFile = new File(project.getBuild().getDirectory() + File.separator + sduName);
        try (FileOutputStream rootStream = new FileOutputStream(sduFile)) {
            try (JarOutputStream sduStream = new JarOutputStream(rootStream, sduManifest)) {
                // Create versions.txt in SDU
                String versionText = project.getGroupId() + "." + project.getArtifactId() + "-" + project.getVersion();
                CRC32 crc = new CRC32();
                byte[] bytes = versionText.getBytes(Charset.forName("UTF-8"));
                crc.update(bytes);
                ZipEntry versionsFile = new ZipEntry("version.txt");
                versionsFile.setMethod(ZipEntry.STORED);
                versionsFile.setSize(bytes.length);
                versionsFile.setCompressedSize(bytes.length);
                versionsFile.setCrc(crc.getValue());
                sduStream.putNextEntry(versionsFile);
                sduStream.write(bytes);

                for (Entry artifact : artifactMap.entrySet()) {
                    ZipEntry newEntry = new ZipEntry(artifact.getValue().entry);
                    try {
                        sduStream.putNextEntry(newEntry);
                        IOUtils.copy(artifact.getValue().stream, sduStream, 1024);
                    } catch (Exception e) {
                        getLog().info("Failed to write an entry.", e);
                    }
                }
            } catch (Exception e) {
                throw new MojoFailureException("Failed to create new SDU.", e);
            }
        } catch (IOException e) {
            throw new MojoFailureException("Failed to create new SDU.", e);
        }

        getLog().info("SDU Created: " + sduFile.getAbsolutePath());
        mavenProjectHelper.attachArtifact(project, PackagingConstants.SDU_EXT, PackagingConstants.SDU_EXT, sduFile);
    }

    private Manifest createProjectManifest() {
        Manifest manifest = new Manifest();
        manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
        manifest.getMainAttributes().putValue("Created-By", "Device Package Maven Plugin by Cisco Systems, Inc.");
        manifest.getMainAttributes().putValue("Version", project.getVersion());
        manifest.getMainAttributes().putValue("Name", project.getGroupId() + "." + project.getArtifactId());
        return manifest;
    }

    private int devicePackageCounter = 0;

    private void addArtifactToMap(String groupId, String artifactId, String versionValue, ZipEntry entry,
            InputStream stream, Map artifactMap, Manifest manifest) {

        String key = groupId + "." + artifactId;
        DefaultArtifactVersion versionComparor = new DefaultArtifactVersion(versionValue);

        boolean isDar = entry.getName().endsWith(PackagingConstants.DAR_EXT);

        if (artifactMap.containsKey(key)) {
            DefaultArtifactVersion otherVersionComparor = artifactMap.get(key).version;
            int compared = versionComparor.compareTo(otherVersionComparor);
            if (compared == 0) {
                return;
            }
            if (compared > 0) {
                getLog().info("Overriding " + otherVersionComparor + " with " + versionComparor + " for " + key);
                artifactMap.put(key, new Artifact(versionComparor, entry, stream));
                if (isDar) {
                    for (Attributes attrs : manifest.getEntries().values()) {
                        if (attrs.getValue(SduCreator.SDU_MANIFEST_ATTR_GROUP_ID).equals(groupId)
                                && attrs.getValue(SduCreator.SDU_MANIFEST_ATTR_ARTIFACT_ID).equals(artifactId)) {
                            attrs.putValue(SduCreator.SDU_MANIFEST_ATTR_VERSION, versionValue);
                        }
                    }
                }
            } else {
                getLog().info("Ignoring version " + versionComparor + " as a newer version is already included "
                        + otherVersionComparor + " for " + key);
                return;
            }
        } else {
            artifactMap.put(key, new Artifact(versionComparor, entry, stream));
            if (isDar) {
                Attributes attrs = new Attributes();
                attrs.putValue(SduCreator.SDU_MANIFEST_ATTR_GROUP_ID, groupId);
                attrs.putValue(SduCreator.SDU_MANIFEST_ATTR_ARTIFACT_ID, artifactId);
                attrs.putValue(SduCreator.SDU_MANIFEST_ATTR_VERSION, versionValue);
                manifest.getEntries().put(SduCreator.SDU_MANIFEST_ATTR_LOAD_ORDER + devicePackageCounter++, attrs);
            }
        }
    }

    protected class Artifact {
        DefaultArtifactVersion version;
        ZipEntry entry;
        InputStream stream;

        public Artifact(DefaultArtifactVersion version, ZipEntry entry, InputStream stream) {
            this.version = version;
            this.entry = entry;
            this.stream = stream;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy