mitm.common.util.ClearableDeferredOutputStream.java Source code

Java tutorial

Introduction

Here is the source code for mitm.common.util.ClearableDeferredOutputStream.java

Source

/*
 * Copyright (c) 2010-2011, Martijn Brinkers, Djigzo.
 * 
 * This file is part of Djigzo email encryption.
 *
 * Djigzo is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License 
 * version 3, 19 November 2007 as published by the Free Software 
 * Foundation.
 *
 * Djigzo is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public 
 * License along with Djigzo. If not, see <http://www.gnu.org/licenses/>
 *
 * Additional permission under GNU AGPL version 3 section 7
 * 
 * If you modify this Program, or any covered work, by linking or 
 * combining it with aspectjrt.jar, aspectjweaver.jar, tyrex-1.0.3.jar, 
 * freemarker.jar, dom4j.jar, mx4j-jmx.jar, mx4j-tools.jar, 
 * spice-classman-1.0.jar, spice-loggerstore-0.5.jar, spice-salt-0.8.jar, 
 * spice-xmlpolicy-1.0.jar, saaj-api-1.3.jar, saaj-impl-1.3.jar, 
 * wsdl4j-1.6.1.jar (or modified versions of these libraries), 
 * containing parts covered by the terms of Eclipse Public License, 
 * tyrex license, freemarker license, dom4j license, mx4j license,
 * Spice Software License, Common Development and Distribution License
 * (CDDL), Common Public License (CPL) the licensors of this Program grant 
 * you additional permission to convey the resulting work.
 */
package mitm.common.util;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.output.DeferredFileOutputStream;

/**
 * An output stream which will retain data in memory until a specified threshold is reached, and only then commit it 
 * to disk. If the stream is closed before the threshold is reached, the data will not be written to disk at all.
 * The written stream can be cleared by calling clear. This will result in a new file if the threshold is reached. 
 * 
 * @author Martijn Brinkers
 *
 */
public class ClearableDeferredOutputStream extends OutputStream {
    /*
     * The threshold at which a temp file will be used.
     */
    private final int threshold;

    /*
     * The prefix for temp files.
     */
    private final String prefix;

    /*
     * The suffix for temp files.
     */
    private final String suffix;

    /*
     * The dir to use for temp files. If null, use system temp dir.
     */
    private final File directory;

    /*
     * Used for storing the output in memory or file depending on nr of bytes written and threshold.
     */
    private DeferredFileOutputStream delegate;

    /*
     * Buffers the DeferredFileOutputStream to spead up writing to file
     */
    private BufferedOutputStream bufferedDelegate;

    /*
     * True if this stream is closed.
     */
    private boolean closed;

    public ClearableDeferredOutputStream(int threshold, String prefix, String suffix, File directory) {
        this.threshold = threshold;
        this.prefix = prefix;
        this.suffix = suffix;
        this.directory = directory;

        delegate = new DeferredFileOutputStream(threshold, prefix, suffix, directory);
        bufferedDelegate = new BufferedOutputStream(delegate);
    }

    private void checkClosed() throws IOException {
        if (closed) {
            throw new IOException("Stream is closed.");
        }
    }

    /**
     * Write the current output of this stream to output. If autoClose is true, the stream will be closed. 
     */
    public void writeTo(OutputStream output, boolean autoClose) throws IOException {
        checkClosed();

        /*
         * We must first close the delegate stream before we can write to output to be 100% certain that all 
         * data written. If we would not close it, it can happen that not all data is written to the file when 
         * we try to read from the file.
         */
        bufferedDelegate.close();

        delegate.writeTo(output);

        DeferredFileOutputStream newDelegate = null;

        if (!autoClose) {
            /*
             * We should create a new DeferredFileOutputStream and copy the existing data to it
             */
            newDelegate = new DeferredFileOutputStream(threshold, prefix, suffix, directory);

            delegate.writeTo(newDelegate);
        } else {
            closed = true;
        }

        /*
         * Make sure the old temp file is deleted
         */
        deleteFile();

        if (newDelegate != null) {
            /*
             * Set the new DeferredFileOutputStream
             */
            delegate = newDelegate;
            bufferedDelegate = new BufferedOutputStream(delegate);
        }
    }

    public void clear() throws IOException {
        checkClosed();

        cleanup();

        delegate = new DeferredFileOutputStream(threshold, prefix, suffix, directory);
        bufferedDelegate = new BufferedOutputStream(delegate);
    }

    @Override
    public void write(int i) throws IOException {
        checkClosed();

        bufferedDelegate.write(i);
    }

    @Override
    public void write(byte b[]) throws IOException {
        checkClosed();

        bufferedDelegate.write(b);
    }

    @Override
    public void write(byte b[], int off, int len) throws IOException {
        bufferedDelegate.write(b, off, len);
    }

    @Override
    public void flush() throws IOException {
        checkClosed();

        bufferedDelegate.flush();
    }

    private void deleteFile() {
        if (delegate != null) {
            FileUtils.deleteQuietly(delegate.getFile());
        }
    }

    private void cleanup() throws IOException {
        /*
         * Need to close the buffer before deleting the file, otherwise there is 
         * a chance that the file is not yet created but closing the buffer, which
         * flushes the buffer will result in the file.
         */
        bufferedDelegate.close();

        deleteFile();
    }

    @Override
    public void close() throws IOException {
        if (!closed) {
            cleanup();

            closed = true;
        }
    }

    /*
     * Just for testing so we can check whether the file is deleted
     */
    protected DeferredFileOutputStream getDeferredFileOutputStream() throws IOException {
        /*
         * Need to flush the buffer first
         */
        bufferedDelegate.flush();

        return delegate;
    }
}