package com.onionnetworks.io;
//import org.apache.log4j.Category;
import java.io.*;
// Implement Filtering.
public class RAF {
// static Category cat = Category.getInstance(RAF.class.getName());
protected File f;
protected String mode;
protected RandomAccessFile raf;
private boolean closed;
protected boolean deleteOnClose;
public RAF(File f, String mode) throws IOException {
this.f = f;
this.mode = mode;
this.raf = new RandomAccessFile(f,mode);
}
/**
* This is only to be used by subclasses requiring more flexible
* constructors.
*/
protected RAF() {}
public File getFile() {
return f;
}
public synchronized void seekAndWrite(long pos, byte[] b, int off,
int len) throws IOException {
raf.seek(pos);
raf.write(b,off,len);
}
public synchronized int seekAndRead(long pos, byte[] b, int off, int len)
throws IOException {
raf.seek(pos);
return raf.read(b,off,len);
}
public synchronized void seekAndReadFully(long pos, byte[] b, int off,
int len) throws IOException {
raf.seek(pos);
raf.readFully(b,off,len);
}
/**
* This version of renameTo() attempts to mimic the behavior of the unix
* 'mv' command rather than File.renameTo. This means that the destFile
* will automatically be deleted if it exists and if we are unable to
* mv the file directly because they are on different file systems then
* we will manually copy and delete the original.
*
* If there is an exception during the copy then we will attempt to
* delete the new copy and revert back to the old copy. There is a very
* slight chance that after reverting, the original copy may be unusable.
* This would probably only happen with a file system that is
* experiencing IO problems, in which case you are going to have problems
* anyway.
*
* There is also a very rare chance that during a failed copy, we will
* be unable to delete a partially written destination file and it will
* remain on disk. This could happen on a directory with "drop box"
* semantics where you can only create and write, and not delete.
*/
public synchronized void renameTo(File destFile) throws IOException {
if (closed) {
throw new IOException("File closed.");
}
raf.close();
// Move to final location.
try {
// If they are the same file than do nothing. It is obviously
// important for this to be checked before we delete the destFile.
if (f.getCanonicalFile().equals(destFile.getCanonicalFile())) {
return;
}
// Delete the destFile if it exists.
if (destFile.exists()) {
//cat.debug("renameTo(): destFile exists, deleting...");
if (!destFile.delete()) {
throw new IOException("Unable to delete destination :"+
destFile);
}
}
if (!f.renameTo(destFile)) {
//cat.debug("renameTo(): File.renameTo failed, copying...");
try {
byte[] b = new byte[8192];
InputStream is = new FileInputStream(f);
OutputStream os = new FileOutputStream(destFile);
int c;
while ((c = is.read(b,0,b.length)) != -1) {
os.write(b,0,c);
}
is.close();
os.close();
// If there was a prob with the copy then it should
// have bombed before now.
if (!f.delete()) {
// Even though this is a problem with the source,
// we still delete the destination and keep the source
// in the catch {} clause because we want this method
// to fall back to the source under any failure
// condition.
throw new IOException("Unable to delete source "+
"post-move");
}
} catch (IOException e) {
if (destFile.exists() && !destFile.delete()) {
throw new IOException("Unable to delete destination "+
"after failed move : "+destFile+
" : "+e.getMessage());
}
throw new IOException("Unable to move "+f+" to "+
destFile+" : "+e.getMessage());
}
f = destFile;
} else {
f = destFile;
}
} finally {
// If exception is thrown, re-open the old one.
raf = new RandomAccessFile(f,mode);
}
}
public synchronized String getMode() {
return mode;
}
public synchronized boolean isClosed() {
return closed;
}
public synchronized void setReadOnly() throws IOException {
if (closed) {
throw new IOException("File closed.");
}
this.mode = "r";
raf.close();
raf = new RandomAccessFile(f,mode);
}
public synchronized void deleteOnClose() {
if (closed) {
throw new IllegalStateException("File already closed");
}
deleteOnClose = true;
}
public synchronized void setLength(long len) throws IOException {
raf.setLength(len);
}
public synchronized long length() throws IOException {
return raf.length();
}
public synchronized void close() throws IOException {
closed = true;
raf.close();
if (deleteOnClose) {
if (!f.delete()) {
throw new IOException("Unable to delete file on close");
}
}
}
/**
* Cleans up this objects resources by calling close(). This will
* also cause the file to be deleted if deleteOnClose() was called.
*
* @see close()
*/
protected void finalize() throws IOException {
if (!closed) {
close();
}
}
public String toString() {
return "RAF[file="+f.getAbsolutePath()+",mode="+mode+"]";
}
}