Package com.google.common.io

Source Code of com.google.common.io.FileBackedOutputStream$MemoryOutput

/*
* Copyright (C) 2008 The Guava Authors
*
* 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 com.google.common.io;

import com.google.common.annotations.Beta;
import com.google.common.annotations.VisibleForTesting;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
* An {@link OutputStream} that starts buffering to a byte array, but
* switches to file buffering once the data reaches a configurable size.
*
* <p>This class is thread-safe.
*
* @author Chris Nokleberg
* @since Guava release 01
*/
@Beta
public final class FileBackedOutputStream extends OutputStream {

  private final int fileThreshold;
  private final boolean resetOnFinalize;
  private final InputSupplier<InputStream> supplier;

  private OutputStream out;
  private MemoryOutput memory;
  private File file;

  /** ByteArrayOutputStream that exposes its internals. */
  private static class MemoryOutput extends ByteArrayOutputStream {
    byte[] getBuffer() {
      return buf;
    }

    int getCount() {
      return count;
    }
  }

  /** Returns the file holding the data (possibly null). */
  @VisibleForTesting synchronized File getFile() {
    return file;
  }

  /**
   * Creates a new instance that uses the given file threshold, and does
   * not reset the data when the {@link InputSupplier} returned by
   * {@link #getSupplier} is finalized.
   *
   * @param fileThreshold the number of bytes before the stream should
   *     switch to buffering to a file
   */
  public FileBackedOutputStream(int fileThreshold) {
    this(fileThreshold, false);
  }

  /**
   * Creates a new instance that uses the given file threshold, and
   * optionally resets the data when the {@link InputSupplier} returned
   * by {@link #getSupplier} is finalized.
   *
   * @param fileThreshold the number of bytes before the stream should
   *     switch to buffering to a file
   * @param resetOnFinalize if true, the {@link #reset} method will
   *     be called when the {@link InputSupplier} returned by {@link
   *     #getSupplier} is finalized
   */
  public FileBackedOutputStream(int fileThreshold, boolean resetOnFinalize) {
    this.fileThreshold = fileThreshold;
    this.resetOnFinalize = resetOnFinalize;
    memory = new MemoryOutput();
    out = memory;

    if (resetOnFinalize) {
      supplier = new InputSupplier<InputStream>() {
        @Override
        public InputStream getInput() throws IOException {
          return openStream();
        }

        @Override protected void finalize() {
          try {
            reset();
          } catch (Throwable t) {
            t.printStackTrace(System.err);
          }
        }
      };
    } else {
      supplier = new InputSupplier<InputStream>() {
        @Override
        public InputStream getInput() throws IOException {
          return openStream();
        }
      };
    }
  }

  /**
   * Returns a supplier that may be used to retrieve the data buffered
   * by this stream.
   */
  public InputSupplier<InputStream> getSupplier() {
    return supplier;
  }

  private synchronized InputStream openStream() throws IOException {
    if (file != null) {
      return new FileInputStream(file);
    } else {
      return new ByteArrayInputStream(
          memory.getBuffer(), 0, memory.getCount());
    }
  }

  /**
   * Calls {@link #close} if not already closed, and then resets this
   * object back to its initial state, for reuse. If data was buffered
   * to a file, it will be deleted.
   *
   * @throws IOException if an I/O error occurred while deleting the file buffer
   */
  public synchronized void reset() throws IOException {
    try {
      close();
    } finally {
      if (memory == null) {
        memory = new MemoryOutput();
      } else {
        memory.reset();
      }
      out = memory;
      if (file != null) {
        File deleteMe = file;
        file = null;
        if (!deleteMe.delete()) {
          throw new IOException("Could not delete: " + deleteMe);
        }
      }
    }
  }

  @Override public synchronized void write(int b) throws IOException {
    update(1);
    out.write(b);
  }

  @Override public synchronized void write(byte[] b) throws IOException {
    write(b, 0, b.length);
  }

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

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

  @Override public synchronized void flush() throws IOException {
    out.flush();
  }

  /**
   * Checks if writing {@code len} bytes would go over threshold, and
   * switches to file buffering if so.
   */
  private void update(int len) throws IOException {
    if (file == null && (memory.getCount() + len > fileThreshold)) {
      File temp = File.createTempFile("FileBackedOutputStream", null);
      if (resetOnFinalize) {
        // Finalizers are not guaranteed to be called on system shutdown;
        // this is insurance.
        temp.deleteOnExit();
      }
      FileOutputStream transfer = new FileOutputStream(temp);
      transfer.write(memory.getBuffer(), 0, memory.getCount());
      transfer.flush();

      // We've successfully transferred the data; switch to writing to file
      out = transfer;
      file = temp;
      memory = null;
    }
  }
}
TOP

Related Classes of com.google.common.io.FileBackedOutputStream$MemoryOutput

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.