package org.apache.ojb.odmg;
/* Copyright 2002-2005 The Apache Software Foundation
*
* 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.
*/
import org.apache.ojb.broker.OJBRuntimeException;
import org.apache.ojb.broker.util.configuration.Configuration;
import org.apache.ojb.broker.util.logging.Logger;
import org.apache.ojb.broker.util.logging.LoggerFactory;
import org.apache.ojb.broker.transaction.tm.TransactionManagerFactoryException;
import org.apache.ojb.broker.transaction.tm.TransactionManagerFactoryFactory;
import org.apache.commons.lang.SystemUtils;
import org.odmg.TransactionNotInProgressException;
import javax.transaction.Status;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import java.lang.ref.WeakReference;
/**
* @author <a href="mailto:mattbaird@yahoo.com">Matthew Baird</a>
*
* In an app server environment, where we have a transaction manager, we
* use the transactionmanager and it's associated JTA transaction to associate
* with the ODMG transaction. So the key is retrieved by calling getTransaction
* on the transactionManager
*/
public class JTATxManager implements OJBTxManager
{
private Logger log = LoggerFactory.getLogger(JTATxManager.class);
private static ThreadLocal txRepository = new ThreadLocal();
/**
* Remove the ODMG transaction from the transaction buffer
* ODMG transactions are associated with JTA transactions via a map
*/
public void deregisterTx(Object transaction)
{
// TxBuffer buf = (TxBuffer) txRepository.get();
// if (buf != null)
// {
// buf.setInternTx(null);
// }
txRepository.set(null);
}
public void registerTx(TransactionImpl odmgTrans)
{
if (log.isDebugEnabled()) log.debug("registerSynchronization was called");
Transaction transaction = null;
try
{
transaction = getJTATransaction();
}
catch (SystemException e)
{
log.error("Obtain current transaction from container failed", e);
}
if (transaction == null)
{
log.error("Cannot get the external transaction from the external TM");
throw new TransactionNotInProgressException("No external transaction found");
}
if (log.isDebugEnabled())
{
log.debug("registerSynchronization was called with parameters"
+ SystemUtils.LINE_SEPARATOR +"J2EETransactionImpl: " + odmgTrans
+ SystemUtils.LINE_SEPARATOR + "Transaction: " + transaction);
}
registerSynchronization(odmgTrans, transaction);
}
/**
* Do synchronization of the given J2EE ODMG Transaction
*/
private void registerSynchronization(TransactionImpl odmgTrans, Transaction transaction)
{
// todo only need for development
if (odmgTrans == null || transaction == null)
{
log.error("One of the given parameters was null --> cannot do synchronization!" +
" omdg transaction was null: " + (odmgTrans == null) +
", external transaction was null: " + (transaction == null));
return;
}
int status = -1; // default status.
try
{
status = transaction.getStatus();
if (status != Status.STATUS_ACTIVE)
{
throw new OJBRuntimeException(
"Transaction synchronization failed - wrong status of external container tx: " +
getStatusString(status));
}
}
catch (SystemException e)
{
throw new OJBRuntimeException("Can't read status of external tx", e);
}
try
{
//Sequence of the following method calls is significant
// 1. register the synchronization with the ODMG notion of a transaction.
transaction.registerSynchronization((J2EETransactionImpl) odmgTrans);
// 2. mark the ODMG transaction as being in a JTA Transaction
// Associate external transaction with the odmg transaction.
txRepository.set(new TxBuffer(odmgTrans, transaction));
}
catch (Exception e)
{
log.error("Cannot associate PersistenceBroker with running Transaction", e);
throw new OJBRuntimeException(
"Transaction synchronization failed - wrong status of external container tx", e);
}
}
private static String getStatusString(int status)
{
switch (status)
{
case Status.STATUS_ACTIVE:
return "STATUS_ACTIVE";
case Status.STATUS_COMMITTED:
return "STATUS_COMMITTED";
case Status.STATUS_COMMITTING:
return "STATUS_COMMITTING";
case Status.STATUS_MARKED_ROLLBACK:
return "STATUS_MARKED_ROLLBACK";
case Status.STATUS_NO_TRANSACTION:
return "STATUS_NO_TRANSACTION";
case Status.STATUS_PREPARED:
return "STATUS_PREPARED";
case Status.STATUS_PREPARING:
return "STATUS_PREPARING";
case Status.STATUS_ROLLEDBACK:
return "STATUS_ROLLEDBACK";
case Status.STATUS_ROLLING_BACK:
return "STATUS_ROLLING_BACK";
case Status.STATUS_UNKNOWN:
return "STATUS_UNKNOWN";
default:
return "NO STATUS FOUND";
}
}
/**
* Return the TransactionManager of the external app
*/
private TransactionManager getTransactionManager()
{
TransactionManager retval = null;
try
{
if (log.isDebugEnabled()) log.debug("getTransactionManager called");
retval = TransactionManagerFactoryFactory.instance().getTransactionManager();
}
catch (TransactionManagerFactoryException e)
{
log.warn("Exception trying to obtain TransactionManager from Factory", e);
e.printStackTrace();
}
return retval;
}
public Transaction getJTATransaction() throws SystemException
{
if (log.isDebugEnabled()) log.debug("getTransaction called");
if (getTransactionManager() == null)
{
log.warn("TransactionManager was null");
return null;
}
return getTransactionManager().getTransaction();
}
/**
* Returns the current transaction based on the JTA Transaction.
* @throws org.odmg.TransactionNotInProgressException if no transaction was found.
*/
public TransactionImpl getCurrentTransaction()
{
TransactionImpl retval = getTransaction();
if (null == retval)
{
throw new TransactionNotInProgressException(
"Calling method needed transaction, but no transaction found via TransactionManager");
}
return retval;
}
/**
* Returns the current transaction based on the JTA Transaction or <code>null</code>
* if no transaction was found.
*/
public TransactionImpl getTransaction()
{
TxBuffer buf = (TxBuffer) txRepository.get();
return buf != null ? buf.getInternTx() : null;
}
/**
* Abort an active extern transaction associated with the given PB.
*/
public void abortExternalTx(TransactionImpl odmgTrans)
{
if (log.isDebugEnabled()) log.debug("abortExternTransaction was called");
if (odmgTrans == null) return;
TxBuffer buf = (TxBuffer) txRepository.get();
Transaction extTx = buf != null ? buf.getExternTx() : null;
try
{
if (extTx != null && extTx.getStatus() == Status.STATUS_ACTIVE)
{
if(log.isDebugEnabled())
{
log.debug("Set extern transaction to rollback");
}
extTx.setRollbackOnly();
}
}
catch (Exception ignore)
{
}
txRepository.set(null);
}
public void configure(Configuration config)
{
/**
* no-op
*/
}
//************************************************************************
// inner class
//************************************************************************
private static final class TxBuffer
{
private WeakReference externTx = null;
private WeakReference internTx = null;
public TxBuffer()
{
}
/*
arminw:
use WeakReference to make sure that closed Transaction objects can be
immediately reclaimed by the garbage collector.
*/
public TxBuffer(TransactionImpl internTx, Transaction externTx)
{
this.internTx = new WeakReference(internTx);
this.externTx = new WeakReference(externTx);
}
public Transaction getExternTx()
{
return (Transaction) externTx.get();
}
public void setExternTx(Transaction externTx)
{
this.externTx = new WeakReference(externTx);
}
public TransactionImpl getInternTx()
{
return (TransactionImpl) internTx.get();
}
public void setInternTx(TransactionImpl internTx)
{
this.internTx = new WeakReference(internTx);
}
}
}