/*
* utils - TemporaryInputStream.java - Copyright © 2010 David Roden
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.pterodactylus.util.io;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
/**
* An input stream implementation that copies a given input stream to a
* temporary file and delivers the content of the temporary file at a later
* time.
*
* @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
public class TemporaryInputStream extends FilterInputStream {
/**
* Maps input streams to temporary files, for deletion on {@link #close()}.
*/
private static final Map<InputStream, File> streamFiles = new HashMap<InputStream, File>();
/** Counter for streams per file. */
private static final Map<File, Integer> fileCounts = new HashMap<File, Integer>();
/**
* Creates a new temporary input stream.
*
* @param sourceInputStream
* The input stream to copy to a temporary file
* @throws IOException
* if an I/O error occurs
*/
public TemporaryInputStream(InputStream sourceInputStream) throws IOException {
super(createFileInputStream(sourceInputStream));
}
/**
* Creates a new temporary input stream from the given temporary file.
*
* @param tempFile
* The temporary file
* @throws FileNotFoundException
* if the file can not be found
*/
private TemporaryInputStream(File tempFile) throws FileNotFoundException {
super(new FileInputStream(tempFile));
}
/**
* {@inheritDoc}
*/
@Override
public void close() throws IOException {
super.close();
synchronized (fileCounts) {
File tempFile = streamFiles.remove(in);
if (tempFile != null) {
if (fileCounts.get(tempFile) > 0) {
fileCounts.put(tempFile, fileCounts.get(tempFile) - 1);
} else {
fileCounts.remove(tempFile);
tempFile.delete();
}
}
}
}
/**
* Creates a new input stream from the temporary file that is backing this
* input stream. If the file has already been removed, this method will
* throw an exception.
*
* @return A new input stream
* @throws IOException
* if an I/O error occurs
*/
public InputStream reopen() throws IOException {
synchronized (fileCounts) {
File tempFile = streamFiles.get(in);
if (tempFile != null) {
fileCounts.put(tempFile, fileCounts.get(tempFile) + 1);
return new TemporaryInputStream(tempFile);
}
throw new FileNotFoundException("Temporary file has already disappeared.");
}
}
/**
* Creates a temporary file, copies the given input stream to the temporary
* file, and creates an input stream reading from the temporary file. The
* returned input stream will delete the temporary file when its
* {@link #close()} method is called.
*
* @param sourceInputStream
* The input stream to copy
* @return The copied input stream, ready for consumption
* @throws IOException
*/
private static InputStream createFileInputStream(InputStream sourceInputStream) throws IOException {
File tempFile = File.createTempFile("utils-temp-", ".tmp");
tempFile.deleteOnExit();
FileOutputStream fileOutputStream = null;
try {
fileOutputStream = new FileOutputStream(tempFile);
StreamCopier.copy(sourceInputStream, fileOutputStream);
} catch (IOException ioe1) {
throw ioe1;
} finally {
Closer.close(fileOutputStream);
}
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream(tempFile);
streamFiles.put(fileInputStream, tempFile);
synchronized (fileCounts) {
fileCounts.put(tempFile, 0);
}
return fileInputStream;
} catch (IOException ioe1) {
Closer.close(fileInputStream);
tempFile.delete();
throw ioe1;
}
}
}