lockFile = new File(dir, LockPathname);
if (! lockFile.createNewFile()) {
logmon.log(BasicLevel.FATAL,
"NTransaction.init(): Either the server is already running, " +
"either you have to remove lock file " + lockFile.getAbsolutePath());
throw new IOException("Transaction already running.");
}
lockFile.deleteOnExit();
}
if (syncOnWrite)
mode = "rwd";
else
mode = "rw";
log = new Hashtable(LogMemoryCapacity);
long start = System.currentTimeMillis();
logidx = -1;
logFile = new LogFile[nbLogFile];
this.dir = dir ;
String[] list = dir.list(new StartWithFilter("log#"));
if (list == null) {
throw new IOException("NGTransaction error opening " + dir.getAbsolutePath());
} else if (list.length == 0) {
logidx = 0;
} else {
// Recovery of log files..
Arrays.sort(list);
for (int i=0; i<list.length; i++) {
logmon.log(BasicLevel.WARN, "NGTransaction.LogManager, rebuilds index: " + list[i]);
int idx = Integer.parseInt(list[i].substring(4));
// Fix the log index to the lower index, it is needed if all log files
// are garbaged.
if (logidx == -1) logidx = idx;
try {
LogFile logf = new LogFile(dir, idx, mode);
int optype = logf.read();
if (optype == Operation.END) {
// The log is empty
logf.close();
continue;
}
// The index of current log is the bigger index of log with 'live' operations.
logidx = idx;
logFile[logidx%nbLogFile] = logf;
// current if fixed after the log reading
while (optype == Operation.COMMIT) {
String dirName;
String name;
optype = logFile[logidx%nbLogFile].read();
while ((optype == Operation.CREATE) ||
(optype == Operation.SAVE) ||
(optype == Operation.DELETE)) {
int ptr = (int) logFile[logidx%nbLogFile].getFilePointer() -1;
logFile[logidx%nbLogFile].logCounter += 1;
// Gets all operations of one committed transaction then
// adds them to specified log.
dirName = logFile[logidx%nbLogFile].readUTF();
if (dirName.length() == 0) dirName = null;
name = logFile[logidx%nbLogFile].readUTF();
Object key = OperationKey.newKey(dirName, name);
byte buf[] = null;
if ((optype == Operation.SAVE) || (optype == Operation.CREATE)) {
buf = new byte[logFile[logidx%nbLogFile].readInt()];
logFile[logidx%nbLogFile].readFully(buf);
// logFile[logidx%nbLogFile].skipBytes(logFile[logidx%nbLogFile].readInt());
}
if (Debug.debug && logmon.isLoggable(BasicLevel.DEBUG))
logmon.log(BasicLevel.DEBUG,
"NGTransaction.LogManager, OPERATION=" + optype + ", " + name + " buf=" + Arrays.toString(buf));
Operation old = log.get(key);
if (old != null) {
logFile[old.logidx%nbLogFile].logCounter -= 1;
// There is 6 different cases:
//
// new |
// old | C | S | D
// ------+-----+-----+-----+
// C | C | C | NOP
// ------+-----+-----+-----+
// S | S | S | D
// ------+-----+-----+-----+
// D | S | S | D
//
if ((old.type == Operation.CREATE) || (old.type == Operation.SAVE)) {
if ((optype == Operation.CREATE) || (optype == Operation.SAVE)) {
// The resulting operation is still the same, just change the logidx
// and logptr informations.
old.logidx = logidx;
old.logptr = ptr;
} else {
// The operation is a delete
if (old.type == Operation.CREATE) {
// There is no need to memorize the deletion, the object will be never
// created on disk.
old.type = Operation.NOOP;
log.remove(key);
old.free();
logFile[logidx%nbLogFile].logCounter -= 1;
} else {
// The operation is a save, overload it.
old.type = Operation.DELETE;
old.logidx = logidx;
old.logptr = ptr;
}
}
} else if (old.type == Operation.DELETE) {
if ((optype == Operation.CREATE) || (optype == Operation.SAVE)) {
// The resulting operation is a save
old.type = Operation.SAVE;
}
old.logidx = logidx;
old.logptr = ptr;
}
} else {
Operation op = Operation.alloc(optype, dirName, name);
op.logidx = logidx;
op.logptr = ptr;
log.put(key, op);
}
optype = logFile[logidx%nbLogFile].read();
}
if (Debug.debug && logmon.isLoggable(BasicLevel.DEBUG))
logmon.log(BasicLevel.DEBUG, "NGTransaction.LogManager, COMMIT#" + idx);
}
current = (int) logFile[logidx%nbLogFile].getFilePointer();
if (Debug.debug && logmon.isLoggable(BasicLevel.DEBUG))
logmon.log(BasicLevel.DEBUG, "NGTransaction.LogManager, END#" + logidx);
if (optype != Operation.END)
throw new IOException("Corrupted transaction log#" + logidx);
} catch (IOException exc) {
throw exc;
}
}