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

net.java.truevfs.kernel.impl.FinalizeController Maven / Gradle / Ivy

/*
 * Copyright © 2005 - 2021 Schlichtherle IT Services.
 * All rights reserved. Use is subject to license terms.
 */
package net.java.truevfs.kernel.impl;

import bali.Lookup;
import lombok.val;
import net.java.truecommons.cio.*;
import net.java.truecommons.io.DecoratingInputStream;
import net.java.truecommons.io.DecoratingOutputStream;
import net.java.truecommons.io.DecoratingSeekableChannel;
import net.java.truecommons.logging.LocalizedLogger;
import net.java.truecommons.shed.BitField;
import net.java.truecommons.shed.ControlFlowException;
import net.java.truevfs.kernel.spec.FsAccessOption;
import net.java.truevfs.kernel.spec.FsController;
import net.java.truevfs.kernel.spec.FsDelegatingController;
import net.java.truevfs.kernel.spec.FsNodeName;
import org.slf4j.Logger;

import javax.annotation.CheckForNull;
import javax.annotation.concurrent.ThreadSafe;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.channels.SeekableByteChannel;
import java.util.Optional;

/**
 * Finalizes unclosed resources returned by its decorated controller.
 *
 * @author Christian Schlichtherle
 */
@ThreadSafe
abstract class FinalizeController implements FsDelegatingController {

    private static final Logger logger = new LocalizedLogger(FinalizeController.class);

    @Override
    public InputSocket input(BitField options, FsNodeName name) {
        return new DelegatingInputSocket() {

            final InputSocket socket = getController().input(options, name);

            @Override
            protected InputSocket socket() throws IOException {
                return socket;
            }

            @Override
            public InputStream stream(OutputSocket peer) throws IOException {
                return new FinalizeInputStream(socket.stream(peer));
            }

            @Override
            public SeekableByteChannel channel(OutputSocket peer) throws IOException {
                return new FinalizeSeekableChannel(socket.channel(peer));
            }
        };
    }

    @Override
    public OutputSocket output(BitField options, FsNodeName name, @CheckForNull Entry template) {
        return new DelegatingOutputSocket() {

            final OutputSocket socket = getController().output(options, name, template);

            @Override
            protected OutputSocket socket() throws IOException {
                return socket;
            }

            @Override
            public OutputStream stream(InputSocket peer) throws IOException {
                return new FinalizeOutputStream(socket.stream(peer));
            }

            @Override
            public SeekableByteChannel channel(InputSocket peer) throws IOException {
                return new FinalizeSeekableChannel(socket.channel(peer));
            }
        };
    }

    private static final class FinalizeInputStream extends DecoratingInputStream {

        final FinalizeCloseable closeable = new FinalizeCloseable(in);

        FinalizeInputStream(InputStream in) {
            super(in);
        }

        @Override
        public void close() throws IOException {
            closeable.close();
        }

        @Override
        @Deprecated
        protected void finalize() throws Throwable {
            try {
                closeable.onBeforeFinalize();
            } finally {
                super.finalize();
            }
        }
    }

    private static final class FinalizeOutputStream extends DecoratingOutputStream {

        final FinalizeCloseable closeable = new FinalizeCloseable(out);

        FinalizeOutputStream(OutputStream out) {
            super(out);
        }

        @Override
        public void close() throws IOException {
            closeable.close();
        }

        @Override
        @Deprecated
        protected void finalize() throws Throwable {
            try {
                closeable.onBeforeFinalize();
            } finally {
                super.finalize();
            }
        }
    }

    private static final class FinalizeSeekableChannel extends DecoratingSeekableChannel {

        final FinalizeCloseable closeable = new FinalizeCloseable(channel);

        FinalizeSeekableChannel(SeekableByteChannel channel) {
            super(channel);
        }

        @Override
        public void close() throws IOException {
            closeable.close();
        }

        @Override
        @Deprecated
        protected void finalize() throws Throwable {
            try {
                closeable.onBeforeFinalize();
            } finally {
                super.finalize();
            }
        }
    }

    private static final class FinalizeCloseable implements Closeable {

        final Closeable closeable;

        // Accessed by finalizer thread:
        @CheckForNull
        volatile Optional ioException;

        FinalizeCloseable(final Closeable closeable) {
            this.closeable = closeable;
        }

        @Override
        public final void close() throws IOException {
            try {
                closeable.close();
                ioException = Optional.empty();
            } catch (IOException e) {
                ioException = Optional.of(e);
                throw e;
            }
        }

        void onBeforeFinalize() {
            val e = ioException;
            //noinspection OptionalAssignedToNull
            if (null == e) {
                try {
                    closeable.close();
                    logger.info("finalizeCleared");
                } catch (ControlFlowException t) {
                    // Log and swallow:
                    logger.error("finalizeFailed", new AssertionError("Unexpected control flow exception!", t));
                } catch (Throwable t) {
                    // Log and swallow:
                    logger.warn("finalizeFailed", t);
                }
            } else if (e.isPresent()) {
                logger.trace("closeFailed", e.get());
            } else {
                logger.trace("closeCleared");
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy