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

de.schlichtherle.truezip.fs.FsFinalizeController Maven / Gradle / Ivy

/*
 * Copyright (C) 2005-2015 Schlichtherle IT Services.
 * All rights reserved. Use is subject to license terms.
 */
package de.schlichtherle.truezip.fs;

import de.schlichtherle.truezip.entry.Entry;
import de.schlichtherle.truezip.io.DecoratingInputStream;
import de.schlichtherle.truezip.io.DecoratingOutputStream;
import de.schlichtherle.truezip.io.DecoratingSeekableByteChannel;
import de.schlichtherle.truezip.rof.DecoratingReadOnlyFile;
import de.schlichtherle.truezip.rof.ReadOnlyFile;
import de.schlichtherle.truezip.socket.DecoratingInputSocket;
import de.schlichtherle.truezip.socket.DecoratingOutputSocket;
import de.schlichtherle.truezip.socket.InputSocket;
import de.schlichtherle.truezip.socket.OutputSocket;
import de.schlichtherle.truezip.util.BitField;
import de.schlichtherle.truezip.util.ControlFlowException;
import de.schlichtherle.truezip.util.JSE7;
import edu.umd.cs.findbugs.annotations.CreatesObligation;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.channels.SeekableByteChannel;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.CheckForNull;
import javax.annotation.WillCloseWhenClosed;
import javax.annotation.concurrent.Immutable;
import javax.annotation.concurrent.ThreadSafe;

/**
 * Finalizes unclosed resources returned by its decorated controller.
 *
 * @param   the type of the file system model.
 * @since  TrueZIP 7.5
 * @author Christian Schlichtherle
 */
@ThreadSafe
final class FsFinalizeController
extends FsDecoratingController> {

    private static final Logger logger = Logger.getLogger(
            FsFinalizeController.class.getName(),
            FsFinalizeController.class.getName());

    private static final SocketFactory SOCKET_FACTORY = JSE7.AVAILABLE
            ? SocketFactory.NIO2
            : SocketFactory.OIO;

    private static final IOException OK = new IOException((Throwable) null);

    /**
     * Constructs a new file system finalize controller.
     *
     * @param controller the decorated file system controller.
     */
    FsFinalizeController(FsController controller) {
        super(controller);
    }

    @Override
    public InputSocket getInputSocket(   FsEntryName name,
                                            BitField options) {
        return SOCKET_FACTORY.newInputSocket(this, name, options);
    }

    @Override
    public OutputSocket getOutputSocket( FsEntryName name,
                                            BitField options,
                                            @CheckForNull Entry template) {
        return SOCKET_FACTORY.newOutputSocket(this, name, options, template);
    }

    static void finalize(   final Closeable delegate,
                            final @CheckForNull IOException close) {
        if (OK == close) {
            logger.log(Level.FINEST, "closeCleared");
        } else if (null != close) {
            logger.log(Level.FINEST, "closeFailed", close);
        } else {
            try {
                delegate.close();
                logger.log(Level.INFO, "finalizeCleared");
            } catch (final ControlFlowException ex) {  // log and swallow!
                logger.log(Level.SEVERE, "finalizeFailed",
                        new AssertionError("Unexpected control flow exception!", ex));
            } catch (final Throwable ex) {              // log and swallow!
                logger.log(Level.WARNING, "finalizeFailed", ex);
            }
        }
    }

    @Immutable
    private enum SocketFactory {
        NIO2() {
            @Override
            InputSocket newInputSocket(
                    FsFinalizeController controller,
                    FsEntryName name,
                    BitField options) {
                return controller.new Nio2Input(name, options);
            }

            @Override
            OutputSocket newOutputSocket(
                    FsFinalizeController controller,
                    FsEntryName name,
                    BitField options,
                    @CheckForNull Entry template) {
                return controller.new Nio2Output(name, options, template);
            }
        },

        OIO() {
            @Override
            InputSocket newInputSocket(
                    FsFinalizeController controller,
                    FsEntryName name,
                    BitField options) {
                return controller.new Input(name, options);
            }

            @Override
            OutputSocket newOutputSocket(
                    FsFinalizeController controller,
                    FsEntryName name,
                    BitField options,
                    @CheckForNull Entry template) {
                return controller.new Output(name, options, template);
            }
        };

        abstract InputSocket newInputSocket(
                FsFinalizeController controller,
                FsEntryName name,
                BitField options);

        abstract OutputSocket newOutputSocket(
                FsFinalizeController controller,
                FsEntryName name,
                BitField options,
                @CheckForNull Entry template);
    } // SocketFactory

    @Immutable
    private final class Nio2Input extends Input {
        Nio2Input(  final FsEntryName name,
                    final BitField options) {
            super(name, options);
        }

        @Override
        public SeekableByteChannel newSeekableByteChannel() throws IOException {
            return new FinalizeSeekableByteChannel(
                    getBoundSocket().newSeekableByteChannel());
        }
    } // Nio2Input

    @Immutable
    private class Input extends DecoratingInputSocket {
        Input(  final FsEntryName name,
                final BitField options) {
            super(FsFinalizeController.this.delegate
                    .getInputSocket(name, options));
        }

        @Override
        public ReadOnlyFile newReadOnlyFile() throws IOException {
            return new FinalizeReadOnlyFile(
                    getBoundSocket().newReadOnlyFile());
        }

        @Override
        public InputStream newInputStream() throws IOException {
            return new FinalizeInputStream(
                    getBoundSocket().newInputStream());
        }
    } // Input

    @Immutable
    private final class Nio2Output extends Output {
        Nio2Output( final FsEntryName name,
                    final BitField options,
                    final @CheckForNull Entry template) {
            super(name, options, template);
        }

        @Override
        public SeekableByteChannel newSeekableByteChannel() throws IOException {
            return new FinalizeSeekableByteChannel(
                    getBoundSocket().newSeekableByteChannel());
        }
    } // Nio2Output

    @Immutable
    private class Output extends DecoratingOutputSocket {
        Output( final FsEntryName name,
                final BitField options,
                final @CheckForNull Entry template) {
            super(FsFinalizeController.this.delegate
                    .getOutputSocket(name, options, template));
        }

        @Override
        @edu.umd.cs.findbugs.annotations.SuppressWarnings("OBL_UNSATISFIED_OBLIGATION") // false positive
        public OutputStream newOutputStream() throws IOException {
            return new FinalizeOutputStream(
                    getBoundSocket().newOutputStream());
        }
    } // Output

    private static final class FinalizeReadOnlyFile
    extends DecoratingReadOnlyFile {
        volatile IOException close; // accessed by finalizer thread!

        @CreatesObligation
        @edu.umd.cs.findbugs.annotations.SuppressWarnings("OBL_UNSATISFIED_OBLIGATION")
        FinalizeReadOnlyFile(@WillCloseWhenClosed ReadOnlyFile rof) {
            super(rof);
        }

        @Override
        public void close() throws IOException {
            try {
                delegate.close();
                close = OK;
            } catch (final IOException ex) {
                throw close = ex;
            }
        }

        @Override
        @SuppressWarnings("FinalizeDeclaration")
        protected void finalize() throws Throwable {
            try {
                FsFinalizeController.finalize(delegate, close);
            } finally {
                super.finalize();
            }
        }
    } // FinalizeReadOnlyFile

    private static final class FinalizeSeekableByteChannel
    extends DecoratingSeekableByteChannel {
        volatile IOException close; // accessed by finalizer thread!

        @CreatesObligation
        @edu.umd.cs.findbugs.annotations.SuppressWarnings("OBL_UNSATISFIED_OBLIGATION")
        FinalizeSeekableByteChannel(@WillCloseWhenClosed SeekableByteChannel sbc) {
            super(sbc);
        }

        @Override
        public void close() throws IOException {
            try {
                delegate.close();
                close = OK;
            } catch (final IOException ex) {
                throw close = ex;
            }
        }

        @Override
        @SuppressWarnings("FinalizeDeclaration")
        protected void finalize() throws Throwable {
            try {
                FsFinalizeController.finalize(delegate, close);
            } finally {
                super.finalize();
            }
        }
    } // FinalizeSeekableByteChannel

    private static final class FinalizeInputStream
    extends DecoratingInputStream {
        volatile IOException close; // accessed by finalizer thread!

        @CreatesObligation
        @edu.umd.cs.findbugs.annotations.SuppressWarnings("OBL_UNSATISFIED_OBLIGATION")
        FinalizeInputStream(@WillCloseWhenClosed InputStream in) {
            super(in);
        }

        @Override
        public void close() throws IOException {
            try {
                delegate.close();
                close = OK;
            } catch (final IOException ex) {
                throw close = ex;
            }
        }

        @Override
        @SuppressWarnings("FinalizeDeclaration")
        protected void finalize() throws Throwable {
            try {
                FsFinalizeController.finalize(delegate, close);
            } finally {
                super.finalize();
            }
        }
    } // FinalizeInputStream

    private static final class FinalizeOutputStream
    extends DecoratingOutputStream {
        volatile IOException close; // accessed by finalizer thread!

        @CreatesObligation
        @edu.umd.cs.findbugs.annotations.SuppressWarnings("OBL_UNSATISFIED_OBLIGATION")
        FinalizeOutputStream(@WillCloseWhenClosed OutputStream out) {
            super(out);
        }

        @Override
        public void close() throws IOException {
            try {
                delegate.close();
                close = OK;
            } catch (final IOException ex) {
                throw close = ex;
            }
        }

        @Override
        @SuppressWarnings("FinalizeDeclaration")
        protected void finalize() throws Throwable {
            try {
                FsFinalizeController.finalize(delegate, close);
            } finally {
                super.finalize();
            }
        }
    } // FinalizeOutputStream
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy