pt.quintans.ezSQL.toolkit.io.BinStore.java Source code

Java tutorial

Introduction

Here is the source code for pt.quintans.ezSQL.toolkit.io.BinStore.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 pt.quintans.ezSQL.toolkit.io;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;

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

/**
 * <p>
 * The default implementation of the {@link org.apache.commons.fileupload.FileItem FileItem} interface.
 * 
 * <p>
 * After retrieving an instance of this class from a {@link org.apache.commons.fileupload.DiskFileUpload DiskFileUpload} instance (see
 * {@link org.apache.commons.fileupload.DiskFileUpload #parseRequest(javax.servlet.http.HttpServletRequest)}), you may either request all contents of file at once using
 * {@link #get()} or request an {@link java.io.InputStream InputStream} with {@link #getInputStream()} and process the file without attempting to load it into memory, which may
 * come handy with large files.
 * 
 * <p>
 * Temporary files, which are created for file items, should be deleted later on. The best way to do this is using a {@link FileCleaningTracker}, which you can set on the
 * {@link DiskFileItemFactory}. However, if you do use such a tracker, then you must consider the following: Temporary files are automatically deleted as soon as they are no longer
 * needed. (More precisely, when the corresponding instance of {@link java.io.File} is garbage collected.) This is done by the so-called reaper thread, which is started
 * automatically when the class {@link org.apache.commons.io.FileCleaner} is loaded. It might make sense to terminate that thread, for example, if your web application ends. See
 * the section on "Resource cleanup" in the users guide of commons-fileupload.
 * </p>
 * 
 * @author <a href="mailto:Rafal.Krzewski@e-point.pl">Rafal Krzewski</a>
 * @author <a href="mailto:sean@informage.net">Sean Legassick</a>
 * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
 * @author <a href="mailto:jmcnally@apache.org">John McNally</a>
 * @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
 * @author Sean C. Sullivan
 * 
 * @since FileUpload 1.1
 * 
 * @version $Id: DiskFileItem.java 963609 2010-07-13 06:56:47Z jochen $
 */
public class BinStore implements Serializable {

    // ----------------------------------------------------- Manifest constants

    /**
     * The UID to use when serializing this instance.
     */
    private static final long serialVersionUID = 2237570099615271025L;

    public static final int DEFAULT_THRESHOLD = 65535; // 65KB

    // ----------------------------------------------------------- Data members

    /**
     * The size of the item, in bytes. This is used to cache the size when a
     * file item is moved from its original location.
     */
    private long size = -1;

    /**
     * The threshold above which uploads will be stored on disk.
     */
    private int sizeThreshold = DEFAULT_THRESHOLD;

    /**
     * Cached contents of the file.
     */
    private byte[] cachedContent;

    /**
     * Output stream for this item.
     */
    private transient DeferredFileOutputStream dfos;

    /**
     * The temporary file to use.
     */
    private transient File tempFile;

    /**
     * File to allow for serialization of the content of this item.
     */
    private File dfosFile;

    // ----------------------------------------------------------- Constructors

    public BinStore() {
    }

    public BinStore(byte[] data) {
        set(data);
    }

    public BinStore(int sizeThreshold, byte[] data) {
        this(sizeThreshold);
        set(data);
    }

    /**
     * Constructs a new <code>DiskFileItem</code> instance.
     * 
     * @param sizeThreshold
     *            The threshold, in bytes, below which items will be
     *            retained in memory and above which they will be
     *            stored as a file.
     */
    public BinStore(int sizeThreshold) {
        this.sizeThreshold = sizeThreshold;
    }

    // ------------------------------- Methods from javax.activation.DataSource

    /**
     * Returns an {@link java.io.InputStream InputStream} that can be
     * used to retrieve the contents of the file.
     * 
     * @return An {@link java.io.InputStream InputStream} that can be
     *         used to retrieve the contents of the file.
     * 
     * @throws IOException
     *             if an error occurs.
     */
    public InputStream getInputStream() throws IOException {
        if (!isInMemory()) {
            return new FileInputStream(this.dfos.getFile());
        }

        if (this.cachedContent == null) {
            this.cachedContent = this.dfos.getData();
        }
        return new ByteArrayInputStream(this.cachedContent);
    }

    // ------------------------------------------------------- FileItem methods

    /**
     * Provides a hint as to whether or not the file contents will be read
     * from memory.
     * 
     * @return <code>true</code> if the file contents will be read
     *         from memory; <code>false</code> otherwise.
     */
    public boolean isInMemory() {
        if (this.cachedContent != null) {
            return true;
        }
        return this.dfos.isInMemory();
    }

    /**
     * Returns the size of the file.
     * 
     * @return The size of the file, in bytes.
     */
    public long getSize() {
        if (this.size >= 0) {
            return this.size;
        } else if (this.cachedContent != null) {
            return this.cachedContent.length;
        } else if (this.dfos.isInMemory()) {
            return this.dfos.getData().length;
        } else {
            return this.dfos.getFile().length();
        }
    }

    /**
     * Returns the contents of the file as an array of bytes. If the
     * contents of the file were not yet cached in memory, they will be
     * loaded from the disk storage and cached.
     * 
     * @return The contents of the file as an array of bytes.
     */
    public byte[] get() {
        if (isInMemory()) {
            if (this.cachedContent == null) {
                this.cachedContent = this.dfos.getData();
            }
            return this.cachedContent;
        }

        byte[] fileData = new byte[(int) getSize()];
        FileInputStream fis = null;

        try {
            fis = new FileInputStream(this.dfos.getFile());
            fis.read(fileData);
        } catch (IOException e) {
            fileData = null;
        } finally {
            IOUtils.closeQuietly(fis);
        }

        return fileData;
    }

    /**
     * Sets the contents using an array of bytes as a source
     * 
     * @param data
     *            The array of bytes with the data
     */
    public void set(byte[] data) {
        try {
            this.cachedContent = null;
            copyAndClose(new ByteArrayInputStream(data), getOutputStream());
        } catch (IOException e) {
            // ignore
        }
    }

    /**
     * Sets the contents using a File as a source
     * 
     * @param source
     *            The file with the data
     */
    public void set(File source) throws IOException {
        set(new FileInputStream(source));
    }

    /**
     * Sets the contents using a InputStream as a source
     * 
     * @param source
     *            The InputStream with the data
     */
    public void set(InputStream source) throws IOException {
        OutputStream out = null;
        try {
            out = getOutputStream();
            IOUtils.copy(source, out);
        } finally {
            IOUtils.closeQuietly(out);
        }
    }

    /**
     * A convenience method to write an uploaded item to disk. The client code
     * is not concerned with whether or not the item is stored in memory, or on
     * disk in a temporary location. They just want to write the uploaded item
     * to a file.
     * <p>
     * This implementation first attempts to rename the uploaded item to the specified destination file, if the item was originally written to disk. Otherwise, the data will be
     * copied to the specified file.
     * <p>
     * This method is only guaranteed to work <em>once</em>, the first time it is invoked for a particular item. This is because, in the event that the method renames a temporary
     * file, that file will no longer be available to copy or rename again at a later time.
     * 
     * @param file
     *            The <code>File</code> into which the uploaded item should
     *            be stored.
     * 
     * @throws Exception
     *             if an error occurs.
     */
    public void write(File file) throws Exception {
        if (isInMemory()) {
            FileOutputStream fout = null;
            try {
                fout = new FileOutputStream(file);
                fout.write(get());
            } finally {
                if (fout != null) {
                    fout.close();
                }
            }
        } else {
            File outputFile = getStoreLocation();
            if (outputFile != null) {
                // Save the length of the file
                this.size = outputFile.length();
                /*
                 * The uploaded file is being stored on disk
                 * in a temporary location so move it to the
                 * desired file.
                 */
                if (!outputFile.renameTo(file)) {
                    BufferedInputStream in = null;
                    BufferedOutputStream out = null;
                    try {
                        in = new BufferedInputStream(new FileInputStream(outputFile));
                        out = new BufferedOutputStream(new FileOutputStream(file));
                        IOUtils.copy(in, out);
                    } finally {
                        if (in != null) {
                            try {
                                in.close();
                            } catch (IOException e) {
                                // ignore
                            }
                        }
                        if (out != null) {
                            try {
                                out.close();
                            } catch (IOException e) {
                                // ignore
                            }
                        }
                    }
                }
            } else {
                /*
                 * For whatever reason we cannot write the
                 * file to disk.
                 */
                throw new IOException("Cannot write to disk!");
            }
        }
    }

    /**
     * Deletes the underlying storage for a file item, including deleting any
     * associated temporary disk file. Although this storage will be deleted
     * automatically when the <code>FileItem</code> instance is garbage
     * collected, this method can be used to ensure that this is done at an
     * earlier time, thus preserving system resources.
     */
    public void delete() {
        this.cachedContent = null;
        File outputFile = getStoreLocation();
        if (outputFile != null && outputFile.exists()) {
            outputFile.delete();
        }
    }

    public OutputStream getOutputStream() throws IOException {
        return getOutputStream(null);
    }

    /**
     * Returns an {@link java.io.OutputStream OutputStream} that can
     * be used for storing the contents of the file.
     * 
     * @param directory
     *            The directory where the temporary file is created.<br>
     *            If <code>null</code> it's created in the default directory.
     * 
     * @return An {@link java.io.OutputStream OutputStream} that can be used
     *         for storing the contents of the file.
     * 
     * @throws IOException
     *             if an error occurs.
     */
    public OutputStream getOutputStream(File directory) throws IOException {
        this.dfos = new DeferredFileOutputStream(this.sizeThreshold, getTempFile(directory));
        return this.dfos;
    }

    // --------------------------------------------------------- Public methods

    /**
     * Returns the {@link java.io.File} object for the <code>FileItem</code>'s
     * data's temporary location on the disk. Note that for <code>FileItem</code>s that have their data stored in memory,
     * this method will return <code>null</code>. When handling large
     * files, you can use {@link java.io.File#renameTo(java.io.File)} to
     * move the file to new location without copying the data, if the
     * source and destination locations reside within the same logical
     * volume.
     * 
     * @return The data file, or <code>null</code> if the data is stored in
     *         memory.
     */
    public File getStoreLocation() {
        return this.dfos == null ? null : this.dfos.getFile();
    }

    // ------------------------------------------------------ Protected methods

    /**
     * Removes the file contents from the temporary storage.
     */
    @Override
    protected void finalize() {
        File outputFile = this.dfos.getFile();

        if (outputFile != null && outputFile.exists()) {
            outputFile.delete();
        }
    }

    /**
     * Creates and returns a {@link java.io.File File} representing a uniquely
     * named temporary file in the configured repository path. The lifetime of
     * the file is tied to the lifetime of the <code>FileItem</code> instance;
     * the file will be deleted when the instance is garbage collected.
     * 
     * @param directory
     *            The directory where this temporary file is created
     * @return The {@link java.io.File File} to be used for temporary storage.
     */
    protected File getTempFile(File directory) {
        if (this.tempFile == null) {
            try {
                if (directory != null && directory.isDirectory())
                    this.tempFile = File.createTempFile("byteCache_", null, directory);
                else
                    this.tempFile = File.createTempFile("byteCache_", null);
                this.tempFile.deleteOnExit();
            } catch (IOException e) {
            }
        }
        return this.tempFile;
    }

    // -------------------------------------------------------- Private methods

    private void copyAndClose(InputStream in, OutputStream out) throws IOException {
        try {
            IOUtils.copy(in, out);
        } finally {
            IOUtils.closeQuietly(in);
            IOUtils.closeQuietly(out);
        }
    }

    /**
     * Returns a string representation of this object.
     * 
     * @return a string representation of this object.
     */
    @Override
    public String toString() {
        return "StoreLocation=" + (isInMemory() ? "IN MEMORY" : String.valueOf(getStoreLocation())) + ", size="
                + getSize() + "bytes";
    }

    // -------------------------------------------------- Serialization methods

    /**
     * Writes the state of this object during serialization.
     * 
     * @param out
     *            The stream to which the state should be written.
     * 
     * @throws IOException
     *             if an error occurs.
     */
    private void writeObject(ObjectOutputStream out) throws IOException {
        // Read the data
        if (this.dfos.isInMemory()) {
            this.cachedContent = get();
        } else {
            this.cachedContent = null;
            this.dfosFile = this.dfos.getFile();
        }

        // write out values
        out.defaultWriteObject();
    }

    /**
     * Reads the state of this object during deserialization.
     * 
     * @param in
     *            The stream from which the state should be read.
     * 
     * @throws IOException
     *             if an error occurs.
     * @throws ClassNotFoundException
     *             if class cannot be found.
     */
    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        // read values
        in.defaultReadObject();

        OutputStream output = getOutputStream();
        if (this.cachedContent != null) {
            output.write(this.cachedContent);
        } else {
            FileInputStream input = new FileInputStream(this.dfosFile);
            IOUtils.copy(input, output);
            this.dfosFile.delete();
            this.dfosFile = null;
        }
        output.close();

        this.cachedContent = null;
    }
}