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

org.embulk.deps.SelfContainedJarFile Maven / Gradle / Ivy

package org.embulk.deps;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.security.CodeSource;
import java.security.ProtectionDomain;
import java.util.LinkedHashMap;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import java.util.jar.Manifest;

final class SelfContainedJarFile {
    SelfContainedJarFile(final String outerResourceName) {
        if (outerResourceName == null) {
            throw new NullPointerException("null specified for SelfContainedJarFile.");
        }

        if (CODE_SOURCE_URL_BASE == null) {
            System.err.println("org.embulk.deps.SelfContainedJarFile is loaded through invalid method or location.");
            this.codeSourceUrl = null;
            this.manifest = null;
            this.innerResources = null;
            this.innerResourcesBinary = null;
            return;
        }

        final URL codeSourceUrlBuilt;
        try {
            codeSourceUrlBuilt = new URL(
                    CODE_SOURCE_URL_BASE,
                    outerResourceName.startsWith("/") ? outerResourceName.substring(1) : outerResourceName);
        } catch (final MalformedURLException ex) {
            System.err.println("Invalid JAR resource: " + CODE_SOURCE_URL_BASE.toString() + " : " + outerResourceName);
            ex.printStackTrace();
            this.codeSourceUrl = null;
            this.manifest = null;
            this.innerResources = null;
            this.innerResourcesBinary = null;
            return;
        }

        final InputStream inputStream = SelfContainedJarFile.class.getResourceAsStream(outerResourceName);
        if (inputStream == null) {
            System.err.println("JAR resource not found: " + outerResourceName);
            this.codeSourceUrl = null;
            this.manifest = null;
            this.innerResources = null;
            this.innerResourcesBinary = null;
            return;
        }

        final JarInputStream jarInputStream;
        try {
            jarInputStream = new JarInputStream(inputStream, false);
        } catch (final IOException ex) {
            System.err.println("Invalid JAR format: " + outerResourceName);
            ex.printStackTrace();
            this.codeSourceUrl = null;
            this.manifest = null;
            this.innerResources = null;
            this.innerResourcesBinary = null;
            return;
        }

        final LinkedHashMap innerResourcesBuilt = new LinkedHashMap<>();
        final ByteBuffer innerResourcesBinaryBuilt;
        try {
            innerResourcesBinaryBuilt = extract(outerResourceName, jarInputStream, this, innerResourcesBuilt);
        } catch (final IOException ex) {
            System.err.println("Failed to read JAR: " + outerResourceName);
            ex.printStackTrace();
            this.codeSourceUrl = null;
            this.manifest = null;
            this.innerResources = null;
            this.innerResourcesBinary = null;
            closeQuiet(jarInputStream);
            return;
        }

        this.codeSourceUrl = codeSourceUrlBuilt;
        this.manifest = jarInputStream.getManifest();
        this.innerResources = innerResourcesBuilt;
        this.innerResourcesBinary = innerResourcesBinaryBuilt;

        if ("true".equals(System.getProperty("org.embulk.trace_embedded_jar_resources"))) {
            System.err.println(
                    "Extracted an embedded JAR resource: ["
                    + this.codeSourceUrl.toString()
                    + "] ("
                    + this.innerResourcesBinary.capacity()
                    + " bytes)");
        }
    }

    private static void closeQuiet(final InputStream is) {
        if (is != null) {
            try {
                is.close();
            } catch (IOException e) {
                // ignore.
            }
        }
    }

    URL getCodeSourceUrl() {
        return this.codeSourceUrl;
    }

    Manifest getManifest() {
        return this.manifest;
    }

    ByteBuffer getInnerResourcesBinary() {
        return this.innerResourcesBinary.asReadOnlyBuffer();
    }

    /**
     * Returns the resource object.
     *
     * @return resource if found, null otherwise
     */
    Resource getResource(final String resourceName) {
        if (this.innerResources == null) {
            return null;
        }
        return this.innerResources.get(resourceName);
    }

    private static ByteBuffer extract(
            final String outerResourceName,
            final JarInputStream jarInputStream,
            final SelfContainedJarFile jar,
            final LinkedHashMap innerResources)
            throws IOException {
        final byte[] buffer = new byte[4096];
        final ByteArrayOutputStream innerResourcesBinaryOutputStream = new ByteArrayOutputStream();

        int index = 0;
        while (true) {
            final JarEntry jarEntry = (JarEntry) jarInputStream.getNextEntry();
            if (jarEntry == null) {
                break;  // End of embedded JAR
            }

            if (jarEntry.isDirectory()) {
                continue;
            }
            final String entryName = jarEntry.getName();
            if (innerResources.containsKey(entryName)) {
                throw new IOException(String.format("Duplicated in embedded JAR: %s/%s", outerResourceName, entryName));
            }

            final int lengthRead = (int) transfer(jarInputStream, innerResourcesBinaryOutputStream, buffer);
            innerResources.put(entryName, new Resource(entryName, jar, index, index + lengthRead, jarEntry.getCodeSigners()));
            index += lengthRead;
        }

        // TODO: Use ByteBuffer.allocateDirect so that it does not affect GC.
        return ByteBuffer.wrap(innerResourcesBinaryOutputStream.toByteArray()).asReadOnlyBuffer();
    }

    /**
     * Does the same with Java 9+'s {@code InputStream#transferTo}.
     */
    private static long transfer(final InputStream inputStream, final OutputStream outputStream, final byte[] buffer) throws IOException {
        long totalLength = 0L;
        while (true) {
            final int lengthRead = inputStream.read(buffer);
            if (lengthRead < 0) {
                break;
            }
            outputStream.write(buffer, 0, lengthRead);
            totalLength += lengthRead;
        }
        return totalLength;
    }

    // TODO: Apply the Initialization-on-demand holder idiom.
    static {
        final ProtectionDomain protectionDomain = SelfContainedJarFile.class.getProtectionDomain();
        final CodeSource codeSource = protectionDomain.getCodeSource();
        PROTECTION_DOMAIN = protectionDomain;
        CODE_SOURCE = codeSource;

        URL codeSourceUrlBase = null;
        boolean hasFailedInCodeSource = false;
        try {
            final URL codeSourceUrl = codeSource.getLocation();
            final String codeSourceProtocol = codeSourceUrl.getProtocol();
            if ("file".equals(codeSourceProtocol)) {
                codeSourceUrlBase = new URL("jar", "", -1, codeSourceUrl.toString() + "!/");
            } else if ("jar".equals(codeSourceProtocol)) {
                final URL innerUrl = new URL(codeSourceUrl.getPath());
                final String innerProtocol = innerUrl.getProtocol();
                if (!"file".equals(innerProtocol)) {
                    throw new MalformedURLException("Invalid CodeSource URL, non 'file:' in 'jar:': " + codeSourceUrl);
                }
                final String innerPath = innerUrl.getPath();
                if (innerPath.indexOf("!/") < innerPath.length() - 2) {  // If it contains "!/" not at last.
                    throw new MalformedURLException("Invalid CodeSource URL, 'jar:' does not end with '!/': " + codeSourceUrl);
                }
                final String innerUrlString = innerUrl.toString();
                codeSourceUrlBase = new URL("jar", "", -1, innerPath.endsWith("!/") ? innerUrlString : innerUrlString + "!/");
            } else {
                throw new MalformedURLException("Invalid CodeSource URL, neither 'file:' nor 'jar:': " + codeSourceUrl);
            }
        } catch (final Exception ex) {
            System.err.println("org.embulk.deps.SelfContainedJarFile is loaded through invalid method or location.");
            ex.printStackTrace();
            hasFailedInCodeSource = true;
        }

        if (hasFailedInCodeSource) {
            CODE_SOURCE_URL_BASE = null;
        } else {
            CODE_SOURCE_URL_BASE = codeSourceUrlBase;
        }
    }

    private static final ProtectionDomain PROTECTION_DOMAIN;
    private static final CodeSource CODE_SOURCE;
    private static final URL CODE_SOURCE_URL_BASE;

    private final URL codeSourceUrl;
    private final Manifest manifest;
    private final LinkedHashMap innerResources;
    private final ByteBuffer innerResourcesBinary;
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy