/*
* JBoss, Home of Professional Open Source
* Copyright 2006, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags.
* See the copyright.txt in the distribution for a full listing
* of individual contributors.
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* 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,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
* (C) 2005-2006,
* @author JBoss Inc.
*/
/*
* Copyright (C) 2001, 2002,
*
* Hewlett-Packard Arjuna Labs,
* Newcastle upon Tyne,
* Tyne and Wear,
* UK.
*
* $Id: XAResourceRecord.java 2342 2006-03-30 13:06:17Z $
*/
package com.arjuna.ats.internal.jta.resources.jts.orbspecific;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.NotSerializableException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import org.jboss.tm.FirstResource;
import org.jboss.tm.LastResource;
import org.omg.CORBA.OBJECT_NOT_EXIST;
import org.omg.CORBA.SystemException;
import org.omg.CORBA.TRANSACTION_ROLLEDBACK;
import org.omg.CORBA.UNKNOWN;
import org.omg.CosTransactions.Coordinator;
import org.omg.CosTransactions.HeuristicCommit;
import org.omg.CosTransactions.HeuristicHazard;
import org.omg.CosTransactions.HeuristicMixed;
import org.omg.CosTransactions.HeuristicRollback;
import org.omg.CosTransactions.NotPrepared;
import org.omg.CosTransactions.RecoveryCoordinator;
import org.omg.CosTransactions.RecoveryCoordinatorHelper;
import org.omg.CosTransactions.Vote;
import com.arjuna.ArjunaOTS.OTSAbstractRecord;
import com.arjuna.ats.arjuna.common.Uid;
import com.arjuna.ats.arjuna.coordinator.RecordType;
import com.arjuna.ats.arjuna.coordinator.TwoPhaseOutcome;
import com.arjuna.ats.arjuna.objectstore.ParticipantStore;
import com.arjuna.ats.arjuna.objectstore.StoreManager;
import com.arjuna.ats.arjuna.recovery.RecoveryManager;
import com.arjuna.ats.arjuna.recovery.RecoveryModule;
import com.arjuna.ats.arjuna.state.InputObjectState;
import com.arjuna.ats.arjuna.state.OutputObjectState;
import com.arjuna.ats.internal.jta.recovery.jts.XARecoveryModule;
import com.arjuna.ats.internal.jta.resources.XAResourceErrorHandler;
import com.arjuna.ats.internal.jta.transaction.jts.TransactionImple;
import com.arjuna.ats.internal.jta.utils.jtaxLogger;
import com.arjuna.ats.arjuna.logging.tsLogger;
import com.arjuna.ats.internal.jta.xa.TxInfo;
import com.arjuna.ats.internal.jts.ORBManager;
import com.arjuna.ats.jta.common.jtaPropertyManager;
import com.arjuna.ats.jta.logging.jtaLogger;
import com.arjuna.ats.jta.recovery.SerializableXAResourceDeserializer;
import com.arjuna.ats.jta.recovery.XARecoveryResource;
import com.arjuna.ats.jta.utils.XAHelper;
import com.arjuna.ats.jta.xa.RecoverableXAConnection;
import com.arjuna.ats.jta.xa.XidImple;
import com.arjuna.ats.jts.utils.Utility;
import com.arjuna.common.internal.util.ClassloadingUtility;
public class XAResourceRecord extends com.arjuna.ArjunaOTS.OTSAbstractRecordPOA
{
public static final int XACONNECTION = 0;
private static final Uid START_XARESOURCE = Uid.minUid() ;
private static final Uid END_XARESOURCE = Uid.maxUid() ;
/**
* The params represent specific parameters we need to recreate the
* connection to the database in the event of a failure. If they're not set
* then recovery is out of our control.
*
* Could also use it to pass other information, such as the readonly flag.
*/
public XAResourceRecord(TransactionImple tx, XAResource res, Xid xid,
Object[] params)
{
if (jtaxLogger.logger.isTraceEnabled()) {
jtaxLogger.logger.trace("XAResourceRecord.XAResourceRecord ( " + xid + " )");
}
_theXAResource = res;
_recoveryObject = null;
_tranID = xid;
_valid = true;
if (params != null)
{
if (params.length > XACONNECTION)
{
if (params[XACONNECTION] instanceof RecoverableXAConnection)
_recoveryObject = (RecoverableXAConnection) params[XACONNECTION];
}
}
_prepared = false;
_committed = false;
_heuristic = TwoPhaseOutcome.FINISH_OK;
_participantStore = null;
_theUid = new Uid();
_theReference = null;
_recoveryCoordinator = null;
_theTransaction = tx;
if (_theXAResource instanceof FirstResource)
_cachedUidStringForm = START_XARESOURCE.stringForm();
else if (_theXAResource instanceof LastResource)
_cachedUidStringForm = END_XARESOURCE.stringForm();
}
// for recovery only!
public XAResourceRecord ()
{
_theXAResource = null;
_recoveryObject = null;
_tranID = null;
_valid = true;
_prepared = true;
_committed = false;
_heuristic = TwoPhaseOutcome.FINISH_OK;
_participantStore = null;
_theUid = new Uid();
_theReference = null;
_recoveryCoordinator = null;
}
public final Uid get_uid()
{
return _theUid;
}
public final synchronized org.omg.CosTransactions.Resource getResource()
{
if (_theReference == null)
{
ORBManager.getPOA().objectIsReady(this);
_theReference = org.omg.CosTransactions.ResourceHelper
.narrow(ORBManager.getPOA().corbaReference(this));
}
return _theReference;
}
public final Xid getXid()
{
return _tranID;
}
public org.omg.CosTransactions.Vote prepare() throws HeuristicMixed,
HeuristicHazard, org.omg.CORBA.SystemException
{
if (jtaxLogger.logger.isTraceEnabled()) {
jtaxLogger.logger.trace("XAResourceRecord.prepare for " + _tranID);
}
if (!_valid || (_theXAResource == null) || (_tranID == null))
{
jtaxLogger.i18NLogger.warn_jtax_resources_jts_orbspecific_nulltransaction("XAResourceRecord.prepare");
removeConnection();
return Vote.VoteRollback;
}
try
{
/*
* Window of vulnerability versus performance trade-off: if we
* create the resource log here then we slow things down in the case
* the resource rolls back or returns read only (since we have
* written data for no reason and now need to delete it). If we
* create the resource log after we know the prepare outcome then
* there's a chance we may crash between prepare and writing the
* state.
*
* We go for the latter currently since failures are rare, but
* performance is always required. The result is that the
* transaction will roll back (since it won't get an ack from
* prepare) and the resource won't be recovered. The sys. admin.
* will have to clean up manually.
*
* Actually what will happen in the case of ATS is that the XA
* recovery module will eventually roll back this resource when it
* notices that there is no log entry for it.
*/
endAssociation(XAResource.TMSUCCESS, TxInfo.NOT_ASSOCIATED);
if (_theXAResource.prepare(_tranID) == XAResource.XA_RDONLY)
{
removeConnection();
return Vote.VoteReadOnly;
}
else
{
if (createState())
return Vote.VoteCommit;
else
return Vote.VoteRollback;
}
}
catch (XAException e1)
{
jtaxLogger.i18NLogger.warn_jtax_resources_jts_orbspecific_preparefailed(_theXAResource.toString(),
XAHelper.xidToString(_tranID), XAHelper.printXAErrorCode(e1), e1);
if (jtaxLogger.logger.isTraceEnabled()) {
jtaxLogger.logger.tracef(
"XAResourceRecord.prepare exception %s resource_trace: txn uid=%s " +
"resource uid=%s\n",
XAHelper.printXAErrorCode(e1), _tranID, get_uid());
}
/*
* XA_RB*, XAER_RMERR, XAER_RMFAIL, XAER_NOTA, XAER_INVAL, or
* XAER_PROTO.
*/
if (_rollbackOptimization) // won't have rollback called on it
removeConnection();
switch (e1.errorCode)
{
case XAException.XAER_RMERR:
case XAException.XAER_RMFAIL:
case XAException.XA_RBROLLBACK:
case XAException.XA_RBEND:
case XAException.XA_RBCOMMFAIL:
case XAException.XA_RBDEADLOCK:
case XAException.XA_RBINTEGRITY:
case XAException.XA_RBOTHER:
case XAException.XA_RBPROTO:
case XAException.XA_RBTIMEOUT:
case XAException.XAER_INVAL:
case XAException.XAER_PROTO:
case XAException.XAER_NOTA: // resource may have arbitrarily rolled back (shouldn't, but ...)
return Vote.VoteRollback;
default:
throw new HeuristicHazard(); // we're not really sure (shouldn't get here though).
}
}
catch (Exception e2)
{
jtaxLogger.i18NLogger.warn_jtax_resources_jts_orbspecific_preparefailed(_theXAResource.toString(),
XAHelper.xidToString(_tranID), "-", e2);
if (_rollbackOptimization) // won't have rollback called on it
removeConnection();
return Vote.VoteRollback;
}
}
public void rollback() throws org.omg.CORBA.SystemException,
HeuristicCommit, HeuristicMixed, HeuristicHazard
{
if (jtaxLogger.logger.isTraceEnabled()) {
jtaxLogger.logger.trace("XAResourceRecord.rollback for " + _tranID);
}
if (_theTransaction != null
&& _theTransaction.getXAResourceState(_theXAResource) == TxInfo.OPTIMIZED_ROLLBACK)
{
/*
* Already rolledback during delist.
*/
return;
}
if (!_valid || (_tranID == null))
{
jtaxLogger.i18NLogger.warn_jtax_resources_jts_orbspecific_nulltransaction("XAResourceRecord.rollback");
}
else
{
if (_theXAResource != null)
{
switch (_heuristic)
{
case TwoPhaseOutcome.HEURISTIC_HAZARD:
throw new org.omg.CosTransactions.HeuristicHazard();
case TwoPhaseOutcome.HEURISTIC_MIXED:
throw new org.omg.CosTransactions.HeuristicMixed();
default:
break;
}
try
{
if (!_prepared)
{
endAssociation(XAResource.TMFAIL, TxInfo.FAILED);
}
}
catch (XAException e1)
{
if ((e1.errorCode >= XAException.XA_RBBASE)
&& (e1.errorCode < XAException.XA_RBEND))
{
/*
* Has been marked as rollback-only. We still
* need to call rollback.
*/
} else if ((e1.errorCode == XAException.XAER_RMERR) || (e1.errorCode == XAException.XAER_RMFAIL)){
try {
_theXAResource.rollback(_tranID);
} catch (XAException e2)
{
jtaxLogger.i18NLogger.warn_jtax_resources_jts_orbspecific_xaerror("XAResourceRecord.rollback",
XAHelper.printXAErrorCode(e2), _theXAResource.toString(), XAHelper.xidToString(_tranID), e2);
removeConnection();
throw new UNKNOWN();
}
}
else
{
removeConnection();
throw new UNKNOWN();
}
}
try
{
_theXAResource.rollback(_tranID);
}
catch (XAException e1)
{
if (notAProblem(e1, false))
{
// some other thread got there first (probably)
}
else
{
jtaxLogger.i18NLogger.warn_jtax_resources_jts_orbspecific_xaerror("XAResourceRecord.rollback",
XAHelper.printXAErrorCode(e1), _theXAResource.toString(), XAHelper.xidToString(_tranID), e1);
if (jtaxLogger.logger.isTraceEnabled()) {
jtaxLogger.logger.tracef(
"XAResourceRecord.rollback exception %s " +
"resource_trace: " +
"txn uid=%s resource uid=%s\n",
XAHelper.printXAErrorCode(e1), _tranID,
get_uid());
}
switch (e1.errorCode)
{
case XAException.XAER_RMERR:
if (!_prepared)
break; // just do the finally block
case XAException.XA_HEURHAZ:
updateState(TwoPhaseOutcome.HEURISTIC_HAZARD);
throw new org.omg.CosTransactions.HeuristicHazard();
case XAException.XA_HEURCOM:
updateState(TwoPhaseOutcome.HEURISTIC_COMMIT);
throw new org.omg.CosTransactions.HeuristicCommit();
case XAException.XA_HEURMIX:
updateState(TwoPhaseOutcome.HEURISTIC_MIXED);
throw new org.omg.CosTransactions.HeuristicMixed();
case XAException.XA_HEURRB: // forget?
case XAException.XA_RBROLLBACK:
case XAException.XA_RBEND:
case XAException.XA_RBCOMMFAIL:
case XAException.XA_RBDEADLOCK:
case XAException.XA_RBINTEGRITY:
case XAException.XA_RBOTHER:
case XAException.XA_RBPROTO:
case XAException.XA_RBTIMEOUT:
destroyState();
break;
default:
destroyState();
if (_prepared)
throw new org.omg.CosTransactions.HeuristicHazard();
else
throw new UNKNOWN();
}
}
}
catch (Exception e2)
{
jtaxLogger.i18NLogger.warn_jtax_resources_jts_orbspecific_generror("XAResourceRecord.rollback",
_theXAResource.toString(), XAHelper.xidToString(_tranID), e2);
throw new UNKNOWN();
}
finally
{
if (_prepared)
destroyState();
else
removeConnection();
}
}
}
}
public void commit() throws org.omg.CORBA.SystemException, NotPrepared,
HeuristicRollback, HeuristicMixed, HeuristicHazard
{
if (jtaxLogger.logger.isTraceEnabled()) {
jtaxLogger.logger.trace("XAResourceRecord.commit for " + _tranID);
}
if (_tranID == null)
{
jtaxLogger.i18NLogger.warn_jtax_resources_jts_orbspecific_nulltransaction("XAResourceRecord.commit");
}
else
{
if ((_theXAResource != null) && (!_committed))
{
switch (_heuristic)
{
case TwoPhaseOutcome.HEURISTIC_HAZARD:
throw new org.omg.CosTransactions.HeuristicHazard();
case TwoPhaseOutcome.HEURISTIC_MIXED:
throw new org.omg.CosTransactions.HeuristicMixed();
case TwoPhaseOutcome.HEURISTIC_ROLLBACK:
throw new org.omg.CosTransactions.HeuristicRollback();
default:
break;
}
if (!_prepared)
throw new NotPrepared();
boolean removeConnection = true;
try
{
if (!_committed)
{
_committed = true;
_theXAResource.commit(_tranID, false);
destroyState();
}
}
catch (XAException e1)
{
e1.printStackTrace();
if (notAProblem(e1, true))
{
// some other thread got there first (probably)
destroyState();
}
else
{
_committed = false;
jtaxLogger.i18NLogger.warn_jtax_resources_jts_orbspecific_xaerror("XAResourceRecord.commit",
XAHelper.printXAErrorCode(e1), _theXAResource.toString(), XAHelper.xidToString(_tranID), e1);
if (jtaxLogger.logger.isTraceEnabled()) {
jtaxLogger.logger.tracef(
"XAResourceRecord.commit exception %s " +
"resource_trace: txn uid=%s resource uid=%s\n",
XAHelper.printXAErrorCode(e1), _tranID,
get_uid());
}
/*
* XA_HEURHAZ, XA_HEURCOM, XA_HEURRB, XA_HEURMIX,
* XAER_RMERR, XAER_RMFAIL, XAER_NOTA, XAER_INVAL, or
* XAER_PROTO.
*/
switch (e1.errorCode)
{
case XAException.XA_HEURHAZ:
updateState(TwoPhaseOutcome.HEURISTIC_HAZARD);
throw new org.omg.CosTransactions.HeuristicHazard();
case XAException.XA_HEURCOM: // what about forget? OTS doesn't support this code here.
destroyState();
break;
case XAException.XA_HEURRB:
case XAException.XA_RBROLLBACK:
case XAException.XA_RBCOMMFAIL:
case XAException.XA_RBDEADLOCK:
case XAException.XA_RBINTEGRITY:
case XAException.XA_RBOTHER:
case XAException.XA_RBPROTO:
case XAException.XA_RBTIMEOUT:
case XAException.XA_RBTRANSIENT:
case XAException.XAER_RMERR:
updateState(TwoPhaseOutcome.HEURISTIC_ROLLBACK);
throw new org.omg.CosTransactions.HeuristicRollback();
case XAException.XA_HEURMIX:
updateState(TwoPhaseOutcome.HEURISTIC_MIXED);
throw new org.omg.CosTransactions.HeuristicMixed();
case XAException.XAER_NOTA:
// RM unexpectedly lost track of the tx, outcome is uncertain
updateState(TwoPhaseOutcome.HEURISTIC_HAZARD);
throw new org.omg.CosTransactions.HeuristicHazard();
case XAException.XAER_PROTO:
// presumed abort (or we could be really paranoid and throw a heuristic)
throw new TRANSACTION_ROLLEDBACK();
case XAException.XA_RETRY:
case XAException.XAER_RMFAIL:
removeConnection = false;
// Since JBTM-2710 this is not right because it will mean that commit is not reattempted _committed = true; // remember for recovery later.
throw new UNKNOWN(); // will cause log to be rewritten.
case XAException.XAER_INVAL: // resource manager failed, did it rollback?
default:
throw new org.omg.CosTransactions.HeuristicHazard();
}
}
}
catch (Exception e2)
{
_committed = false;
jtaxLogger.i18NLogger.warn_jtax_resources_jts_orbspecific_generror("XAResourceRecord.commit",
_theXAResource.toString(), XAHelper.xidToString(_tranID), e2);
throw new UNKNOWN();
}
finally
{
if (removeConnection) {
removeConnection();
}
}
}
}
}
public org.omg.CosTransactions.Vote prepare_subtransaction()
throws SystemException
{
return Vote.VoteRollback; // shouldn't be possible!
}
public void commit_subtransaction(Coordinator parent)
throws SystemException
{
throw new UNKNOWN();
}
public void rollback_subtransaction() throws SystemException
{
throw new UNKNOWN();
}
public int type_id() throws SystemException
{
return RecordType.JTAX_RECORD;
}
public String uid() throws SystemException
{
if (_cachedUidStringForm == null)
_cachedUidStringForm = _theUid.stringForm();
return _cachedUidStringForm;
}
public boolean propagateOnAbort() throws SystemException
{
return false;
}
public boolean propagateOnCommit() throws SystemException
{
return false; // nesting not supported
}
public boolean saveRecord() throws SystemException
{
return true;
}
public void merge(OTSAbstractRecord record) throws SystemException
{
}
public void alter(OTSAbstractRecord record) throws SystemException
{
}
public boolean shouldAdd(OTSAbstractRecord record) throws SystemException
{
return false;
}
public boolean shouldAlter(OTSAbstractRecord record) throws SystemException
{
return false;
}
public boolean shouldMerge(OTSAbstractRecord record) throws SystemException
{
return false;
}
public boolean shouldReplace(OTSAbstractRecord record)
throws SystemException
{
return false;
}
/**
* Is the XAException a non-error when received in reply to commit or
* rollback? It normally is, but may be overridden in recovery.
*/
protected boolean notAProblem(XAException ex, boolean commit)
{
return XAResourceErrorHandler.notAProblem(_theXAResource, ex, commit);
}
/**
* For commit_one_phase we can do whatever we want since the transaction
* outcome is whatever we want. Therefore, we do not need to save any
* additional recoverable state, such as a reference to the transaction
* coordinator, since it will not have an intentions list anyway.
*/
public void commit_one_phase() throws HeuristicHazard,
org.omg.CORBA.SystemException
{
if (jtaxLogger.logger.isTraceEnabled()) {
jtaxLogger.logger.trace("XAResourceRecord.commit_one_phase for " + _tranID);
}
if (_tranID == null)
{
jtaxLogger.i18NLogger.warn_jtax_resources_jts_orbspecific_nulltransaction("XAResourceRecord.commit_one_phase");
throw new TRANSACTION_ROLLEDBACK();
}
else
{
if (_theXAResource != null)
{
try
{
switch (_heuristic)
{
case TwoPhaseOutcome.HEURISTIC_HAZARD:
throw new org.omg.CosTransactions.HeuristicHazard();
default:
break;
}
/*
* TODO in Oracle, the end is not required. Is this
* common to other RM implementations?
*/
boolean commit = true;
XAException endRBOnly = null;
try
{
endAssociation(XAResource.TMSUCCESS, TxInfo.NOT_ASSOCIATED);
}
catch (XAException e1)
{
/*
* Now it's not legal to return a heuristic from end, but
* apparently Oracle does (http://jira.jboss.com/jira/browse/JBTM-343)
* Since this is 1PC we can call forget: the outcome of the
* transaction is the outcome of the participant.
*/
switch (e1.errorCode)
{
case XAException.XA_HEURHAZ:
case XAException.XA_HEURMIX:
case XAException.XA_HEURCOM:
case XAException.XA_HEURRB:
throw e1;
case XAException.XA_RBROLLBACK:
case XAException.XA_RBCOMMFAIL:
case XAException.XA_RBDEADLOCK:
case XAException.XA_RBINTEGRITY:
case XAException.XA_RBOTHER:
case XAException.XA_RBPROTO:
case XAException.XA_RBTIMEOUT:
case XAException.XA_RBTRANSIENT:
/*
* Has been marked as rollback-only. We still
* need to call rollback.
*/
endRBOnly = e1;
commit = false;
break;
case XAException.XAER_RMERR:
case XAException.XAER_NOTA:
case XAException.XAER_PROTO:
case XAException.XAER_INVAL:
case XAException.XAER_RMFAIL:
default:
{
throw new UNKNOWN();
}
}
}
if (commit)
_theXAResource.commit(_tranID, true);
else {
_theXAResource.rollback(_tranID);
throw endRBOnly;
}
}
catch (XAException e1)
{
/*
* XA_HEURHAZ, XA_HEURCOM, XA_HEURRB, XA_HEURMIX,
* XAER_RMERR, XAER_RMFAIL, XAER_NOTA, XAER_INVAL, or
* XAER_PROTO. XA_RB*
*/
if ((e1.errorCode >= XAException.XA_RBBASE)
&& (e1.errorCode <= XAException.XA_RBEND))
{
throw new TRANSACTION_ROLLEDBACK();
}
switch (e1.errorCode)
{
case XAException.XA_HEURHAZ:
case XAException.XA_HEURMIX:
updateState(TwoPhaseOutcome.HEURISTIC_HAZARD);
throw new org.omg.CosTransactions.HeuristicHazard();
case XAException.XA_HEURCOM:
try {
handleForget() ;
} catch (SystemException ignore) {
}
break;
case XAException.XA_HEURRB:
try {
handleForget() ;
} catch (SystemException ignore) {
}
throw new TRANSACTION_ROLLEDBACK();
case XAException.XA_RBROLLBACK:
case XAException.XA_RBCOMMFAIL:
case XAException.XA_RBDEADLOCK:
case XAException.XA_RBINTEGRITY:
case XAException.XA_RBOTHER:
case XAException.XA_RBPROTO:
case XAException.XA_RBTIMEOUT:
case XAException.XA_RBTRANSIENT:
case XAException.XAER_RMERR:
throw new TRANSACTION_ROLLEDBACK();
case XAException.XAER_NOTA:
// RM unexpectedly lost track of the tx, outcome is uncertain
updateState(TwoPhaseOutcome.HEURISTIC_HAZARD);
throw new org.omg.CosTransactions.HeuristicHazard();
case XAException.XAER_PROTO:
case XAException.XA_RETRY: // not allowed to be thrown here by XA specification!
// presumed abort (or we could be really paranoid and throw a heuristic)
throw new TRANSACTION_ROLLEDBACK();
case XAException.XAER_INVAL: // resource manager failed, did it rollback?
throw new org.omg.CosTransactions.HeuristicHazard();
case XAException.XAER_RMFAIL: // This was modified as part of JBTM-XYZ - although RMFAIL is not clear there is a rollback/commit we are flagging this to the user
throw new org.omg.CosTransactions.HeuristicHazard();
default:
_committed = true; // will cause log to be rewritten
throw new UNKNOWN();
}
}
catch (SystemException ex)
{
ex.printStackTrace();
throw ex;
}
catch (org.omg.CosTransactions.HeuristicHazard ex)
{
throw ex;
}
catch (Exception e2)
{
jtaxLogger.i18NLogger.warn_jtax_resources_jts_orbspecific_coperror(e2);
throw new UNKNOWN();
}
finally
{
removeConnection();
}
}
else
throw new TRANSACTION_ROLLEDBACK();
}
}
public void forget() throws org.omg.CORBA.SystemException
{
if (jtaxLogger.logger.isTraceEnabled()) {
jtaxLogger.logger.trace("XAResourceRecord.forget for " + _tranID +
" _forgotten=" + _forgotten);
}
// if we have not yet seen a successful forget call then tell the resource to forget
if (!_forgotten)
handleForget(); // this call can fail silently which will leave _forgotten as false
// if it is known that the resource has forgotten the heuristic then delete our record of it
if (_forgotten) {
destroyState();
removeConnection();
}
}
public boolean isForgotten() {
return _forgotten;
}
private void handleForget() throws org.omg.CORBA.SystemException
{
if ((_theXAResource != null) && (_tranID != null))
{
try
{
_theXAResource.forget(_tranID);
_heuristic = TwoPhaseOutcome.FINISH_OK;
_forgotten = true;
}
catch (Exception e)
{
jtaLogger.i18NLogger.warn_recovery_forgetfailed(
"XAResourceRecord forget failed:", e);
throw new UNKNOWN(e.getMessage()); // log will be rewritten
}
}
}
public boolean saveState(OutputObjectState os)
{
boolean res = false;
try
{
os.packInt(_heuristic);
os.packBoolean(_committed);
/*
* Since we don't know what type of Xid we are using, leave it up to
* XID to pack.
*/
XidImple.pack(os, _tranID);
/*
* If no recovery object set then rely upon object serialisation!
*/
if (_recoveryObject == null)
{
os.packInt(RecoverableXAConnection.OBJECT_RECOVERY);
if (_theXAResource instanceof Serializable)
{
try
{
ByteArrayOutputStream s = new ByteArrayOutputStream();
ObjectOutputStream o = new ObjectOutputStream(s);
o.writeObject(_theXAResource);
o.close();
os.packBoolean(true);
String name = _theXAResource.getClass().getName();
os.packString(name);
os.packBytes(s.toByteArray());
}
catch (NotSerializableException ex)
{
jtaxLogger.i18NLogger.warn_jtax_resources_jts_orbspecific_saveState();
return false;
}
}
else
{
// have to rely upon XAResource.recover!
os.packBoolean(false);
}
}
else
{
os.packInt(RecoverableXAConnection.AUTO_RECOVERY);
os.packString(_recoveryObject.getClass().getName());
_recoveryObject.packInto(os);
}
if (_recoveryCoordinator == null)
os.packBoolean(false);
else
{
os.packBoolean(true);
String ior = ORBManager.getORB().orb().object_to_string(
_recoveryCoordinator);
os.packString(ior);
ior = null;
}
res = true;
}
catch (Exception e)
{
e.printStackTrace();
res = false;
}
return res;
}
public boolean restoreState(InputObjectState os)
{
boolean res = false;
try
{
_heuristic = os.unpackInt();
_committed = os.unpackBoolean();
_tranID = XidImple.unpack(os);
_theXAResource = null;
_recoveryObject = null;
if (os.unpackInt() == RecoverableXAConnection.OBJECT_RECOVERY)
{
boolean haveXAResource = os.unpackBoolean();
if (haveXAResource)
{
try
{
// Read the classname of the serialized XAResource
String className = os.unpackString();
byte[] b = os.unpackBytes();
ByteArrayInputStream s = new ByteArrayInputStream(b);
ObjectInputStream o = new ObjectInputStream(s);
// Give the list of deserializers a chance to deserialize the record
boolean deserialized = false;
Iterator<SerializableXAResourceDeserializer> iterator = getXAResourceDeserializers().iterator();
while (iterator.hasNext()) {
SerializableXAResourceDeserializer proxyXAResourceDeserializer = iterator.next();
if (proxyXAResourceDeserializer.canDeserialze(className)) {
_theXAResource = proxyXAResourceDeserializer.deserialze(o);
deserialized = true;
break;
}
}
try {
// Give it a go ourselves
if (!deserialized) {
_theXAResource = (XAResource) o.readObject();
}
if (jtaxLogger.logger.isTraceEnabled()) {
jtaxLogger.logger.trace("XAResourceRecord.restore_state - XAResource de-serialized");
}
} catch (ClassNotFoundException cnfe) {
jtaxLogger.i18NLogger.warn_could_not_load_class_will_wait_for_bottom_up(cnfe);
} finally {
o.close();
}
}
catch (Exception ex)
{
// not serializable in the first place!
jtaxLogger.i18NLogger.warn_jtax_resources_jts_orbspecific_restoreerror1(ex);
return false;
}
}
}
else
{
String creatorName = os.unpackString();
_recoveryObject = ClassloadingUtility.loadAndInstantiateClass(RecoverableXAConnection.class, creatorName, null);
if(_recoveryObject == null) {
throw new ClassNotFoundException();
}
_recoveryObject.unpackFrom(os);
_theXAResource = _recoveryObject.getResource();
if (_theXAResource == null)
{
jtaxLogger.i18NLogger.warn_jtax_resources_jts_orbspecific_norecoveryxa(XAHelper.xidToString(_tranID));
}
if (jtaxLogger.logger.isTraceEnabled()) {
jtaxLogger.logger.trace("XAResourceRecord.restore_state - XAResource got from "
+ creatorName);
}
}
boolean haveRecCoord = os.unpackBoolean();
if (haveRecCoord)
{
String ior = os.unpackString();
if (ior == null)
return false;
else
{
org.omg.CORBA.Object objRef = ORBManager.getORB().orb()
.string_to_object(ior);
_recoveryCoordinator = RecoveryCoordinatorHelper
.narrow(objRef);
}
}
else
_recoveryCoordinator = null;
res = true;
}
catch (Exception e)
{
jtaxLogger.i18NLogger.warn_jtax_resources_jts_orbspecific_restoreerror2(e);
res = false;
}
finally
{
/*
* If we're here then we've restored enough to print data on
* this instance.
*/
if (_heuristic != TwoPhaseOutcome.FINISH_OK)
{
jtaxLogger.logger.warn("XAResourceRecord restored heuristic instance: "+this);
}
}
return res;
}
public String type()
{
return XAResourceRecord.typeName();
}
public static String typeName()
{
return "/CosTransactions/XAResourceRecord";
}
public final void setRecoveryCoordinator(RecoveryCoordinator recCoord)
{
_recoveryCoordinator = recCoord;
}
/**
* @deprecated Only used in tests
*/
public final RecoveryCoordinator getRecoveryCoordinator()
{
return _recoveryCoordinator;
}
public String toString ()
{
return "XAResourceRecord < resource:"+_theXAResource+", txid:"+_tranID+", heuristic"+TwoPhaseOutcome.stringForm(_heuristic)+" "+super.toString()+" >";
}
protected XAResourceRecord(Uid u)
{
_theXAResource = null;
_recoveryObject = null;
_tranID = null;
_prepared = true;
_committed = false;
_heuristic = TwoPhaseOutcome.FINISH_OK;
_theUid = new Uid(u);
_participantStore = null;
_valid = false;
_theReference = null;
_recoveryCoordinator = null;
_theTransaction = null;
_valid = loadState();
}
/**
* For those objects where the original XAResource could not be saved.
*/
protected synchronized void setXAResource(XAResource res)
{
_theXAResource = res;
}
protected int recover()
{
if (jtaxLogger.logger.isTraceEnabled()) {
jtaxLogger.logger.trace("XAResourceRecord.recover");
}
if (_valid)
{
org.omg.CosTransactions.Status s = org.omg.CosTransactions.Status.StatusUnknown;
try
{
// force tx to rollback if not prepared
s = _recoveryCoordinator.replay_completion(getResource());
}
catch (OBJECT_NOT_EXIST ex)
{
// no coordinator, so presumed abort unless we have better information.
if (_committed)
s = org.omg.CosTransactions.Status.StatusCommitted;
else
s = org.omg.CosTransactions.Status.StatusRolledBack;
}
catch (NotPrepared ex1)
{
jtaxLogger.i18NLogger.warn_jtax_resources_jts_orbspecific_notprepared("XAResourceRecord");
return XARecoveryResource.TRANSACTION_NOT_PREPARED;
}
catch (java.lang.NullPointerException ne)
{
/*
* No recovery coordinator!
*/
}
catch (Exception e)
{
/*
* Unknown error, so better to do nothing at this stage.
*/
jtaxLogger.i18NLogger.warn_jtax_resources_jts_orbspecific_unexpected("XAResourceRecord", e);
return XARecoveryResource.FAILED_TO_RECOVER;
}
if (jtaxLogger.logger.isTraceEnabled()) {
jtaxLogger.logger.trace("XAResourceRecord.recover got status: "
+ Utility.stringStatus(s));
}
boolean doCommit = false;
switch (s.value())
{
case org.omg.CosTransactions.Status._StatusUnknown:
// some problem occurred
return XARecoveryResource.FAILED_TO_RECOVER;
case org.omg.CosTransactions.Status._StatusMarkedRollback:
case org.omg.CosTransactions.Status._StatusRollingBack:
// we should be told eventually, so wait
return XARecoveryResource.WAITING_FOR_RECOVERY;
case org.omg.CosTransactions.Status._StatusCommitted:
doCommit = true;
break;
case org.omg.CosTransactions.Status._StatusRolledBack:
case org.omg.CosTransactions.Status._StatusNoTransaction:
// presumed abort
doCommit = false;
break;
case org.omg.CosTransactions.Status._StatusCommitting:
// leave it for now as we'll be driven top-down soon
return XARecoveryResource.WAITING_FOR_RECOVERY;
default:
// wait
return XARecoveryResource.FAILED_TO_RECOVER;
}
return doRecovery(doCommit);
}
return XARecoveryResource.FAILED_TO_RECOVER;
}
private final void setObjectStore()
{
if (_participantStore == null)
_participantStore = StoreManager.getParticipantStore();
}
private final boolean createState()
{
setObjectStore();
if ((_theXAResource != null) && (_tranID != null)
&& (_participantStore != null))
{
OutputObjectState os = new OutputObjectState();
if (saveState(os))
{
try
{
_valid = _participantStore.write_committed(_theUid, type(), os);
_prepared = true;
}
catch (Exception e)
{
jtaxLogger.i18NLogger.warn_jtax_resources_jts_orbspecific_createstate();
_valid = false;
}
}
else
_valid = false;
}
else
_valid = false;
return _valid;
}
protected final boolean updateState(int h)
{
setObjectStore();
if (_prepared) // only need do if we have prepared
{
OutputObjectState os = new OutputObjectState();
_heuristic = h;
if (saveState(os))
{
try
{
_valid = _participantStore.write_committed(_theUid, type(), os);
}
catch (Exception e)
{
_valid = false;
}
}
else
{
jtaxLogger.i18NLogger.warn_jtax_resources_jts_orbspecific_updatestate();
_valid = false;
}
}
return _valid;
}
private final boolean loadState()
{
setObjectStore();
InputObjectState os = null;
try
{
os = _participantStore.read_committed(_theUid, type());
}
catch (Exception e)
{
jtaxLogger.i18NLogger.warn_jtax_resources_jts_orbspecific_loadstateread(e);
os = null;
}
if (os != null)
{
_valid = restoreState(os);
os = null;
}
else
_valid = false;
return _valid;
}
private final boolean destroyState()
{
setObjectStore();
if (_prepared && _valid)
{
try
{
_valid = _participantStore.remove_committed(_theUid, type());
}
catch (Exception e)
{
tsLogger.i18NLogger.warn_objectstore_remove_state_exception(e);
_valid = false;
}
}
removeConnection();
return _valid;
}
private final void removeConnection()
{
/*
* Should only be called once. Remove the connection so that user can
* reuse the driver as though it were fresh (e.g., can do read only
* optimisation).
*/
if (_recoveryObject != null)
{
_recoveryObject.close();
_recoveryObject = null;
}
if (_theTransaction != null)
_theTransaction = null;
try
{
if (_theReference != null)
{
ORBManager.getPOA().shutdownObject(this);
_theReference = null;
}
}
catch (Exception e)
{
jtaxLogger.i18NLogger.warn_jtax_resources_jts_orbspecific_remconn(e);
}
}
private final int doRecovery(boolean commit)
{
if (jtaxLogger.logger.isTraceEnabled()) {
jtaxLogger.logger.trace("XAResourceRecord.doRecovery ( " + commit + " )");
}
int result = XARecoveryResource.FAILED_TO_RECOVER;
if ((_theXAResource != null) && (_tranID != null))
{
try
{
if (commit) {
jtaxLogger.i18NLogger.info_jtax_recovery_jts_orbspecific_commit(XAHelper.xidToString(_tranID));
commit();
} else {
jtaxLogger.i18NLogger.info_jtax_recovery_jts_orbspecific_rollback(XAHelper.xidToString(_tranID));
rollback();
}
// if those succeed, they will have removed any persistent state
result = XARecoveryResource.RECOVERED_OK;
}
catch (Exception e2)
{
jtaxLogger.i18NLogger.warn_jtax_resources_jts_orbspecific_recfailed(((commit) ? "commit" : "rollback"), e2);
}
}
return result;
}
/*
* Ask the transaction whether or not this XAResource is still associated
* with the thread, i.e., has end already been called on it?
*/
private final void endAssociation(int xaState, int txInfoState) throws XAException
{
if (_theTransaction != null) {
_theTransaction.endAssociation(_tranID, _theXAResource, xaState, txInfoState);
}
}
private List<SerializableXAResourceDeserializer> getXAResourceDeserializers() {
if (serializableXAResourceDeserializers != null) {
return serializableXAResourceDeserializers;
}
synchronized (this) {
if (serializableXAResourceDeserializers != null) {
return serializableXAResourceDeserializers;
}
serializableXAResourceDeserializers = new ArrayList<SerializableXAResourceDeserializer>();
for (RecoveryModule recoveryModule : RecoveryManager.manager().getModules()) {
if (recoveryModule instanceof XARecoveryModule) {
XARecoveryModule xaRecoveryModule = (XARecoveryModule) recoveryModule;
serializableXAResourceDeserializers.addAll(xaRecoveryModule.getSeriablizableXAResourceDeserializers());
return serializableXAResourceDeserializers;
}
}
}
return serializableXAResourceDeserializers;
}
protected XAResource _theXAResource;
private RecoverableXAConnection _recoveryObject;
private Xid _tranID;
private boolean _prepared;
private boolean _committed;
private boolean _valid;
private int _heuristic;
private ParticipantStore _participantStore;
private Uid _theUid;
private org.omg.CosTransactions.Resource _theReference;
private org.omg.CosTransactions.RecoveryCoordinator _recoveryCoordinator;
private TransactionImple _theTransaction;
private boolean _forgotten;
// cached variables
private String _cachedUidStringForm;
private static boolean _rollbackOptimization = jtaPropertyManager.getJTAEnvironmentBean().isXaRollbackOptimization();
private List<SerializableXAResourceDeserializer> serializableXAResourceDeserializers;
}