/**
* VMware Continuent Tungsten Replicator
* Copyright (C) 2015 VMware, Inc. All rights reserved.
*
* 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.
*
* Initial developer(s): Robert Hodges
* Contributor(s):
*/
package com.continuent.tungsten.common.io;
import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import org.apache.log4j.Logger;
/**
* Merges the capabilities of the following stream classes into a single class:
* FileOutputStream, BufferedOutputStream, and DataOutputStream. This allows us
* to manage buffered data writes to files efficiently.
*
* @author <a href="mailto:robert.hodges@continuent.com">Robert Hodges</a>
*/
public class BufferedFileDataOutput
{
private static Logger logger = Logger
.getLogger(BufferedFileDataOutput.class);
// Read parameters.
private File file;
private int bufferSize;
// Variables to control writing.
private FileOutputStream fileOutput;
private BufferedOutputStream bufferedOutput;
private DataOutputStream dataOutput;
private long offset = -1;
/**
* Creates instance positioned on end of file and read to write.
*
* @param file File to which to write.
* @param bufferSize Size of buffer for buffered I/O
*/
public BufferedFileDataOutput(File file, int bufferSize)
throws FileNotFoundException, IOException
{
this.file = file;
this.bufferSize = bufferSize;
open();
}
/**
* Creates instance with default buffer size.
*/
public BufferedFileDataOutput(File file) throws FileNotFoundException,
IOException
{
this(file, 4096);
}
/**
* Returns the current offset position.
*
* @throws IOException Thrown if position cannot be determined
*/
public long getOffset() throws IOException
{
return offset;
}
/**
* Open for writes at the tail of the file.
*
* @throws IOException Thrown if position cannot be found
* @throws FileNotFoundException Thrown if file is not found
*/
private void open() throws FileNotFoundException, IOException
{
fileOutput = new FileOutputStream(file, true);
bufferedOutput = new BufferedOutputStream(fileOutput, bufferSize);
dataOutput = new DataOutputStream(bufferedOutput);
fileOutput.getFD().sync();
// NOTE: Channel.position() does not return correct position on some platforms.
offset = fileOutput.getChannel().size();
}
/**
* Writes a single byte.
*/
public void writeByte(byte v) throws IOException
{
dataOutput.writeByte(v);
offset += 1;
}
/**
* Writes a single short.
*/
public void writeShort(short v) throws IOException
{
dataOutput.writeShort(v);
offset += 2;
}
/**
* Writes a single int.
*/
public void writeInt(int v) throws IOException
{
dataOutput.writeInt(v);
offset += 4;
}
/**
* Writes a single long.
*/
public void writeLong(long v) throws IOException
{
dataOutput.writeLong(v);
offset += 8;
}
/**
* Writes a byte array completely.
*
* @throws IOException Thrown if full byte array cannot be written
*/
public void write(byte[] bytes) throws IOException
{
dataOutput.write(bytes);
offset += bytes.length;
}
/**
* Flush buffered data to stream. This does not guarantee persistence, only
* that lower streams can see it.
*
* @throws IOException Thrown if flush fails
*/
public void flush() throws IOException
{
dataOutput.flush();
}
/**
* Synchronizes file contents to disk using fsync. You must call this method
* to commit data. Does an automatic flush.
*/
public void fsync() throws IOException
{
flush();
//fileOutput.getFD().sync();
}
/**
* Truncate the file to the provided length. Performs an automatic fsync and
* reopens the file.
*/
public void setLength(long length) throws IOException
{
FileChannel channel = fileOutput.getChannel();
channel.truncate(length);
fsync();
close();
open();
}
/** Close and release all resources. */
public void close()
{
if (fileOutput != null)
{
try
{
dataOutput.close();
}
catch (IOException e)
{
logger.warn("Unable to close log file writer: file="
+ file.getName() + " exception=" + e.getMessage());
}
fileOutput = null;
bufferedOutput = null;
dataOutput = null;
}
}
/**
* Print contents of the reader.
*/
public String toString()
{
// Try to get the offset.
long offset = -1;
try
{
offset = getOffset();
}
catch (IOException e)
{
}
// Print a string.
StringBuffer sb = new StringBuffer();
sb.append(this.getClass().getSimpleName());
sb.append(" file=").append(file.getName());
sb.append(" buffersize=").append(bufferSize);
sb.append(" offset=").append(offset);
return sb.toString();
}
}