com.jaspersoft.jasperserver.api.metadata.common.domain.util.FileBufferedOutputStream.java Source code

Java tutorial

Introduction

Here is the source code for com.jaspersoft.jasperserver.api.metadata.common.domain.util.FileBufferedOutputStream.java

Source

/*
 * Copyright (C) 2005 - 2014 TIBCO Software Inc. All rights reserved.
 * http://www.jaspersoft.com.
 *
 * Unless you have purchased  a commercial license agreement from Jaspersoft,
 * the following license terms  apply:
 *
 * This program is free software: you can redistribute it and/or  modify
 * it under the terms of the GNU Affero General Public License  as
 * published by the Free Software Foundation, either version 3 of  the
 * License, or (at your option) any later version.
 *
 * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
 */

package com.jaspersoft.jasperserver.api.metadata.common.domain.util;

/**
 * Borrowed from JasperReports class of the same name. 
 * 
 * Modified the original DataStream class to cope with DB2 related issue: see ContentRepoFileResource.copyDataFrom
 * 
 * @author swood
 */

import net.sf.jasperreports.engine.util.JRProperties;

import com.jaspersoft.jasperserver.api.JSException;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.WeakHashMap;

import org.apache.commons.collections.map.ReferenceMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * @author Lucian Chirita (lucianc@users.sourceforge.net)
 * @version $Id: FileBufferedOutputStream.java 47331 2014-07-18 09:13:06Z kklein $
 */
public class FileBufferedOutputStream extends OutputStream {

    private static final Log log = LogFactory.getLog(FileBufferedOutputStream.class);

    /**
     * Specifies the maximum in-memory buffer length that triggers the creation of a temporary file on disk to store further content sent to this output stream.  
     */
    public static final String PROPERTY_MEMORY_THRESHOLD = JRProperties.PROPERTY_PREFIX
            + "file.buffer.os.memory.threshold";
    //public static final int DEFAULT_MEMORY_THRESHOLD = 1 << 18;
    public static final int INFINIT_MEMORY_THRESHOLD = -1;
    public static final int DEFAULT_INITIAL_MEMORY_BUFFER_SIZE = 1 << 16;
    public static final int DEFAULT_INPUT_BUFFER_LENGTH = 1 << 14;

    private final int memoryThreshold;
    private final int initialMemoryBufferSize;
    private final int inputBufferLength;

    private final ByteArrayOutputStream memoryOutput;
    private int size;
    private File file;
    private BufferedOutputStream fileOutput;
    private boolean closed;
    private boolean disposed;

    private final WeakHashMap<DataStream, Boolean> inputStreams;

    public FileBufferedOutputStream() {
        this(JRProperties.getIntegerProperty(PROPERTY_MEMORY_THRESHOLD, INFINIT_MEMORY_THRESHOLD),
                DEFAULT_INITIAL_MEMORY_BUFFER_SIZE, DEFAULT_INPUT_BUFFER_LENGTH);
    }

    public FileBufferedOutputStream(int memoryThreshold) {
        this(memoryThreshold, DEFAULT_INITIAL_MEMORY_BUFFER_SIZE, DEFAULT_INPUT_BUFFER_LENGTH);
    }

    public FileBufferedOutputStream(int memoryThreshold, int initialMemoryBufferSize) {
        this(memoryThreshold, initialMemoryBufferSize, DEFAULT_INPUT_BUFFER_LENGTH);
    }

    public FileBufferedOutputStream(int memoryThreshold, int initialMemoryBufferSize, int inputBufferLength) {
        this.memoryThreshold = memoryThreshold;
        this.initialMemoryBufferSize = initialMemoryBufferSize;
        this.inputBufferLength = inputBufferLength;

        size = 0;
        if (this.memoryThreshold == 0) {
            memoryOutput = null;
        } else {
            int initialSize = this.initialMemoryBufferSize;
            if (initialSize > this.memoryThreshold) {
                initialSize = this.memoryThreshold;
            }
            memoryOutput = new ByteArrayOutputStream(initialSize);
        }

        this.inputStreams = new WeakHashMap<DataStream, Boolean>();
    }

    public void write(int b) throws IOException {
        checkClosed();

        if (availableMemorySpace() > 0) {
            memoryOutput.write(b);
        } else {
            ensureFileOutput().write(b);
        }

        ++size;
    }

    protected int availableMemorySpace() {
        int availableMemorySpace;
        if (memoryOutput != null && (memoryThreshold < 0 || memoryOutput.size() < memoryThreshold)) {
            availableMemorySpace = memoryThreshold - memoryOutput.size();
        } else {
            availableMemorySpace = 0;
        }
        return availableMemorySpace;
    }

    protected BufferedOutputStream ensureFileOutput() throws IOException, FileNotFoundException {
        if (fileOutput == null) {
            file = File.createTempFile("file.buff.os.", ".tmp");
            FileOutputStream fileOutputStream = new FileOutputStream(file);
            fileOutput = new BufferedOutputStream(fileOutputStream);
        }
        return fileOutput;
    }

    public void write(byte[] b, int off, int len) throws IOException {
        checkClosed();

        int memoryLen = availableMemorySpace();
        if (len < memoryLen) {
            memoryLen = len;
        }

        if (memoryLen > 0) {
            memoryOutput.write(b, off, memoryLen);
        }

        if (memoryLen < len) {
            ensureFileOutput().write(b, off + memoryLen, len - memoryLen);
        }

        size += len;
    }

    public void checkClosed() {
        if (closed) {
            throw new JSException("Output stream already closed.");
        }
    }

    public void close() throws IOException {
        if (!closed && fileOutput != null) {
            fileOutput.flush();
            fileOutput.close();
        }

        closed = true;
    }

    public void flush() throws IOException {
        if (fileOutput != null) {
            fileOutput.flush();
        }
    }

    public int size() {
        return size;
    }

    public void writeData(OutputStream out) throws IOException {
        if (!closed) {
            close();
        }

        if (memoryOutput != null) {
            memoryOutput.writeTo(out);
        }

        if (file != null) {
            FileInputStream fileInput = new FileInputStream(file);
            boolean inputClosed = false;
            try {
                byte[] buffer = new byte[inputBufferLength];
                int read;
                while ((read = fileInput.read(buffer)) > 0) {
                    out.write(buffer, 0, read);
                }
                fileInput.close();
                inputClosed = true;
            } finally {
                if (!inputClosed) {
                    try {
                        fileInput.close();
                    } catch (IOException e) {
                        log.warn("Could not close file input stream", e);
                    }
                }
            }
        }
    }

    public void dispose() {
        if (disposed) {
            return;
        }

        boolean success = true;
        if (!closed && fileOutput != null) {
            try {
                fileOutput.close();
            } catch (IOException e) {
                log.warn("Error while closing the temporary file output stream", e);
                success = false;
            }
        }

        for (DataStream dataStream : inputStreams.keySet()) {
            try {
                dataStream.close();
            } catch (IOException e) {
                log.warn("Error while closing temporary file input stream", e);
                success = false;
            }
        }

        if (file != null && !file.delete()) {
            log.warn("Error while deleting the temporary file");
            success = false;
        }

        disposed = success;
    }

    protected void finalize() throws Throwable {
        dispose();
        super.finalize();
    }

    public InputStream getDataInputStream() throws IOException {
        if (!closed) {
            close();
        }

        DataStream dataStream = new DataStream();
        // keep for dispose()
        inputStreams.put(dataStream, Boolean.TRUE);
        return dataStream;
    }

    protected class DataStream extends InputStream {
        private int memoryIdx;
        private int memoryLength;

        private final InputStream byteInputStream;
        private final InputStream fileInput;

        public DataStream() throws FileNotFoundException, IOException {
            memoryIdx = 0;
            byte[] memoryData = memoryOutput == null ? new byte[0] : memoryOutput.toByteArray();
            memoryLength = memoryData.length;
            byteInputStream = new ByteArrayInputStream(memoryData);
            fileInput = file == null ? null : new BufferedInputStream(new FileInputStream(file));
        }

        public synchronized int read() throws IOException {
            int read;

            if (memoryIdx < memoryLength) {
                read = byteInputStream.read();
                ++memoryIdx;
            } else if (fileInput != null) {
                read = fileInput.read();
            } else {
                read = -1;
            }
            return read;
        }

        public synchronized int read(byte b[], int off, int len) throws IOException {
            if (len <= 0) {
                return 0;
            }

            int read;
            if (memoryIdx < memoryLength) {
                read = len;
                if (read > memoryLength - memoryIdx) {
                    read = memoryLength - memoryIdx;
                }

                byteInputStream.read(b, off, read);
                memoryIdx += read;

            } else {
                read = 0;
            }

            if (read < len && fileInput != null) {
                int readFile = fileInput.read(b, off + read, len - read);
                if (readFile > 0) {
                    read += readFile;
                }
            }

            return read == 0 ? -1 : read;
        }

        public void close() throws IOException {

            if (fileInput != null) {
                fileInput.close();
            }
        }

        public synchronized int available() throws IOException {
            int available = byteInputStream.available();
            if (fileInput != null) {
                available += fileInput.available();
            }

            return available;
        }

        public synchronized long skip(long n) throws IOException {
            if (n <= 0) {
                return 0;
            }

            long skipped;
            if (memoryIdx < memoryLength) {
                skipped = n;
                if (skipped > memoryLength - memoryIdx) {
                    skipped = memoryLength - memoryIdx;
                }

                skipped = byteInputStream.skip(skipped);
                memoryIdx += skipped;
            } else {
                skipped = 0;
            }

            if (skipped < n && fileInput != null) {
                skipped += fileInput.skip(n - skipped);
            }

            return skipped;
        }

        protected void finalize() throws Throwable {
            close();
            super.finalize();
        }

    }
}