/**
*
* Copyright 2004 Protique Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
**/
package org.activemq.store.bdbn;
import com.sleepycat.bdb.CurrentTransaction;
import com.sleepycat.db.Db;
import com.sleepycat.db.DbEnv;
import com.sleepycat.db.DbException;
import com.sleepycat.db.DbTxn;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.activemq.util.JMSExceptionHelper;
import javax.jms.JMSException;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.LinkedList;
/**
* Some helper factory methods for creating default configured Berkeley DB objects
*
* @version $Revision: 1.1 $
*/
public class BDbHelper {
private static final Log log = LogFactory.getLog(BDbHelper.class);
private static ThreadLocal threadLocalTxn = new ThreadLocal();
public static final int TRANSACTION_FLAGS = Db.DB_TXN_SYNC;
private static DbEnv cachedEnvironment;
public static DbEnv createEnvironment(File dir, boolean runRecovery) throws DbException, FileNotFoundException {
DbEnv env = new DbEnv(0);
// Open the Berkeley DB environment in transactional mode.
int envFlags = Db.DB_INIT_TXN | Db.DB_INIT_LOCK | Db.DB_INIT_LOG | Db.DB_INIT_MPOOL |
Db.DB_CREATE;
if (runRecovery) {
envFlags |= Db.DB_RECOVER;
}
env.open(dir.getPath(), envFlags, 0);
return env;
}
public static Db open(DbEnv environment, String name, boolean isQueue) throws FileNotFoundException, DbException, JMSException {
int flags = Db.DB_CREATE; // | Db.DB_AUTO_COMMIT
Db db = new Db(environment, 0);
if (isQueue) {
db.setFlags(Db.DB_RENUMBER);
}
// only use Db.DB_RECNUM for list tables as its a performance hog
int type = Db.DB_BTREE;
if (isQueue) {
type = Db.DB_RECNO;
}
String databaseName = null;
DbTxn transaction = createTransaction(environment);
try {
db.open(transaction, name, databaseName, type, flags, 0);
transaction = commitTransaction(transaction);
}
finally {
rollbackTransaction(transaction);
}
return db;
}
/**
* @return the current thread local transaction that is in progress or null if there is no
* transaction in progress
*/
public static DbTxn getTransaction() {
LinkedList list = (LinkedList) threadLocalTxn.get();
if (list != null && !list.isEmpty()) {
return (DbTxn) list.getFirst();
}
return null;
}
/**
* Pops off the current transaction from the stack
*/
public static DbTxn popTransaction() {
LinkedList list = (LinkedList) threadLocalTxn.get();
if (list == null || list.isEmpty()) {
log.warn("Attempt to pop transaction when no transaction in progress");
return null;
}
else {
return (DbTxn) list.removeFirst();
}
}
/**
* Sets the current transaction, possibly including nesting
*/
public static void pushTransaction(DbTxn transaction) {
LinkedList list = (LinkedList) threadLocalTxn.get();
if (list == null) {
list = new LinkedList();
threadLocalTxn.set(list);
}
list.addLast(transaction);
}
public static int getTransactionCount() {
LinkedList list = (LinkedList) threadLocalTxn.get();
if (list != null) {
return list.size();
}
return 0;
}
public static DbTxn createTransaction(DbEnv environment) throws DbException {
// TODO remove dirty hack!
cachedEnvironment = environment;
CurrentTransaction currentTxn = CurrentTransaction.getInstance(environment);
return currentTxn.beginTxn();
/**
// TODO temporary hack until BDB supports nested transactions
if (getTransactionCount() == 0) {
DbTxn transaction = environment.txnBegin(getTransaction(), TRANSACTION_FLAGS);
pushTransaction(transaction);
return transaction;
}
else {
DbTxn transaction = getTransaction();
pushTransaction(transaction);
return transaction;
}
*/
}
/**
* Commit a transaction, throwing a JMSException if a failure occurs to avoid TRA
* rolling back
*
* @param transaction
* @throws javax.jms.JMSException if the transaction could not be committed
*/
public static DbTxn commitTransaction(DbTxn transaction) throws JMSException {
try {
CurrentTransaction currentTxn = CurrentTransaction.getInstance(cachedEnvironment);
currentTxn.commitTxn();
return null;
}
catch (DbException e) {
throw JMSExceptionHelper.newJMSException("Failed to commit transaction: " + transaction + " in container: " + e, e);
}
/*
// TODO temporary hack until BDB supports nested transactions
if (getTransactionCount() == 1) {
try {
transaction.commit(TRANSACTION_FLAGS);
return null;
}
catch (DbException e) {
throw JMSExceptionHelper.newJMSException("Failed to commit transaction: " + transaction + " in container: " + e, e);
}
finally {
popTransaction();
}
}
else {
popTransaction();
return null;
}
*/
}
/**
* Rolls back the transaction, catching all exceptions as we only rollback
* if we are about to throw an exception anyways
*
* @param transaction
*/
public static void rollbackTransaction(DbTxn transaction) {
if (transaction != null) {
try {
CurrentTransaction currentTxn = CurrentTransaction.getInstance(cachedEnvironment);
currentTxn.abortTxn();
}
catch (DbException e) {
log.warn("Cannot rollback transaction due to: " + e, e);
}
}
/*
// TODO temporary hack until BDB supports nested transactions
if (transaction != null) {
if (getTransactionCount() == 1) {
try {
transaction.abort();
}
catch (DbException e) {
log.warn("Cannot rollback transaction due to: " + e, e);
}
finally {
popTransaction();
}
}
else {
popTransaction();
}
}
*/
}
}