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

io.reactiverse.vertx.maven.plugin.components.impl.ShrinkWrapFatJarPackageService Maven / Gradle / Ivy

Go to download

A plugin to allow package, start, stop, run of Eclipse Vert.x applications

There is a newer version: 2.0.1
Show newest version
/*
 *
 *   Copyright (c) 2016-2018 Red Hat, Inc.
 *
 *   Red Hat 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 io.reactiverse.vertx.maven.plugin.components.impl;

import io.reactiverse.vertx.maven.plugin.components.*;
import io.reactiverse.vertx.maven.plugin.mojos.Archive;
import io.reactiverse.vertx.maven.plugin.mojos.DependencySet;
import io.reactiverse.vertx.maven.plugin.mojos.FileItem;
import io.reactiverse.vertx.maven.plugin.mojos.FileSet;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.project.MavenProject;
import org.apache.maven.shared.artifact.filter.resolve.ScopeFilter;
import org.apache.maven.shared.artifact.filter.resolve.transform.ArtifactIncludeFilterTransformer;
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.util.DirectoryScanner;
import org.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.util.SelectorUtils;
import org.jboss.shrinkwrap.api.ArchivePath;
import org.jboss.shrinkwrap.api.Node;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.ByteArrayAsset;
import org.jboss.shrinkwrap.api.exporter.ZipExporter;
import org.jboss.shrinkwrap.api.importer.ZipImporter;
import org.jboss.shrinkwrap.api.spec.JavaArchive;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.util.*;
import java.util.jar.Attributes;
import java.util.jar.Manifest;

/**
 * Service packaging the fat jar using ShrinkWrap.
 *
 * @author Clement Escoffier
 */
@Component(
    role = PackageService.class,
    hint = "fat-jar")
public class ShrinkWrapFatJarPackageService implements PackageService {

    private static final List DEFAULT_EXCLUDES;

    static {
        DEFAULT_EXCLUDES = new ArrayList<>(FileUtils.getDefaultExcludesAsList());
        DEFAULT_EXCLUDES.add("**/*.DSA");
        DEFAULT_EXCLUDES.add("**/*.RSA");
        DEFAULT_EXCLUDES.add("**/INDEX.LIST");
        DEFAULT_EXCLUDES.add("**/*.SF");
    }

    @Override
    public PackageType type() {
        return PackageType.FAT_JAR;
    }

    @Override
    public File doPackage(PackageConfig config) throws PackagingException {

        Log logger = Objects.requireNonNull(config.getMojo().getLog());
        Archive archive = Objects.requireNonNull(config.getArchive());

        JavaArchive jar = ShrinkWrap.create(JavaArchive.class);

        addDependencies(config, archive.getDependencySets(), jar);

        addFileSets(config, archive, jar);

        addProjectClasses(config, archive, jar);

        // File Items
        for (FileItem item : archive.getFiles()) {
            embedFile(config, jar, item);
        }

        // Generate manifest
        try {
            generateManifest(jar, archive.getManifest());
        } catch (IOException e) {
            throw new PackagingException(e);
        }

        // Generate output file
        File jarFile;

        try {
            jarFile = config.getOutput();

            boolean useTmpFile = false;
            File theCreatedFile = jarFile;
            if (jarFile.isFile()) {
                useTmpFile = true;
                theCreatedFile = new File(jarFile.getParentFile(), jarFile.getName() + ".tmp");
            }

            jar.as(ZipExporter.class).exportTo(theCreatedFile);

            if (useTmpFile) {
                boolean delete = Files.deleteIfExists(jarFile.toPath());
                boolean renameTo = theCreatedFile.renameTo(jarFile);
                logger.debug("Main jar file deleted: " + delete);
                logger.debug("Main jar file replaced by temporary file: " + renameTo);
            }

        } catch (Exception e) {
            throw new PackagingException(e);
        }

        return jarFile;
    }

    private void addFileSets(PackageConfig config, Archive archive, JavaArchive jar) {
        Log logger = config.getMojo().getLog();
        for (FileSet fs : archive.getFileSets()) {
            embedFileSet(logger, config.getProject(), fs, jar);
        }
    }

    private static void addProjectClasses(PackageConfig config, Archive archive, JavaArchive jar) {
        if (archive.isIncludeClasses()) {
            File classes = new File(config.getProject().getBuild().getOutputDirectory());
            if (classes.isDirectory()) {
                jar.addAsResource(classes, "/");
            }
        }
    }

    private void addDependencies(PackageConfig config, Collection dependencies, JavaArchive jar) {
        Log logger = config.getMojo().getLog();
        for (DependencySet ds : dependencies) {
            ScopeFilter scopeFilter = ServiceUtils.newScopeFilter(ds.getScope());
            ArtifactFilter filter = new ArtifactIncludeFilterTransformer().transform(scopeFilter);
            Set artifacts = ServiceUtils.filterArtifacts(config.getArtifacts(),
                ds.getIncludes(), ds.getExcludes(),
                ds.isUseTransitiveDependencies(), logger, filter);

            for (Artifact artifact : artifacts) {
                File file = artifact.getFile();
                if (file.isFile()) {
                    logger.debug("Adding Dependency :" + artifact);
                    embedDependency(logger, ds, jar, file);
                } else {
                    logger.warn("Cannot embed artifact " + artifact
                        + " - the file does not exist");
                }
            }
        }
    }

    private static void embedFile(PackageConfig config, JavaArchive jar, FileItem item) throws PackagingException {
        String path;
        if (item.getOutputDirectory() == null) {
            path = "/";
        } else {
            path = item.getOutputDirectory();
            if (!path.startsWith("/")) {
                path = "/" + path;
            }
            if (!path.endsWith("/")) {
                path = path + "/";
            }
        }

        File source = new File(config.getProject().getBasedir(), item.getSource());
        if (!source.isFile()) {
            Node node = jar.get(item.getSource());
            if (node == null) {
                throw new PackagingException("Unable to handle the file item " + item.getSource() + ", " +
                    "file not found in the project or in the archive.");
            }

            String name = item.getDestName();
            if (name == null) {
                name = node.getPath().get().substring(node.getPath().getParent().get().length() + 1);
            }

            String out = path + name;
            jar.add(node.getAsset(), out);
            jar.delete(node.getPath());
        } else {
            String name = item.getDestName();
            if (name == null) {
                name = source.getName();
            }
            String out = path + name;
            jar.addAsResource(source, out);
        }
    }

    private static void embedFileSet(Log log, MavenProject project, FileSet fs, JavaArchive jar) {
        File directory = new File(fs.getDirectory());
        if (!directory.isAbsolute()) {
            directory = new File(project.getBasedir(), fs.getDirectory());
        }

        if (!directory.isDirectory()) {
            log.warn("File set root directory (" + directory.getAbsolutePath() + ") does not exist " +
                "- skipping");
            return;
        }

        DirectoryScanner scanner = new DirectoryScanner();
        scanner.setBasedir(directory);


        if (fs.getOutputDirectory() == null) {
            fs.setOutputDirectory("/");
        }
        if (!fs.getOutputDirectory().startsWith("/")) {
            fs.setOutputDirectory("/" + fs.getOutputDirectory());
        }
        if (!fs.getOutputDirectory().endsWith("/")) {
            fs.setOutputDirectory(fs.getOutputDirectory() + "/");
        }

        List excludes = fs.getExcludes();
        if (fs.isUseDefaultExcludes()) {
            excludes.addAll(FileUtils.getDefaultExcludesAsList());
        }
        if (!excludes.isEmpty()) {
            scanner.setExcludes(excludes.toArray(new String[0]));
        }
        if (!fs.getIncludes().isEmpty()) {
            scanner.setIncludes(fs.getIncludes().toArray(new String[0]));
        }
        scanner.scan();
        String[] files = scanner.getIncludedFiles();
        for (String path : files) {
            File file = new File(directory, path);
            log.debug("Adding " + fs.getOutputDirectory() + path + " to the archive");
            jar.addAsResource(file, fs.getOutputDirectory() + path);
        }
    }


    private boolean toExclude(DependencySet set, ArchivePath path) {
        String name = path.get();

        // Check whether the file is explicitly included
        if (isExplicitlyIncluded(set, name)) {
            return true;
        }

        if (set.getOptions().isUseDefaultExcludes()) {
            for (String pattern : DEFAULT_EXCLUDES) {
                if (SelectorUtils.match(pattern, name)) {
                    return true;
                }
            }
        }

        if (name.equalsIgnoreCase("/META-INF/MANIFEST.MF")) {
            return true;
        }

        if (name.equals("/module-info.class")){
            return true;
        }

        if (set.getOptions().getExcludes() != null) {
            for (String pattern : set.getOptions().getExcludes()) {
                if (SelectorUtils.match(pattern, name)) {
                    return true;
                }
            }
        }

        return false;
    }

    private static boolean isExplicitlyIncluded(DependencySet set, String name) {
        List includes = set.getOptions().getIncludes();
        if (includes != null && !includes.isEmpty()) {
            boolean included = false;

            // Check for each include pattern whether or not the path is explicitly included
            for (String pattern : includes) {
                if (SelectorUtils.match(pattern, name)) {
                    included = true;
                }
            }

            // If the path is not included, exclude the file
            // otherwise apply the excludes pattern on it.
            return !included;
        }
        return false;
    }

    /**
     * Import from file and make sure the file is closed.
     *
     * @param log  the logger
     * @param set  the dependency set
     * @param jar  the archive
     * @param file the file, must not be {@code null}
     */
    private void embedDependency(Log log, DependencySet set, JavaArchive jar, File file) {
        try (FileInputStream fis = new FileInputStream(file)) {
            jar.as(ZipImporter.class).importFrom(fis, path -> {
                if (jar.contains(path)) {
                    log.debug(path.get() + " already embedded in the jar");
                    return false;
                }
                if (!toExclude(set, path)) {
                    return true;
                } else {
                    log.debug("Excluding " + path.get() + " from " + file.getName());
                    return false;
                }
            });
        } catch (Exception e) {
            throw new RuntimeException("Unable to read the file " + file.getAbsolutePath(), e);
        }
    }

    /**
     * Generate the manifest for the über jar.
     */
    private static void generateManifest(JavaArchive jar, Map entries) throws IOException {
        Manifest manifest = new Manifest();
        Attributes attributes = manifest.getMainAttributes();
        attributes.put(Attributes.Name.MANIFEST_VERSION, "1.0");

        if (!entries.containsKey("Multi-Release")) {
            Node multiReleaseNode = jar.get("/META-INF/versions");
            if (multiReleaseNode != null && !multiReleaseNode.getChildren().isEmpty()) {
                attributes.put(new Attributes.Name("Multi-Release"), Boolean.TRUE.toString());
            }
        }

        if (entries != null) {
            for (Map.Entry entry : entries.entrySet()) {
                attributes.put(new Attributes.Name(entry.getKey()), entry.getValue());
            }
        }

        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        manifest.write(bout);
        bout.close();
        byte[] bytes = bout.toByteArray();
        jar.setManifest(new ByteArrayAsset(bytes));
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy