/* $Id: TempFileInput.java 988245 2010-08-23 18:39:35Z kwright $ */
/**
* 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 org.apache.manifoldcf.core.interfaces;
import java.io.*;
import org.apache.manifoldcf.core.system.ManifoldCF;
/** This class represents a temporary file data input
* stream. Call the "done" method to clean up the
* file when done.
* NOTE: The implied flow of this method is to be handled
* a file that has already been created by some means. The
* file must be a dedicated temporary file, which can be
* destroyed when the data has been used.
*/
public class TempFileInput extends BinaryInput
{
public static final String _rcsid = "@(#)$Id: TempFileInput.java 988245 2010-08-23 18:39:35Z kwright $";
protected File file;
protected byte[] inMemoryBuffer;
protected final static int CHUNK_SIZE = 65536;
protected final static int DEFAULT_MAX_MEM_SIZE = 8192;
/** Construct from an input stream.
* This will also create a temporary, backing file.
*@param is is the input stream to use to construct the temporary file.
*/
public TempFileInput(InputStream is)
throws ManifoldCFException, IOException
{
this(is,-1L);
}
/** Construct from a length-delimited input stream.
*@param is is the input stream.
*@param length is the maximum number of bytes to transfer, or -1 if no limit.
*/
public TempFileInput(InputStream is, long length)
throws ManifoldCFException, IOException
{
this(is,length,DEFAULT_MAX_MEM_SIZE);
}
/** Construct from a length-delimited input stream.
*@param is is the input stream.
*@param length is the maximum number of bytes to transfer, or -1 if no limit.
*@param maxMemSize is the maximum bytes we keep in memory in lieu of using a file.
*/
public TempFileInput(InputStream is, long length, int maxMemSize)
throws ManifoldCFException, IOException
{
super();
// Before we do anything else, we read the first chunk. This will allow
// us to determine if we're going to buffer the data in memory or not. However,
// it may need to be read in chunks, since there's no guarantee it will come in
// in the size requested.
int chunkSize = CHUNK_SIZE;
byte[] buffer = new byte[chunkSize];
int chunkTotal = 0;
boolean eofSeen = false;
while (true)
{
int chunkAmount;
if (length == -1L || length > chunkSize)
chunkAmount = chunkSize-chunkTotal;
else
{
chunkAmount = (int)(length-chunkTotal);
eofSeen = true;
}
if (chunkAmount == 0)
break;
int readsize = is.read(buffer,chunkTotal,chunkAmount);
if (readsize == -1)
{
eofSeen = true;
break;
}
chunkTotal += readsize;
}
if (eofSeen && chunkTotal < maxMemSize)
{
// In memory!!
file = null;
inMemoryBuffer = new byte[chunkTotal];
for (int i = 0; i < inMemoryBuffer.length; i++)
{
inMemoryBuffer[i] = buffer[i];
}
this.length = chunkTotal;
}
else
{
inMemoryBuffer = null;
// Create a temporary file to put the stuff in
File outfile;
try
{
outfile = File.createTempFile("_MC_","");
}
catch (IOException e)
{
handleIOException(e,"creating backing file");
outfile = null;
}
try
{
// Register the file for autodeletion, using our infrastructure.
ManifoldCF.addFile(outfile);
// deleteOnExit() causes memory leakage!
// outfile.deleteOnExit();
FileOutputStream outStream;
try
{
outStream = new FileOutputStream(outfile);
}
catch (IOException e)
{
handleIOException(e,"opening backing file");
outStream = null;
}
try
{
long totalMoved = 0;
// Transfor what we've already read.
try
{
outStream.write(buffer,0,chunkTotal);
}
catch (IOException e)
{
handleIOException(e,"writing backing file");
}
totalMoved += chunkTotal;
while (true)
{
int moveAmount;
if (length == -1L || length-totalMoved > chunkSize)
moveAmount = chunkSize;
else
moveAmount = (int)(length-totalMoved);
if (moveAmount == 0)
break;
// Read binary data in 64K chunks
int readsize = is.read(buffer,0,moveAmount);
if (readsize == -1)
break;
try
{
outStream.write(buffer,0,readsize);
}
catch (IOException e)
{
handleIOException(e,"writing backing file");
}
totalMoved += readsize;
}
// System.out.println(" Moved "+Long.toString(totalMoved));
}
finally
{
try
{
outStream.close();
}
catch (IOException e)
{
handleIOException(e,"closing backing file");
}
}
// Now, create the input stream.
// Save the file name
file = outfile;
this.length = file.length();
}
catch (Throwable e)
{
// Delete the temp file we created on any error condition
// outfile.delete();
ManifoldCF.deleteFile(outfile);
if (e instanceof Error)
throw (Error)e;
if (e instanceof RuntimeException)
throw (RuntimeException)e;
if (e instanceof ManifoldCFException)
throw (ManifoldCFException)e;
if (e instanceof IOException)
throw (IOException)e;
throw new RuntimeException("Unexpected throwable of type "+e.getClass().getName()+": "+e.getMessage(),e);
}
}
}
/** Construct from an existing temporary fle.
*@param tempFile is the existing temporary file.
*/
public TempFileInput(File tempFile)
{
super();
inMemoryBuffer = null;
file = tempFile;
ManifoldCF.addFile(file);
// deleteOnExit() causes memory leakage; better to leak files on hard shutdown than memory.
// file.deleteOnExit();
}
protected TempFileInput()
{
super();
}
/** Transfer to a new object; this causes the current object to become "already discarded" */
public BinaryInput transfer()
{
TempFileInput rval = new TempFileInput();
rval.file = file;
rval.inMemoryBuffer = inMemoryBuffer;
rval.stream = stream;
rval.length = length;
file = null;
inMemoryBuffer = null;
stream = null;
length = -1L;
return rval;
}
public void discard()
throws ManifoldCFException
{
super.discard();
if (file != null)
{
ManifoldCF.deleteFile(file);
file = null;
}
}
protected void openStream()
throws ManifoldCFException
{
if (file != null)
{
try
{
// Open the file and create a stream.
stream = new FileInputStream(file);
}
catch (FileNotFoundException e)
{
throw new ManifoldCFException("Can't create stream: "+e.getMessage(),e,ManifoldCFException.GENERAL_ERROR);
}
}
else if (inMemoryBuffer != null)
{
stream = new ByteArrayInputStream(inMemoryBuffer);
}
}
protected void calculateLength()
throws ManifoldCFException
{
if (file != null)
this.length = file.length();
else if (inMemoryBuffer != null)
this.length = inMemoryBuffer.length;
}
}