
com.mindoo.domino.jna.transactions.Transactions Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of domino-jna Show documentation
Show all versions of domino-jna Show documentation
Java project to access the HCL Domino C API using Java Native Access (JNA)
package com.mindoo.domino.jna.transactions;
import com.mindoo.domino.jna.NotesDatabase;
import com.mindoo.domino.jna.errors.NotesError;
import com.mindoo.domino.jna.errors.NotesErrorUtils;
import com.mindoo.domino.jna.internal.NotesNativeAPI32;
import com.mindoo.domino.jna.internal.NotesNativeAPI64;
import com.mindoo.domino.jna.internal.NotesConstants;
import com.mindoo.domino.jna.utils.PlatformUtils;
/**
* Utility class that gives access to NSF transactions. NSF transactions cover
* normal document operations like add/update/delete, but in R9 they have some
* caveats like folder operations (e.g. moving docs between folders) that may not
* be covered properly according to IBM dev. Transactions currently only work
* in local databases.
*
* The concept behind NSF transactions is called nested top actions. In short, you can
* open nested transactions in your running transaction, which can be committed, although
* the surrounding transaction may be rolled back.
*
* Here is a short description of the concept:
* Nested Top Actions.
*
* Running code via {@link #runInDbTransaction(NotesDatabase, ITransactionCallable)}
* does not automatically lock out all other threads.
* Instead, only an Intend Shared lock is allocated, all users
* can still read and write. Blocking other threads from writing starts when the
* first write operations is being made (e.g. via NSFNoteUpdate). In addition, you
* should not take too much time for your transaction operation, because it gets
* aborted after a certain amount of time and when many other threads are waiting in the queue.
*
* @author Karsten Lehmann
*/
public class Transactions {
private static final ThreadLocal activeTransactionDepthForCurrentThread = new ThreadLocal() {
protected Integer initialValue() {
return 0;
};
};
private static Boolean transactionsAvailable;
/**
* Method to check whether transactions are available for the specified database.
*
* @param anyLocalDb any database
* @return true if available
*/
public static boolean areTransactionsSupported(NotesDatabase anyLocalDb) {
if (transactionsAvailable==null) {
if (PlatformUtils.is64Bit()) {
short result = NotesNativeAPI64.get().NSFTransactionBegin(anyLocalDb.getHandle64(), NotesConstants.NSF_TRANSACTION_BEGIN_SUB_COMMIT);
if (result==0) {
transactionsAvailable = true;
NotesNativeAPI64.get().NSFTransactionRollback(anyLocalDb.getHandle64());
}
else {
transactionsAvailable = false;
}
}
else {
short result = NotesNativeAPI32.get().NSFTransactionBegin(anyLocalDb.getHandle32(), NotesConstants.NSF_TRANSACTION_BEGIN_SUB_COMMIT);
if (result==0) {
transactionsAvailable = true;
NotesNativeAPI32.get().NSFTransactionRollback(anyLocalDb.getHandle32());
}
else {
transactionsAvailable = false;
}
}
}
return transactionsAvailable.booleanValue();
}
/**
* Method to check if the current thread is running in a transaction ({@link #getTransactionLevelForCurrentThread()}
* is 1 or higher).
*
* @return true if in transaction
*/
public static boolean isTransactionActiveForCurrentThread() {
return activeTransactionDepthForCurrentThread.get() > 0;
}
/**
* Method to read the current transaction depth. If the returned value is 0, the code is not running
* in a transaction, for 1 we are running in a main transaction, for higher values the code is
* running in a nested transaction.
*
* @return level
*/
public static int getTransactionLevelForCurrentThread() {
return activeTransactionDepthForCurrentThread.get();
}
/**
* Executes a {@link ITransactionCallable} atomically in a database. If
* {@link #getTransactionLevelForCurrentThread()} is 0, the method is
* waiting for an exclusive lock on the database, for 1 or higher it
* opens a sub transaction. The transaction is automatically committed
* after execution of the {@link ITransactionCallable} if no exception
* is thrown.
*
* @param db database
* @param callable callable
* @return optional return value
* @param return value type
*/
public static T runInDbTransaction(NotesDatabase db, ITransactionCallable callable) throws RollbackException {
if (db.isRecycled()) {
throw new NotesError(0, "Database is already recycled");
}
int transactionLevel = activeTransactionDepthForCurrentThread.get().intValue();
int flags;
if (transactionLevel==0) {
flags = NotesConstants.NSF_TRANSACTION_BEGIN_LOCK_DB;
}
else {
flags = NotesConstants.NSF_TRANSACTION_BEGIN_SUB_COMMIT;
}
if (PlatformUtils.is64Bit()) {
short result = NotesNativeAPI64.get().NSFTransactionBegin(db.getHandle64(), flags);
NotesErrorUtils.checkResult(result);
}
else {
short result = NotesNativeAPI32.get().NSFTransactionBegin(db.getHandle32(), flags);
NotesErrorUtils.checkResult(result);
}
activeTransactionDepthForCurrentThread.set(transactionLevel+1);
try {
T retValue = callable.runInDbTransaction(db);
if (PlatformUtils.is64Bit()) {
short result = NotesNativeAPI64.get().NSFTransactionCommit(db.getHandle64(), 0);
NotesErrorUtils.checkResult(result);
}
else {
short result = NotesNativeAPI32.get().NSFTransactionCommit(db.getHandle32(), 0);
NotesErrorUtils.checkResult(result);
}
return retValue;
}
catch (Throwable t) {
if (PlatformUtils.is64Bit()) {
short result = NotesNativeAPI64.get().NSFTransactionRollback(db.getHandle64());
NotesErrorUtils.checkResult(result);
}
else {
short result = NotesNativeAPI32.get().NSFTransactionRollback(db.getHandle32());
NotesErrorUtils.checkResult(result);
}
if (t instanceof RollbackException) {
throw (RollbackException) t;
}
else {
throw new RollbackException(t);
}
}
finally {
activeTransactionDepthForCurrentThread.set(transactionLevel);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy