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

org.wildfly.plugin.tools.ReusableInputStream Maven / Gradle / Ivy

/*
 * Copyright The WildFly Authors
 * SPDX-License-Identifier: Apache-2.0
 */

package org.wildfly.plugin.tools;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.Objects;
import java.util.concurrent.locks.ReentrantLock;

import org.jboss.logging.Logger;

/**
 * @author James R. Perkins
 */
class ReusableInputStream extends InputStream {
    private static final Logger LOGGER = Logger.getLogger(ReusableInputStream.class);

    private final ReentrantLock lock = new ReentrantLock();
    private final String name;
    private final Path content;
    private final Thread shutdownHook;
    private boolean closed;
    private InputStream delegate;

    ReusableInputStream(final InputStream content, final String name) {
        this.name = name;
        this.content = createContent(content, name);
        this.shutdownHook = new Thread(new Thread(() -> {
            try {
                Files.deleteIfExists(ReusableInputStream.this.content);
            } catch (IOException e) {
                LOGGER.errorf(e, "Failed to content file delete file %s for deployment %s.", ReusableInputStream.this.content,
                        name);
            }
        }));
        // Add a shutdown hook to delete the file on close
        Runtime.getRuntime().addShutdownHook(shutdownHook);
        this.closed = false;
    }

    @Override
    public int read() throws IOException {
        return getDelegate().read();
    }

    @Override
    public int read(final byte[] b) throws IOException {
        return getDelegate().read(b);
    }

    @Override
    public int read(final byte[] b, final int off, final int len) throws IOException {
        return getDelegate().read(b, off, len);
    }

    @Override
    public byte[] readAllBytes() throws IOException {
        return getDelegate().readAllBytes();
    }

    @Override
    public byte[] readNBytes(final int len) throws IOException {
        return getDelegate().readNBytes(len);
    }

    @Override
    public int readNBytes(final byte[] b, final int off, final int len) throws IOException {
        return getDelegate().readNBytes(b, off, len);
    }

    @Override
    public long skip(final long n) throws IOException {
        return getDelegate().skip(n);
    }

    @Override
    public int available() throws IOException {
        return getDelegate().available();
    }

    @Override
    public void close() {
        close(false);
    }

    @Override
    public void mark(final int readlimit) {
        try {
            getDelegate().mark(readlimit);
        } catch (IOException e) {
            throw new UncheckedIOException("Failed to create the delegate.", e);
        }
    }

    @Override
    public void reset() {
        close(false);
    }

    @Override
    public boolean markSupported() {
        try {
            return getDelegate().markSupported();
        } catch (IOException e) {
            throw new UncheckedIOException("Failed to create the delegate.", e);
        }
    }

    @Override
    public long transferTo(final OutputStream out) throws IOException {
        return getDelegate().transferTo(out);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name);
    }

    @Override
    public boolean equals(final Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof ReusableInputStream)) {
            return false;
        }
        final ReusableInputStream other = (ReusableInputStream) obj;
        return Objects.equals(name, other.name);
    }

    @Override
    public String toString() {
        return "ReusableInputStream[" + name + ']';
    }

    /**
     * Closes this input stream and deletes the backing file if {@code deleteContent} is {@code true}.
     *
     * @param deleteContent {@code true} to delete the backing file and close this input stream, meaning it can no longer be
     *                          read from
     */
    void close(final boolean deleteContent) {
        lock.lock();
        try {
            if (delegate != null) {
                delegate.close();
                delegate = null;
            }
            if (deleteContent) {
                closed = true;
                Files.deleteIfExists(content);
                Runtime.getRuntime().removeShutdownHook(shutdownHook);
            }
        } catch (IOException e) {
            throw new UncheckedIOException("Failed to close delegate stream.", e);
        } finally {
            lock.unlock();
        }
    }

    private InputStream getDelegate() throws IOException {
        lock.lock();
        try {
            if (closed) {
                throw new IllegalStateException("This stream has been closed and can no longer be read from.");
            }
            if (delegate == null) {
                delegate = Files.newInputStream(content);
            }
            return delegate;
        } finally {
            lock.unlock();
        }
    }

    private static Path createContent(final InputStream content, final String name) {
        try (content) {
            final var file = Files.createTempFile("deployment-", "-" + name);
            // Use REPLACE_EXISTING because the file is created above
            Files.copy(content, file, StandardCopyOption.REPLACE_EXISTING);
            return file;
        } catch (IOException e) {
            throw new UncheckedIOException("Failed to copy the content " + content, e);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy