
fr.dyade.aaa.ext.NGTransaction Maven / Gradle / Ivy
Show all versions of a3-rt Show documentation
/*
* Copyright (C) 2009 - 2014 ScalAgent Distributed Technologies
*
* 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):
*/
package fr.dyade.aaa.ext;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Set;
import java.util.Map.Entry;
import org.objectweb.util.monolog.api.BasicLevel;
import fr.dyade.aaa.common.Debug;
import fr.dyade.aaa.util.AbstractTransaction;
import fr.dyade.aaa.util.Operation;
import fr.dyade.aaa.util.OperationKey;
import fr.dyade.aaa.util.Repository;
import fr.dyade.aaa.util.StartWithFilter;
import fr.dyade.aaa.util.Transaction;
/**
* The NGTransaction class implements a transactional storage.
* For efficiency it uses multiples files for its transaction journal, the final
* storage is provided through the Repository interface on filesystem or database.
*
* Be Careful, the configuration properties don't work for the transaction component:
* these properties are saved in the transaction repository so they can not be used to
* configure it.
*
* @see Transaction
* @see Repository
* @see FileRepository
* @see DBRepository
* @see MySqlDBRepository
*/
public final class NGTransaction extends AbstractTransaction implements NGTransactionMBean {
/**
* Global in memory log initial capacity, by default 4096.
* This value can be adjusted for a particular server by setting
* Transaction.LogMemoryCapacity
specific property.
*
* This property can be set only at first launching.
*/
static int LogMemoryCapacity = 4096;
/**
* Returns the initial capacity of global in memory log (by default 4096).
*
* @return The initial capacity of global in memory log.
*/
public final int getLogMemoryCapacity() {
return LogMemoryCapacity;
}
/**
* Returns the number of operation in the memory log.
*
* @return The number of operation in the memory log.
*/
public int getLogMemorySize() {
return logManager.log.size();
}
/**
* Maximum size of disk log in Mb, by default 16Mb.
* This value can be adjusted (Mb) for a particular server by setting
* Transaction.MaxLogFileSize
specific property.
*
* This property can be set only at first launching.
*/
static int MaxLogFileSize = 16 * Mb;
/**
* Returns the maximum size of disk log in Mb, by default 16Mb.
*
* @return The maximum size of disk log in Mb.
*/
public final int getMaxLogFileSize() {
return MaxLogFileSize/Mb;
}
/**
* Sets the maximum size of disk log in Mb.
*
* @param size The maximum size of disk log in Mb.
*/
public final void setMaxLogFileSize(int size) {
if (size > 0) MaxLogFileSize = size *Mb;
}
/**
* Returns the current size of disk log in Kb.
*
* @return The size of disk log in Kb.
*/
public final int getLogFileSize() {
return (logManager.getLogFileSize() /Kb);
}
/**
* Maximum number of disk log used by the Transaction component, by
* default 4.
* This value can be adjusted for a particular server by setting
* Transaction.NbLogFile
specific property.
*
* This property can be set only at first launching.
*/
static int nbLogFile = 4;
/**
* Returns the number of rolled log files.
*
* @return The number of rolled log files.
*/
public final int getNbLogFiles() {
return nbLogFile;
}
/**
* Minimum number of 'live' objects in a disk log before a garbage, by
* default 64.
* This value can be adjusted for a particular server by setting
* Transaction.minObjInLog
specific property.
*
* This property can be set only at first launching.
*/
static int minObjInLog = 64;
/**
* If true every write in the log file is synced to disk, by default
* false. This value can be adjusted for a particular server by setting
* Transaction.SyncOnWrite
specific property.
*
* This property can be set only at first launching.
*/
boolean syncOnWrite = false;
/**
* @return the syncOnWrite value.
*/
public boolean isSyncOnWrite() {
return syncOnWrite;
}
/**
* If true (default) use a lock file to avoid multiples activation of Transaction
* component. This value can be adjusted for a particular server by setting
* Transaction.UseLockFile
specific property.
*
* This property can be set only at first launching.
*/
boolean useLockFile = true;
/**
* Number of pooled operation, by default 1000.
* This value can be adjusted for a particular server by setting
* Transaction.LogThresholdOperation
specific property.
*
* This property can be set only at first launching.
*/
int LogThresholdOperation = 1000;
/**
* Returns the pool size for operation
objects, by default 1000.
*
* @return The pool size for operation
objects.
*/
public final int getLogThresholdOperation() {
return LogThresholdOperation;
}
/**
* Returns the number of commit operation since starting up.
*
* @return The number of commit operation.
*/
public final int getCommitCount() {
return logManager.commitCount;
}
/**
* Returns the number of garbage operation since starting up.
*
* @return The number of garbage operation.
*/
public final int getGarbageCount() {
return logManager.garbageCount;
}
/**
* Returns the cumulated time of garbage operations since starting up.
*
* @return The cumulated time of garbage operations since starting up.
*/
public long getGarbageTime() {
return logManager.garbageTime;
}
/**
* Returns the number of load operation from a log file since last start.
*
* @return The number of load operation from a log file.
*/
public int getNbLoadedFromLog() {
return logManager.loadFromLog;
}
/**
* Returns the ratio of garbage operations since starting up.
*
* @return The ratio of garbage operations since starting up.
*/
public int getGarbageRatio() {
return (int) ((logManager.garbageTime *100) / (System.currentTimeMillis() - startTime));
}
public void resetGarbageRatio() {
logManager.garbageTime = 0L;
startTime = System.currentTimeMillis();
}
/**
* The Repository classname implementation.
* This value can be set for a particular server by setting the
* Transaction.RepositoryImpl
specific property. By default its value
* is "fr.dyade.aaa.util.FileRepository".
*
* This property can be set only at first launching.
*/
String repositoryImpl = "fr.dyade.aaa.util.FileRepository";
/**
* Returns the Repository classname implementation.
*
* @return The Repository classname implementation.
*/
public String getRepositoryImpl() {
return repositoryImpl;
}
/**
* Returns the number of save operation to repository.
*
* @return The number of save operation to repository.
*/
public int getNbSavedObjects() {
return repository.getNbSavedObjects();
}
/**
* Returns the number of delete operation on repository.
*
* @return The number of delete operation on repository.
*/
public int getNbDeletedObjects() {
return repository.getNbDeletedObjects();
}
/**
* Returns the number of useless delete operation on repository.
*
* @return The number of useless delete operation on repository.
*/
public int getNbBadDeletedObjects() {
return repository.getNbBadDeletedObjects();
}
/**
* Returns the number of load operation from repository.
*
* @return The number of load operation from repository.
*/
public int getNbLoadedObjects() {
return repository.getNbLoadedObjects();
}
LogManager logManager = null;
Repository repository = null;
public NGTransaction() {}
public final void initRepository() throws IOException {
LogMemoryCapacity = getInteger("Transaction.LogMemoryCapacity", LogMemoryCapacity).intValue();
MaxLogFileSize = getInteger("Transaction.MaxLogFileSize", MaxLogFileSize / Mb).intValue() * Mb;
nbLogFile = getInteger("Transaction.NbLogFile", nbLogFile).intValue();
minObjInLog = getInteger("Transaction.minObjInLog", minObjInLog).intValue();
LogThresholdOperation = getInteger("Transaction.LogThresholdOperation", LogThresholdOperation).intValue();
Operation.initPool(LogThresholdOperation);
syncOnWrite = getBoolean("Transaction.SyncOnWrite");
try {
repositoryImpl = getProperty("Transaction.RepositoryImpl", repositoryImpl);
repository = (Repository) Class.forName(repositoryImpl).newInstance();
repository.init(this, dir);
} catch (ClassNotFoundException exc) {
logmon.log(BasicLevel.FATAL,
"NTransaction, cannot initializes the repository ", exc);
throw new IOException(exc.getMessage());
} catch (InstantiationException exc) {
logmon.log(BasicLevel.FATAL,
"NTransaction, cannot initializes the repository ", exc);
throw new IOException(exc.getMessage());
} catch (IllegalAccessException exc) {
logmon.log(BasicLevel.FATAL,
"NTransaction, cannot initializes the repository ", exc);
throw new IOException(exc.getMessage());
}
useLockFile = getBoolean("Transaction.UseLockFile");
logManager = new LogManager(dir, repository, useLockFile, syncOnWrite);
}
/**
* Tests if the Transaction component is persistent.
*
* @return true.
*/
public boolean isPersistent() {
return true;
}
/**
* Returns the path of persistence directory.
*
* @return The path of persistence directory.
*/
public String getPersistenceDir() {
return dir.getPath();
}
protected final void setPhase(int newPhase) {
phase = newPhase;
}
/**
* Returns an array of strings naming the persistent objects denoted by
* a name that satisfy the specified prefix. Each string is an object name.
*
* @param prefix the prefix
* @return An array of strings naming the persistent objects
* denoted by a name that satisfy the specified prefix. The
* array will be empty if no names match.
*/
public synchronized String[] getList(String prefix) {
return logManager.getList(prefix);
}
/**
* Save an object state already serialized. The byte array in parameter
* may be modified so we must duplicate it.
*/
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,
"NGTransaction, saveInLog(" + dirName + '/' + name + ", " + copy + ", " + first + ")");
Object key = OperationKey.newKey(dirName, name);
Operation op = null;
if (first)
op = Operation.alloc(Operation.CREATE, dirName, name, buf);
else
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 {
// Searches in the log a new value for the object.
Operation op = (Operation) log.get(key);
if (op != null) {
if ((op.type == Operation.SAVE) || (op.type == Operation.CREATE)) {
return op.value;
} else if (op.type == Operation.DELETE) {
// The object was deleted.
throw new FileNotFoundException();
}
}
return null;
}
private final synchronized byte[] getFromLog(String dirName, String name) throws IOException {
// First searches in the current transaction log a new value for the object.
Object key = OperationKey.newKey(dirName, name);
byte[] buf = getFromLog(perThreadContext.get().getLog(), key);
if (buf != null) return buf;
// Then search in the log files and repository.
return logManager.load(dirName, name);
}
public byte[] loadByteArray(String dirName, String name) throws IOException {
if (logmon.isLoggable(BasicLevel.DEBUG))
logmon.log(BasicLevel.DEBUG,
"NTransaction, loadByteArray(" + dirName + '/' + name + ")");
// First searches in the logs a new value for the object.
try {
return getFromLog(dirName, name);
} catch (FileNotFoundException exc) {
if (logmon.isLoggable(BasicLevel.DEBUG))
logmon.log(BasicLevel.DEBUG,
"NTransaction, loadByteArray(" + dirName + '/' + name + ") not found");
return null;
}
}
public final void delete(String dirName, String name) {
if (logmon.isLoggable(BasicLevel.DEBUG))
logmon.log(BasicLevel.DEBUG,
"NTransaction, delete(" + dirName + ", " + name + ")");
Object key = OperationKey.newKey(dirName, name);
Hashtable