io.squark.nestedjarclassloader.NestedJarURLConnection.java Source code

Java tutorial

Introduction

Here is the source code for io.squark.nestedjarclassloader.NestedJarURLConnection.java

Source

/*
 * Copyright (c) 2016 Erik Hkansson, http://squark.io
 *
 * Licensed 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.
 */

/*
 * Copyright (c) 2016 Erik Hkansson, http://squark.io
 *
 * Licensed 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.squark.nestedjarclassloader;

import com.google.common.io.FileBackedOutputStream;
import sun.net.www.ParseUtil;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarInputStream;

public class NestedJarURLConnection extends URLConnection implements AutoCloseable {

    private final boolean isDirectory;
    private URL jarFileURL;
    private String entryName;
    private boolean connected;
    private String subEntryName;
    private String file;
    private FileBackedOutputStream entryOutputStream;
    private FileBackedOutputStream subEntryOutputStream;

    /**
     * Constructs a URL connection to the specified URL. A connection to
     * the object referenced by the URL is not created.
     *
     * @param url the specified URL.
     */
    public NestedJarURLConnection(URL url, boolean connect, boolean isDirectory) throws IOException {
        super(url);
        this.isDirectory = isDirectory;
        parseSpecs(url);
        if (connect) {
            connect();
        }
    }

    /**
     * Modified from java.net.JarURLConnection
     *
     * @param url URL to parse
     * @throws MalformedURLException
     */
    private void parseSpecs(URL url) throws MalformedURLException {
        String spec = url.getFile();

        if (spec.startsWith("jar:")) {
            spec = spec.substring(4, spec.length());
        }

        int separator = spec.indexOf("!/");

        jarFileURL = new URL(spec.substring(0, separator++));
        entryName = null;

        /* if ! is the last letter of the innerURL, entryName is null */
        if (++separator != spec.length()) {
            entryName = spec.substring(separator, spec.length());
            entryName = ParseUtil.decode(entryName);
            int subEntrySeparator = entryName.indexOf("!/");
            if (subEntrySeparator != -1) {
                subEntryName = entryName.substring(subEntrySeparator + 2, entryName.length());
                entryName = entryName.substring(0, subEntrySeparator);
            }
        }
    }

    @Override
    public void connect() throws IOException {
        file = jarFileURL.getFile();
        if (entryName != null) {
            JarFile jarFile = new JarFile(file);
            InputStream is = jarFile.getInputStream(jarFile.getEntry(entryName));
            int len;
            byte[] b = new byte[2048];
            entryOutputStream = new FileBackedOutputStream((int) Runtime.getRuntime().freeMemory() / 2, true);

            while ((len = is.read(b)) > 0) {
                entryOutputStream.write(b, 0, len);
            }

            is.close();
            if (subEntryName != null) {
                JarInputStream entryInputStream = new JarInputStream(
                        entryOutputStream.asByteSource().openBufferedStream());
                JarEntry subEntry;
                while ((subEntry = entryInputStream.getNextJarEntry()) != null) {
                    if (subEntry.getName().equals(subEntryName)) {
                        subEntryOutputStream = new FileBackedOutputStream(
                                (int) Runtime.getRuntime().freeMemory() / 2, true);
                        if (subEntry.isDirectory()) {
                            JarInputStream newEntryInputStream = new JarInputStream(
                                    entryOutputStream.asByteSource().openBufferedStream());
                            OutputStreamWriter outputStreamWriter = new OutputStreamWriter(subEntryOutputStream);
                            JarEntry file;
                            while ((file = newEntryInputStream.getNextJarEntry()) != null) {
                                if (file.getName().startsWith(subEntryName)) {
                                    Path path;
                                    if ((path = Paths.get(file.getName())).getNameCount()
                                            - Paths.get(subEntryName).getNameCount() == 1) {
                                        outputStreamWriter.append(path.getFileName().toString()).append('\n');
                                    }
                                }
                            }
                            outputStreamWriter.close();
                            newEntryInputStream.close();
                        } else {
                            b = new byte[2048];
                            while ((len = entryInputStream.read(b)) > 0) {
                                subEntryOutputStream.write(b, 0, len);
                            }
                            break;
                        }
                    }
                }
                entryInputStream.close();
                entryOutputStream.reset();
                entryOutputStream.close();
                entryOutputStream = null;
            }
        }
        connected = true;
    }

    @Override
    public InputStream getInputStream() throws IOException {
        if (!connected) {
            connect();
        }
        if (subEntryName != null) {
            if (subEntryOutputStream != null) {
                return subEntryOutputStream.asByteSource().openBufferedStream();
            } else {
                throw new IOException("Failed to load " + subEntryName);
            }
        } else if (entryName != null) {
            if (entryOutputStream != null) {
                return entryOutputStream.asByteSource().openBufferedStream();
            } else {
                throw new IOException("Failed to load " + entryName);
            }
        } else {
            return new JarInputStream(new FileInputStream(file));
        }
    }

    @Override
    public void close() throws IOException {
        if (this.subEntryOutputStream != null) {
            this.subEntryOutputStream.reset();
            this.subEntryOutputStream.close();
            this.subEntryOutputStream = null;
        }
        if (this.entryOutputStream != null) {
            this.entryOutputStream.reset();
            this.entryOutputStream.close();
            this.entryOutputStream = null;
        }
    }
}