fr.dyade.aaa.util.ATransaction Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of a3-rt Show documentation
Show all versions of a3-rt Show documentation
Builds the Joram a3 rt project.
/*
* Copyright (C) 2001 - 2012 ScalAgent Distributed Technologies
* Copyright (C) 1996 - 2000 BULL
* Copyright (C) 1996 - 2000 INRIA
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA.
*
* Initial developer(s): ScalAgent Distributed Technologies
* Contributor(s): Alexander Fedorowicz
*/
package fr.dyade.aaa.util;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Enumeration;
import java.util.Hashtable;
import org.objectweb.util.monolog.api.BasicLevel;
public final class ATransaction extends AbstractTransaction implements ATransactionMBean, Runnable {
final static int CLEANUP_THRESHOLD_COMMIT = 9600;
final static int CLEANUP_THRESHOLD_OPERATION = 36000;
final static int CLEANUP_THRESHOLD_SIZE = 8 * Mb;
private int commitCount = 0; // Number of commited transaction in clog.
private int operationCount = 0; // Number of operations reported to clog.
private int cumulativeSize = 0; // Byte amount in clog.
/**
* Log of all operations already commited but not reported on disk
* by the "garbage" Thread. On event (at least previous log plog must
* be empty), it is moved to plog.
*/
private Hashtable clog = null;
/**
* Log currently used by "garbage" Thread, its thread reports all
* operation it contents on disk, then it deletes it.
*/
private Hashtable plog = null;
static private final String LOCK = "lock";
static private final String LOG = "log";
static private final String PLOG = "plog";
private File lockFile = null;
protected File logFilePN = null;
protected File plogFilePN = null;
// State of the garbage.
private boolean garbage;
private Object lock = null;
private boolean isRunning;
private Thread gThread = null;
static final boolean debug = false;
public ATransaction() {}
public boolean isPersistent() {
return true;
}
public final void initRepository() throws IOException {
Operation.initPool(CLEANUP_THRESHOLD_OPERATION);
// Search for log files: plog then clog, reads it, then apply all
// committed operation, finally deletes it.
lockFile = new File(dir, LOCK);
if (! lockFile.createNewFile()) {
logmon.log(BasicLevel.FATAL,
"ATransaction.init(): Either the server is already running, " +
"either you have to remove lock file: " + lockFile.getAbsolutePath());
throw new IOException("Transaction already running.");
}
lockFile.deleteOnExit();
logFilePN = new File(dir, LOG);
plogFilePN = new File(dir, PLOG);
Hashtable tempLog = new Hashtable();
restart(tempLog, logFilePN);
restart(tempLog, plogFilePN);
commit(tempLog);
plogFilePN.delete();
logFilePN.delete();
clog = new Hashtable(CLEANUP_THRESHOLD_OPERATION / 2);
plog = new Hashtable(CLEANUP_THRESHOLD_OPERATION / 2);
baos = new ByteArrayOutputStream(10 * Kb);
dos = new DataOutputStream(baos);
newLogFile();
lock = new Object();
garbage = false;
gThread = new Thread(this, "TGarbage");
gThread.start();
}
private final void restart(Hashtable log, File logFilePN) throws IOException {
if (logmon.isLoggable(BasicLevel.INFO))
logmon.log(BasicLevel.INFO, "ATransaction, restart");
if ((logFilePN.exists()) && (logFilePN.length() > 0)) {
RandomAccessFile logFile = new RandomAccessFile(logFilePN, "r");
try {
Hashtable templog = new Hashtable();
while (true) {
int optype;
String dirName;
String name;
while ((optype = logFile.read()) != Operation.COMMIT) {
// Gets all operations of one committed transaction then
// adds them to specified log.
dirName = logFile.readUTF();
if (dirName.length() == 0) dirName = null;
name = logFile.readUTF();
Object key = OperationKey.newKey(dirName, name);
Operation op = null;
if (optype == Operation.SAVE) {
byte buf[] = new byte[logFile.readInt()];
logFile.readFully(buf);
op = Operation.alloc(optype, dirName, name, buf);
op = (Operation) templog.put(key, op);
} else {
op = Operation.alloc(optype, dirName, name);
op = (Operation) templog.put(key, op);
}
if (op != null) op.free();
}
// During this aggregation somes operation object can be lost due
// to Hashtable collision...
log.putAll(templog);
templog.clear();
}
} catch (EOFException exc) {
logFile.close();
} catch (IOException exc) {
logFile.close();
throw exc;
}
}
if (logmon.isLoggable(BasicLevel.INFO))
logmon.log(BasicLevel.INFO, "ATransaction, started");
}
public final File getDir() {
return dir;
}
protected final void setPhase(int newPhase) {
phase = newPhase;
}
// Be careful: only used in Server
public final String[] getList(String prefix) {
return dir.list(new StartWithFilter(prefix));
}
protected final void saveInLog(byte[] buf,
String dirName, String name,
Hashtable log,
boolean copy,
boolean first) throws IOException {
if (logmon.isLoggable(BasicLevel.DEBUG))
logmon.log(BasicLevel.DEBUG,
"ATransaction, saveInLog(" + dirName + '/' + name + ", " + copy + ", " + first + ")");
Object key = OperationKey.newKey(dirName, name);
Operation op = Operation.alloc(Operation.SAVE, dirName, name, buf);
Operation old = (Operation) log.put(key, op);
if (copy) {
if ((old != null) &&
(old.type == Operation.SAVE) &&
(old.value.length == buf.length)) {
// reuse old buffer
op.value = old.value;
} else {
// alloc a new one
op.value = new byte[buf.length];
}
System.arraycopy(buf, 0, op.value, 0, buf.length);
}
if (old != null) old.free();
}
private final byte[] getFromLog(Hashtable log, Object key) throws IOException {
// Searchs in the log a new value for the object.
Operation op = (Operation) log.get(key);
if (op != null) {
if (op.type == Operation.SAVE) {
return op.value;
} else if (op.type == Operation.DELETE) {
// The object was deleted.
throw new FileNotFoundException();
}
}
return null;
}
private final byte[] getFromLog(String dirName, String name) throws IOException {
// First searchs in the logs a new value for the object.
Object key = OperationKey.newKey(dirName, name);
byte[] buf = getFromLog(perThreadContext.get().getLog(), key);
if (buf != null) return buf;
if (((buf = getFromLog(clog, key)) != null) ||
((buf = getFromLog(plog, key)) != null)) {
return buf;
}
return null;
}
public final byte[] loadByteArray(String dirName, String name) throws IOException {
// First searchs in the logs a new value for the object.
try {
byte[] buf = getFromLog(dirName, name);
if (buf != null) return buf;
// Gets it from disk.
File file;
if (dirName == null) {
file = new File(dir, name);
} else {
File parentDir = new File(dir, dirName);
file = new File(parentDir, name);
}
FileInputStream fis = new FileInputStream(file);
buf = new byte[(int) file.length()];
for (int nb=0; nb log = perThreadContext.get().getLog();
Operation op = Operation.alloc(Operation.DELETE, dirName, name);
op = log.put(key, op);
if (op != null) op.free();
}
static private final byte[] emptyUTFString = {0, 0};
static private ByteArrayOutputStream baos = null;
static private DataOutputStream dos = null;
public void commit(boolean release) throws IOException {
if (phase != RUN)
throw new IllegalStateException("Can not commit.");
if (logmon.isLoggable(BasicLevel.DEBUG))
logmon.log(BasicLevel.DEBUG, "ATransaction, commit");
commitCount += 1; // AF: Monitoring
Hashtable
© 2015 - 2025 Weber Informatics LLC | Privacy Policy