use of com.arjuna.ats.jta.xa.XAModifier in project narayana by jbosstm.
the class ConnectionImple method registerDatabase.
/**
* Whenever a JDBC call is invoked on us we get an XAResource and try to
* register it with the transaction. If the same thread causes this to
* happen many times within the same transaction then we will currently
* attempt to get and register many redundant XAResources for it. The JTA
* implementation will detect this and ignore all but the first for each
* thread. However, a further optimisation would be to trap such calls here
* and not do a registration at all. This would require the connection
* object to be informed whenever a transaction completes so that it could
* flush its cache of XAResources though.
*/
protected final synchronized void registerDatabase() throws SQLException {
if (jdbcLogger.logger.isTraceEnabled()) {
jdbcLogger.logger.trace("ConnectionImple.registerDatabase ()");
}
boolean needsClose = _theConnection == null;
Connection theConnection = getConnection();
if (theConnection != null) {
XAResource xares = null;
try {
javax.transaction.TransactionManager tm = com.arjuna.ats.jta.TransactionManager.transactionManager();
javax.transaction.Transaction tx = tm.getTransaction();
if (tx == null) {
return;
}
if (!_transactionalDriverXAConnectionConnection.setTransaction(tx))
throw new SQLException(jdbcLogger.i18NLogger.get_alreadyassociated());
Object[] params;
if (_theModifier != null)
params = new Object[2];
else
params = new Object[1];
params[com.arjuna.ats.jta.transaction.Transaction.XACONNECTION] = _transactionalDriverXAConnectionConnection;
if (_theModifier != null)
params[com.arjuna.ats.jta.transaction.Transaction.XAMODIFIER] = (XAModifier) _theModifier;
/*
* Use our extended version of enlistResource.
*/
xares = _transactionalDriverXAConnectionConnection.getResource();
if (!((com.arjuna.ats.jta.transaction.Transaction) tx).enlistResource(xares, params)) {
try {
tx.setRollbackOnly();
} catch (Exception e) {
jdbcLogger.i18NLogger.warn_rollbackerror("ConnectionImple.registerDatabase");
SQLException sqlException = new SQLException(e.toString());
sqlException.initCause(e);
throw sqlException;
}
throw new SQLException("ConnectionImple.registerDatabase - " + jdbcLogger.i18NLogger.get_enlistfailed());
} else {
getModifier();
if (_theModifier == null) {
jdbcLogger.i18NLogger.info_closingconnectionnull(_theConnection.toString());
// no indication about connections, so assume close immediately
/*
* Don't return just yet. Drop through bottom of these clauses and
* close _theConnection and _recoveryConnection.
*
* delayClose is false at this point.
*
* JBTM-789.
*/
} else {
if (((ConnectionModifier) _theModifier).supportsMultipleConnections()) {
/*
* We can't close the connection until the transaction has
* terminated, so register a Synchronization here.
*/
jdbcLogger.i18NLogger.debug_closingconnection(_theConnection.toString());
jtaPropertyManager.getJTAEnvironmentBean().getTransactionSynchronizationRegistry().registerInterposedSynchronization(new ConnectionSynchronization(this));
}
}
}
} catch (RollbackException e1) {
SQLException sqlException = new SQLException("ConnectionImple.registerDatabase - " + e1);
sqlException.initCause(e1);
throw sqlException;
} catch (SystemException e2) {
SQLException sqlException = new SQLException("ConnectionImple.registerDatabase - " + e2);
sqlException.initCause(e2);
throw sqlException;
} catch (SQLException e3) {
throw e3;
} catch (Exception e4) {
SQLException sqlException = new SQLException(e4.toString());
sqlException.initCause(e4);
throw sqlException;
}
}
}
use of com.arjuna.ats.jta.xa.XAModifier in project narayana by jbosstm.
the class TransactionImple method enlistResource.
public boolean enlistResource(XAResource xaRes, Object[] params) throws RollbackException, IllegalStateException, javax.transaction.SystemException {
if (jtaxLogger.logger.isTraceEnabled()) {
jtaxLogger.logger.trace("TransactionImple.enlistResource ( " + xaRes + " )");
}
if (xaRes == null)
throw new javax.transaction.SystemException("TransactionImple.enlistResource - " + jtaxLogger.i18NLogger.get_jtax_transaction_jts_nullparam());
int status = getStatus();
switch(status) {
case javax.transaction.Status.STATUS_MARKED_ROLLBACK:
throw new RollbackException("TransactionImple.enlistResource - " + jtaxLogger.i18NLogger.get_jtax_transaction_jts_markedrollback());
case javax.transaction.Status.STATUS_ACTIVE:
break;
default:
throw new IllegalStateException(jtaxLogger.i18NLogger.get_jtax_transaction_jts_inactivetx());
}
XAModifier theModifier = null;
if (params != null) {
if (params.length > XAMODIFIER) {
if (params[XAMODIFIER] instanceof XAModifier) {
theModifier = (XAModifier) params[XAMODIFIER];
}
}
}
try {
/*
* For each transaction we maintain a list of resources registered
* with it. Each element on this list also contains a list of
* threads which have registered this resource, and what their XID
* was for that registration.
*/
TxInfo info = null;
try {
synchronized (this) {
info = (TxInfo) _resources.get(xaRes);
if (info == null) {
/*
* Null info means it's not in the main resources list,
* but may be in the duplicates.
*/
info = (TxInfo) _duplicateResources.get(xaRes);
}
}
if (info != null) {
switch(info.getState()) {
case TxInfo.ASSOCIATION_SUSPENDED:
{
/*
* Have seen resource before, so do a resume. The
* Resource instance will still be registered with the
* transaction though.
*/
xaRes.start(info.xid(), XAResource.TMRESUME);
info.setState(TxInfo.ASSOCIATED);
synchronized (this) {
_suspendCount--;
}
// already registered resource with this
return true;
// transaction!
}
case TxInfo.ASSOCIATED:
{
return true;
}
case TxInfo.NOT_ASSOCIATED:
{
/*
* Resource was associated, but was presumably delisted.
*/
xaRes.start(info.xid(), XAResource.TMJOIN);
info.setState(TxInfo.ASSOCIATED);
return true;
}
default:
{
// block
throw new IllegalStateException("TransactionImple.enlistResource - " + jtaxLogger.i18NLogger.get_jtax_transaction_jts_illegalstate() + info.getState());
}
}
}
} catch (IllegalStateException ex) {
// we threw it in the first place
throw ex;
} catch (XAException exp) {
if (info != null)
info.setState(TxInfo.FAILED);
jtaxLogger.i18NLogger.warn_jtax_transaction_jts_xaerror("TransactionImple.enlistResource", XAHelper.printXAErrorCode(exp), exp);
return false;
}
// if (threadIsActive(xaRes))
// return true; // this thread has already registered a resource for
// this db
/*
* We definitely haven't seen this specific resource instance
* before, but that doesn't mean that we haven't seen the RM it is
* connected to.
*/
Xid xid = null;
TxInfo existingRM = isNewRM(xaRes);
if (existingRM == null) {
/*
* New RM, so create xid with new branch.
*/
boolean branchRequired = true;
synchronized (this) {
if (// first ever, so no need for
_resources.size() == 0) // branch
{
// branchRequired = false;
branchRequired = true;
}
}
xid = createXid(branchRequired, theModifier);
boolean associatedWork = false;
int retry = 20;
while (!associatedWork) {
try {
if (_xaTransactionTimeoutEnabled) {
int timeout = _theTransaction.getTimeout();
if (timeout > 0) {
try {
xaRes.setTransactionTimeout(timeout);
} catch (XAException te) {
jtaxLogger.i18NLogger.warn_jtax_transaction_jts_timeouterror("TransactionImple.enlistResource", XAHelper.printXAErrorCode(te), XAHelper.xidToString(xid), te);
}
}
}
// Pay attention now, this bit is hairy. We need to add a new XAResourceRecord
// to the transaction, which will thereafter drive its completion. However, the transaction
// core is not directly XA aware, so it's our job to start the XAResource. Problem is, if
// adding the record fails, the tx will never end the resource via the XAResourceRecord,
// so we must do so directly. start may fail due to dupl xid or other reason, and transactions
// may rollback async, for which reasons we can't call add before start.
// The xid will change on each pass of the loop, so we need to create a new record on each pass.
// The registerResource will fail in the case of multiple last resources being disallowed.
// see JBTM-362 and JBTM-363
XAResourceRecord xaResourceRecord = createRecord(xaRes, params, xid);
if (xaResourceRecord != null) {
xaRes.start(xid, XAResource.TMNOFLAGS);
try {
RecoveryCoordinator recCoord = _theTransaction.registerResource(xaResourceRecord.getResource());
xaResourceRecord.setRecoveryCoordinator(recCoord);
if (jtaxLogger.logger.isTraceEnabled()) {
jtaxLogger.logger.tracef("TransactionImple.enlistResource: " + "resource_trace: txn uid=%s XAReource=%s resource uid=%s\n", get_uid(), xaRes, xaResourceRecord.get_uid());
}
} catch (Exception e) {
// we called start on the resource, but _theTransaction did not accept it.
// we therefore have a mess which we must now clean up by ensuring the start is undone:
xaResourceRecord.rollback();
markRollbackOnly();
jtaxLogger.logger.debug("Can't set recovery coordinator for xa resource record: " + xaResourceRecord + ", " + e.getClass().getName() + ": " + e.getMessage(), e);
return false;
}
_resources.put(xaRes, new TxInfo(xid));
// dive out, no need to set associatedWork = true;
return true;
}
// if we get to here, something other than a failure of xaRes.start probably went wrong.
// so we don't loop and retry, we just give up.
markRollbackOnly();
return false;
} catch (XAException e) {
/* We get this from Oracle instead of DUPID. */
if (e.errorCode == XAException.XAER_RMERR) {
if (retry > 0)
xid = createXid(true, theModifier);
retry--;
} else if (e.errorCode == XAException.XAER_DUPID) {
if (retry > 0)
xid = createXid(true, theModifier);
retry--;
} else {
/*
* Can't do start, so set transaction to
* rollback only.
*/
jtaxLogger.i18NLogger.warn_jtax_transaction_jts_starterror("TransactionImple.enlistResource - XAResource.start", XAHelper.printXAErrorCode(e), XAHelper.xidToString(xid), e);
markRollbackOnly();
throw e;
}
if (retry < 0) {
jtaxLogger.i18NLogger.warn_jtax_transaction_jts_starterror("TransactionImple.enlistResource - XAResource.start", XAHelper.printXAErrorCode(e), XAHelper.xidToString(xid), e);
markRollbackOnly();
throw new UNKNOWN();
}
}
}
} else {
/*
* Have seen this RM before, so ignore this instance. The first
* registered RM instance will be used to drive the transaction
* completion. We add it to the duplicateResource list so we can
* delist it correctly later though.
*/
/*
* Re-create xid.
*/
xid = existingRM.xid();
try {
xaRes.start(xid, XAResource.TMJOIN);
} catch (XAException ex) {
jtaxLogger.i18NLogger.warn_jtax_transaction_jts_xaerror("TransactionImple.enlistResource - xa_start: ", XAHelper.printXAErrorCode(ex), ex);
markRollbackOnly();
throw ex;
}
/*
* Add to duplicate resources list so we can keep track of it
* (particularly if we later have to delist).
*/
_duplicateResources.put(xaRes, new TxInfo(xid));
return true;
}
return false;
} catch (Exception e) {
/*
* Some exceptional condition arose and we probably could not enlist
* the resouce. So, for safety mark the transaction as rollback
* only.
*/
jtaxLogger.i18NLogger.warn_could_not_enlist_xar(xaRes, params, e);
markRollbackOnly();
return false;
}
}
use of com.arjuna.ats.jta.xa.XAModifier in project narayana by jbosstm.
the class TransactionImple method enlistResource.
public boolean enlistResource(XAResource xaRes, Object[] params) throws RollbackException, IllegalStateException, javax.transaction.SystemException {
if (jtaLogger.logger.isTraceEnabled()) {
jtaLogger.logger.trace("TransactionImple.enlistResource ( " + xaRes + " )");
}
if (xaRes == null)
throw new javax.transaction.SystemException("TransactionImple.enlistResource - " + jtaLogger.i18NLogger.get_transaction_arjunacore_nullres());
int status = getStatus();
switch(status) {
case javax.transaction.Status.STATUS_MARKED_ROLLBACK:
throw new RollbackException("TransactionImple.enlistResource - " + jtaLogger.i18NLogger.get_transaction_arjunacore_invalidstate());
case javax.transaction.Status.STATUS_ACTIVE:
break;
default:
throw new IllegalStateException(jtaLogger.i18NLogger.get_transaction_arjunacore_inactive());
}
XAModifier theModifier = null;
if (params != null) {
if (params.length >= XAMODIFIER + 1) {
if (params[XAMODIFIER] instanceof XAModifier) {
theModifier = (XAModifier) params[XAMODIFIER];
}
}
}
try {
/*
* For each transaction we maintain a list of resources registered
* with it. Each element on this list also contains a list of
* threads which have registered this resource, and what their XID
* was for that registration.
*/
TxInfo info = null;
try {
synchronized (this) {
info = (TxInfo) _resources.get(xaRes);
if (info == null) {
/*
* Null info means it's not in the main resources list,
* but may be in the duplicates.
*/
info = (TxInfo) _duplicateResources.get(xaRes);
}
}
if (info != null) {
switch(info.getState()) {
case TxInfo.ASSOCIATION_SUSPENDED:
{
/*
* Have seen resource before, so do a resume. The
* Resource instance will still be registered with the
* transaction though.
*/
int xaStartResume = ((theModifier == null) ? XAResource.TMRESUME : theModifier.xaStartParameters(XAResource.TMRESUME));
xaRes.start(info.xid(), xaStartResume);
info.setState(TxInfo.ASSOCIATED);
synchronized (this) {
_suspendCount--;
}
// already registered resource with this
return true;
// transaction!
}
case TxInfo.ASSOCIATED:
{
return true;
}
case TxInfo.NOT_ASSOCIATED:
{
/*
* Resource was associated, but was presumably delisted.
*/
int xaStartJoin = ((theModifier == null) ? XAResource.TMJOIN : theModifier.xaStartParameters(XAResource.TMJOIN));
xaRes.start(info.xid(), xaStartJoin);
info.setState(TxInfo.ASSOCIATED);
return true;
}
default:
{
throw new IllegalStateException("TransactionImple.enlistResource - " + jtaLogger.i18NLogger.get_transaction_arjunacore_illresstate() + ":" + info.getState());
}
}
}
} catch (IllegalStateException ex) {
// we threw it in the first place
throw ex;
} catch (XAException exp) {
if (info != null)
info.setState(TxInfo.FAILED);
jtaLogger.i18NLogger.warn_transaction_arjunacore_enlisterror("TransactionImple.enlistResource", XAHelper.printXAErrorCode(exp));
return false;
}
// if (threadIsActive(xaRes))
// return true; // this thread has already registered a resource for
// this db
/*
* We definitely haven't seen this specific resource instance
* before, but that doesn't mean that we haven't seen the RM it is
* connected to.
*/
Xid xid = null;
TxInfo existingRM = isNewRM(xaRes);
if (existingRM == null) {
/*
* New RM, so create xid with new branch.
*/
boolean branchRequired = true;
synchronized (this) {
if (// first ever, so no need for
_resources.size() == 0) // branch
{
// branchRequired = false;
branchRequired = true;
}
}
xid = createXid(branchRequired, theModifier, xaRes);
boolean associatedWork = false;
int retry = 20;
while (!associatedWork) {
try {
if (_xaTransactionTimeoutEnabled) {
int timeout = _theTransaction.getTimeout();
if (timeout > 0) {
try {
xaRes.setTransactionTimeout(timeout);
} catch (XAException te) {
jtaLogger.i18NLogger.warn_transaction_arjunacore_timeouterror("TransactionImple.enlistResource", XAHelper.xidToString(xid), XAHelper.printXAErrorCode(te), te);
}
}
}
int xaStartNormal = ((theModifier == null) ? XAResource.TMNOFLAGS : theModifier.xaStartParameters(XAResource.TMNOFLAGS));
// Pay attention now, this bit is hairy. We need to add a new AbstractRecord (XAResourceRecord)
// to the BasicAction, which will thereafter drive its completion. However, the transaction
// core is not directly XA aware, so it's our job to start the XAResource. Problem is, if
// adding the record fails, BasicAction will never end the resource via the XAResourceRecord,
// so we must do so directly. start may fail due to dupl xid or other reason, and transactions
// may rollback async, for which reasons we can't call add before start.
// The xid will change on each pass of the loop, so we need to create a new record on each pass.
// The add will fail in the case of multiple last resources being disallowed
// see JBTM-362 and JBTM-363
AbstractRecord abstractRecord = createRecord(xaRes, params, xid);
if (abstractRecord != null) {
xaRes.start(xid, xaStartNormal);
if (_theTransaction.add(abstractRecord) == AddOutcome.AR_ADDED) {
_resources.put(xaRes, new TxInfo(xid));
// dive out, no need to set associatedWork = true;
return true;
} else {
// we called start on the resource, but _theTransaction did not accept it.
// we therefore have a mess which we must now clean up by ensuring the start is undone:
abstractRecord.topLevelAbort();
}
}
// if we get to here, something other than a failure of xaRes.start probably went wrong.
// so we don't loop and retry, we just give up.
markRollbackOnly();
return false;
} catch (XAException e) {
if ((e.errorCode == XAException.XAER_DUPID) || (e.errorCode == XAException.XAER_RMERR)) {
if (retry > 0)
xid = createXid(true, theModifier, xaRes);
retry--;
} else {
/*
* Can't do start, so set transaction to rollback
* only.
*/
jtaLogger.i18NLogger.warn_transaction_arjunacore_enliststarterror("TransactionImple.enlistResource", XAHelper.xidToString(xid), XAHelper.printXAErrorCode(e), e);
markRollbackOnly();
throw e;
}
if (retry < 0) {
jtaLogger.i18NLogger.warn_transaction_arjunacore_enliststarterror("TransactionImple.enlistResource", XAHelper.xidToString(xid), XAHelper.printXAErrorCode(e), e);
markRollbackOnly();
throw new javax.transaction.SystemException("TransactionImple.enlistResource - XAResource.start " + jtaLogger.i18NLogger.get_transaction_arjunacore_couldnotregister() + ": " + xid);
}
}
}
} else {
/*
* Have seen this RM before, so ignore this instance. The first
* registered RM instance will be used to drive the transaction
* completion. We add it to the duplicateResource list so we can
* delist it correctly later though.
*/
/*
* Re-create xid.
*/
xid = existingRM.xid();
try {
int xaStartJoin = ((theModifier == null) ? XAResource.TMJOIN : theModifier.xaStartParameters(XAResource.TMJOIN));
xaRes.start(xid, xaStartJoin);
} catch (XAException ex) {
jtaLogger.i18NLogger.warn_transaction_arjunacore_xastart("TransactionImple.enlistResource - xa_start ", XAHelper.xidToString(xid), XAHelper.printXAErrorCode(ex), ex);
markRollbackOnly();
throw ex;
}
/*
* Add to duplicate resources list so we can keep track of it
* (particularly if we later have to delist).
*/
_duplicateResources.put(xaRes, new TxInfo(xid));
return true;
}
return false;
} catch (Exception e) {
e.printStackTrace();
/*
* Some exceptional condition arose and we probably could not enlist
* the resouce. So, for safety mark the transaction as rollback
* only.
*/
markRollbackOnly();
return false;
}
}
Aggregations