/**
*
*/
package edu.washington.cs.oneswarm.f2f.xml;
import java.beans.XMLEncoder;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.Arrays;
import java.util.Comparator;
import java.util.concurrent.Semaphore;
import java.util.logging.Logger;
import org.gudy.azureus2.core3.util.Debug;
import org.gudy.azureus2.core3.util.FileUtil;
import edu.washington.cs.oneswarm.f2f.friends.FriendManager;
public class OSF2FXMLBeanWriter<T> implements Runnable {
private final static Logger logger = Logger.getLogger(OSF2FXMLBeanWriter.class.getName());
private final static int NUM_BACKUP_FILES_TO_KEEP = 10;
private final static int KEEP_EVERY_X_BACKUP_FILE = 20;
private static final File OSF2F_DIR = FriendManager.OSF2F_DIR;
private boolean allowDecreasedSize;
private final String filename;
private final Semaphore lock;
private final boolean makeBackup;
private final T[] objects;
public OSF2FXMLBeanWriter(T[] objects, Semaphore lock, String filename, boolean makeBackup,
boolean allowDecreasedSize) {
this.makeBackup = makeBackup;
this.allowDecreasedSize = allowDecreasedSize;
this.objects = objects;
this.lock = lock;
this.filename = filename;
}
public void run() {
try {
if (!OSF2F_DIR.isDirectory()) {
boolean success = OSF2F_DIR.mkdirs();
if (!success) {
System.err.println("ERROR: failed to create dir: "
+ OSF2F_DIR.getAbsolutePath());
}
}
logger.finest("waiting for semaphore");
lock.acquire();
logger.finest("got semaphore");
File f = File.createTempFile("osf2f_", filename);
f.deleteOnExit();
// Serialize object into XML
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(f));
XMLEncoder encoder = new XMLEncoder(out);
encoder.writeObject(objects);
encoder.close();
out.close();
logger.finer("wrote " + objects.length + " objects to disk: " + f.getAbsolutePath());
File oldFile = new File(OSF2F_DIR, filename);
if (oldFile.isFile() && makeBackup) {
cleanBackupFiles(OSF2F_DIR);
java.text.DecimalFormat nft = new java.text.DecimalFormat("#0000.###");
nft.setDecimalSeparatorAlwaysShown(false);
int previousBackupFile = getLargestId(OSF2F_DIR);
File backupFile = new File(oldFile.getCanonicalPath() + ".backup"
+ nft.format(previousBackupFile + 1));
logger.finest("moving: " + oldFile.getAbsolutePath() + " to "
+ backupFile.getAbsolutePath());
renameOrCopy(oldFile, backupFile);
oldFile = new File(OSF2F_DIR, filename);
}
if (System.getProperty("oneswarm.experimental.config.file") != null) {
allowDecreasedSize = true;
}
// check if we wrote everything out completely or if an error
// occurred
if (oldFile.length() < f.length() + 1000 || allowDecreasedSize) {
logger.finest("moving: " + f.getAbsolutePath() + " to " + oldFile.getAbsolutePath());
renameOrCopy(f, oldFile);
} else {
(new Exception()).printStackTrace();
System.err
.println("the new version of the friends file is significantly smaller than the old version. Not overwriting. Old: "
+ oldFile.length() + " New: " + f.length());
}
} catch (IOException e) {
Debug.out("unable to write friend file to disk", e);
}
catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
lock.release();
}
}
private void cleanBackupFiles(File folder) {
String[] backupFiles = getBackupFilesList(folder);
for (String f : backupFiles) {
logger.finest(f);
}
logger.finest("deleting the oldest ones, keeping " + NUM_BACKUP_FILES_TO_KEEP);
for (int i = 0; i < backupFiles.length - NUM_BACKUP_FILES_TO_KEEP; i++) {
String file = backupFiles[i];
if (!(getIdOfBackupFile(file) % KEEP_EVERY_X_BACKUP_FILE == 0)) {
new File(folder, file).delete();
logger.finer("deleting: " + file);
}
}
}
private String[] getBackupFilesList(File folder) {
String[] backupFiles = folder.list(new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.startsWith(filename + ".backup");
}
});
sortBackupFilesById(backupFiles);
return backupFiles;
}
private int getIdOfBackupFile(String fn) {
try {
return Integer.parseInt(fn.replace(filename + ".backup", ""));
} catch (NumberFormatException e) {
logger.fine("got number format exception for: " + fn);
}
return -1;
}
private int getLargestId(File folder) {
String[] filenames = getBackupFilesList(folder);
if (filenames.length == 0) {
return 0;
}
String filename = filenames[filenames.length - 1];
return getIdOfBackupFile(filename);
}
/*
* for testing friend file backups/deletes
*/
public static void main(String[] args) {
try {
File friendsDir = new File("/tmp/friendsTest/");
friendsDir.mkdirs();
for (int i = 9000; i < 10100; i++) {
java.text.DecimalFormat nft = new java.text.DecimalFormat("#0000.###");
nft.setDecimalSeparatorAlwaysShown(false);
File backupFile = new File(friendsDir, "osf2f.friends.backup" + nft.format(i));
backupFile.createNewFile();
}
// logger.finest("largest id: " + getLargestId(friendsDir));
// cleanBackupFiles(friendsDir);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private static void renameOrCopy(File toRenameOrCopy, File dst) throws IOException {
if (toRenameOrCopy.renameTo(dst) == false) {
if (FileUtil.copyFile(toRenameOrCopy, dst) == false) {
throw new IOException("couldn't rename or copy: " + toRenameOrCopy.toString()
+ " to " + dst.toString());
}
toRenameOrCopy.delete();
}
}
private void sortBackupFilesById(String[] filenames) {
Arrays.sort(filenames, new Comparator<String>() {
public int compare(String o1, String o2) {
return getIdOf(o1) - getIdOf(o2);
}
private int getIdOf(String fn) {
try {
int id = Integer.parseInt(fn.replace(filename + ".backup", ""));
return id;
} catch (NumberFormatException e) {
logger.fine("got number format exception for: " + fn);
}
return -1;
}
});
}
}