/*
* 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) 1998, 1999, 2000, 2001,
*
* Arjuna Solutions Limited,
* Newcastle upon Tyne,
* Tyne and Wear,
* UK.
*
* $Id: XAResourceRecord.java 2342 2006-03-30 13:06:17Z $
*/
package com.arjuna.ats.internal.jta.resources.arjunacore;
import com.arjuna.ats.jta.recovery.*;
import com.arjuna.ats.jta.common.jtaPropertyManager;
import com.arjuna.ats.jta.common.Environment;
import com.arjuna.ats.jta.xa.RecoverableXAConnection;
import com.arjuna.ats.jta.xa.XidImple;
import com.arjuna.ats.jta.utils.XAHelper;
import com.arjuna.ats.jta.logging.*;
import com.arjuna.ats.jta.resources.StartXAResource;
import com.arjuna.ats.jta.resources.EndXAResource;
import com.arjuna.common.util.logging.*;
import com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple;
import com.arjuna.ats.internal.jta.xa.TxInfo;
import com.arjuna.ats.internal.jta.resources.XAResourceErrorHandler;
import com.arjuna.ats.internal.jta.recovery.arjunacore.XARecoveryModule;
import com.arjuna.ats.arjuna.ObjectType;
import com.arjuna.ats.arjuna.coordinator.AbstractRecord;
import com.arjuna.ats.arjuna.coordinator.TwoPhaseOutcome;
import com.arjuna.ats.arjuna.coordinator.RecordType;
import com.arjuna.ats.arjuna.common.*;
import com.arjuna.ats.arjuna.state.*;
import com.arjuna.ats.arjuna.gandiva.ClassName;
import com.arjuna.ats.arjuna.recovery.RecoveryModule;
import com.arjuna.ats.arjuna.recovery.RecoveryManager;
import java.util.Vector;
import java.util.Enumeration;
import java.io.*;
import javax.transaction.xa.Xid;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.XAException;
/**
* @author Mark Little (mark_little@hp.com)
* @version $Id: XAResourceRecord.java 2342 2006-03-30 13:06:17Z $
* @since JTS 1.2.4.
*/
public class XAResourceRecord extends AbstractRecord
{
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)
{
super(new Uid(), null, ObjectType.ANDPERSISTENT);
if (jtaLogger.logger.isDebugEnabled())
{
jtaLogger.logger.debug(DebugLevel.CONSTRUCTORS,
VisibilityLevel.VIS_PUBLIC,
com.arjuna.ats.jta.logging.FacilityCode.FAC_JTA,
"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;
_heuristic = TwoPhaseOutcome.FINISH_OK;
_theTransaction = tx;
}
public void finalize()
{
if (_theTransaction != null)
{
_theTransaction = null;
}
}
public final Xid getXid()
{
return _tranID;
}
public Uid order()
{
if (_theXAResource instanceof StartXAResource)
return START_XARESOURCE;
else
{
if (_theXAResource instanceof EndXAResource)
return END_XARESOURCE;
else
return super.order();
}
}
public boolean propagateOnCommit()
{
return false; // cannot ever be nested!
}
public int typeIs()
{
return RecordType.JTA_RECORD;
}
public ClassName className()
{
return new ClassName("RecordType.JTA_RECORD");
}
public Object value()
{
return _theXAResource;
}
/**
* @message com.arjuna.ats.internal.jta.resources.arjunacore.setvalue
* [com.arjuna.ats.internal.jta.resources.arjunacore.setvalue] {0}
* called illegally.
*/
public void setValue(Object o)
{
if (jtaLogger.loggerI18N.isWarnEnabled())
{
jtaLogger.loggerI18N
.warn(
"com.arjuna.ats.internal.jta.resources.arjunacore.setvalue",
new Object[]
{ "XAResourceRecord::set_value()" });
}
}
public int nestedAbort()
{
return TwoPhaseOutcome.FINISH_OK;
}
public int nestedCommit()
{
return TwoPhaseOutcome.FINISH_OK;
}
/*
* XA is not subtransaction aware.
*/
public int nestedPrepare()
{
return TwoPhaseOutcome.PREPARE_OK; // do nothing
}
/**
* @message com.arjuna.ats.internal.jta.resources.arjunacore.preparenulltx
* [com.arjuna.ats.internal.jta.resources.arjunacore.preparenulltx]
* {0} - null transaction!
* @message com.arjuna.ats.internal.jta.resources.arjunacore.preparefailed
* [com.arjuna.ats.internal.jta.resources.arjunacore.preparefailed]
* {0} - prepare failed with exception {1}
*/
public int topLevelPrepare()
{
if (jtaLogger.logger.isDebugEnabled())
{
jtaLogger.logger.debug(DebugLevel.FUNCTIONS,
VisibilityLevel.VIS_PUBLIC,
com.arjuna.ats.jta.logging.FacilityCode.FAC_JTA,
"XAResourceRecord.topLevelPrepare for " + _tranID);
}
if (!_valid || (_theXAResource == null))
{
removeConnection();
return TwoPhaseOutcome.PREPARE_READONLY;
}
if (_tranID == null)
{
if (jtaLogger.loggerI18N.isWarnEnabled())
{
jtaLogger.loggerI18N
.warn(
"com.arjuna.ats.internal.jta.resources.arjunacore.preparenulltx",
new Object[]
{ "XAResourceRecord.prepare" });
}
removeConnection();
return TwoPhaseOutcome.PREPARE_NOTOK;
}
try
{
if (endAssociation())
{
_theXAResource.end(_tranID, XAResource.TMSUCCESS);
}
_prepared = true;
if (_theXAResource.prepare(_tranID) == XAResource.XA_RDONLY)
{
return TwoPhaseOutcome.PREPARE_READONLY;
}
else
return TwoPhaseOutcome.PREPARE_OK;
}
catch (XAException e1)
{
if (jtaLogger.loggerI18N.isWarnEnabled())
{
jtaLogger.loggerI18N
.warn(
"com.arjuna.ats.internal.jta.resources.arjunacore.preparefailed",
new Object[]
{ "XAResourceRecord.prepare", XAHelper
.printXAErrorCode(e1) });
}
/*
* XA_RB*, XAER_RMERR, XAER_RMFAIL, XAER_NOTA, XAER_INVAL, or
* XAER_PROTO.
*/
if (_rollbackOptimization) // won't have rollback called on it
removeConnection();
if ((e1.errorCode == XAException.XAER_RMERR) || (e1.errorCode == XAException.XAER_RMFAIL))
return TwoPhaseOutcome.HEURISTIC_HAZARD;
return TwoPhaseOutcome.PREPARE_NOTOK;
}
catch (Exception e2)
{
if (jtaLogger.loggerI18N.isWarnEnabled())
{
jtaLogger.loggerI18N
.warn(
"com.arjuna.ats.internal.jta.resources.arjunacore.preparefailed",
new Object[]
{ "XAResourceRecord.prepare", e2 });
}
if (_rollbackOptimization) // won't have rollback called on it
removeConnection();
return TwoPhaseOutcome.PREPARE_NOTOK;
}
}
/**
* @message com.arjuna.ats.internal.jta.resources.arjunacore.rollbacknulltx
* [com.arjuna.ats.internal.jta.resources.arjunacore.rollbacknulltx]
* {0} - null transaction!
* @message com.arjuna.ats.internal.jta.resources.arjunacore.rollbackxaerror
* [com.arjuna.ats.internal.jta.resources.arjunacore.rollbackxaerror]
* {0} - xa error {1}
*/
public int topLevelAbort()
{
if (jtaLogger.logger.isDebugEnabled())
{
jtaLogger.logger.debug(DebugLevel.FUNCTIONS,
VisibilityLevel.VIS_PUBLIC,
com.arjuna.ats.jta.logging.FacilityCode.FAC_JTA,
"XAResourceRecord.topLevelAbort for " + _tranID);
}
if (!_valid)
return TwoPhaseOutcome.FINISH_ERROR;
if (_theTransaction != null
&& _theTransaction.getXAResourceState(_theXAResource) == TxInfo.OPTIMIZED_ROLLBACK)
{
/*
* Already rolledback during delist.
*/
return TwoPhaseOutcome.FINISH_OK;
}
if (_tranID == null)
{
if (jtaLogger.loggerI18N.isWarnEnabled())
{
jtaLogger.loggerI18N
.warn(
"com.arjuna.ats.internal.jta.resources.arjunacore.rollbacknulltx",
new Object[]
{ "XAResourceRecord.rollback" });
}
return TwoPhaseOutcome.FINISH_ERROR;
}
else
{
if (_theXAResource != null)
{
if (_heuristic != TwoPhaseOutcome.FINISH_OK)
return _heuristic;
try
{
if (!_prepared)
{
if (endAssociation())
{
_theXAResource.end(_tranID, XAResource.TMSUCCESS);
}
}
_theXAResource.rollback(_tranID);
}
catch (XAException e1)
{
if (notAProblem(e1, false))
{
// some other thread got there first (probably)
}
else
{
if (jtaLogger.loggerI18N.isWarnEnabled())
{
jtaLogger.loggerI18N
.warn(
"com.arjuna.ats.internal.jta.resources.arjunacore.rollbackxaerror",
new Object[]
{
"XAResourceRecord.rollback",
XAHelper
.printXAErrorCode(e1) });
}
switch (e1.errorCode)
{
case XAException.XAER_RMERR:
if (!_prepared)
break; // just do the finally block
case XAException.XA_HEURHAZ:
return TwoPhaseOutcome.HEURISTIC_HAZARD;
case XAException.XA_HEURCOM:
return TwoPhaseOutcome.HEURISTIC_COMMIT;
case XAException.XA_HEURMIX:
return TwoPhaseOutcome.HEURISTIC_MIXED;
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:
break;
default:
return TwoPhaseOutcome.FINISH_ERROR;
}
}
}
catch (Exception e2)
{
e2.printStackTrace();
return TwoPhaseOutcome.FINISH_ERROR;
}
finally
{
if (!_prepared)
removeConnection();
}
}
else
return TwoPhaseOutcome.FINISH_ERROR;
}
return TwoPhaseOutcome.FINISH_OK;
}
/**
* @message com.arjuna.ats.internal.jta.resources.arjunacore.commitnulltx
* [com.arjuna.ats.internal.jta.resources.arjunacore.commitnulltx]
* {0} - null transaction!
* @message com.arjuna.ats.internal.jta.resources.arjunacore.commitxaerror
* [com.arjuna.ats.internal.jta.resources.arjunacore.commitxaerror]
* {0} - xa error {1}
*/
public int topLevelCommit()
{
if (jtaLogger.logger.isDebugEnabled())
{
jtaLogger.logger.debug(DebugLevel.FUNCTIONS,
VisibilityLevel.VIS_PUBLIC,
com.arjuna.ats.jta.logging.FacilityCode.FAC_JTA,
"XAResourceRecord.topLevelCommit for " + _tranID);
}
if (!_prepared)
return TwoPhaseOutcome.NOT_PREPARED;
if (_tranID == null)
{
if (jtaLogger.loggerI18N.isWarnEnabled())
{
jtaLogger.loggerI18N
.warn(
"com.arjuna.ats.internal.jta.resources.arjunacore.commitnulltx",
new Object[]
{ "XAResourceRecord.commit" });
}
return TwoPhaseOutcome.FINISH_ERROR;
}
else
{
if (_theXAResource != null)
{
if (_heuristic != TwoPhaseOutcome.FINISH_OK)
return _heuristic;
try
{
_theXAResource.commit(_tranID, false);
}
catch (XAException e1)
{
if (notAProblem(e1, true))
{
// some other thread got there first (probably)
}
else
{
if (jtaLogger.loggerI18N.isWarnEnabled())
{
jtaLogger.loggerI18N
.warn(
"com.arjuna.ats.internal.jta.resources.arjunacore.commitxaerror",
new Object[]
{
"XAResourceRecord.commit",
XAHelper
.printXAErrorCode(e1) });
}
/*
* 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:
return TwoPhaseOutcome.HEURISTIC_HAZARD;
case XAException.XA_HEURCOM: // what about forget? OTS doesn't support this code here.
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:
return TwoPhaseOutcome.HEURISTIC_ROLLBACK;
case XAException.XA_HEURMIX:
return TwoPhaseOutcome.HEURISTIC_MIXED;
case XAException.XAER_NOTA:
case XAException.XAER_PROTO:
break;
case XAException.XA_RETRY:
return TwoPhaseOutcome.FINISH_ERROR;
case XAException.XAER_INVAL:
case XAException.XAER_RMFAIL: // resource manager
// failed, did it
// rollback?
return TwoPhaseOutcome.HEURISTIC_HAZARD;
default:
return TwoPhaseOutcome.HEURISTIC_HAZARD;
}
}
}
catch (Exception e2)
{
return TwoPhaseOutcome.FINISH_ERROR;
}
finally
{
removeConnection();
}
}
else
return TwoPhaseOutcome.FINISH_ERROR;
}
return TwoPhaseOutcome.FINISH_OK;
}
/**
* 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);
}
public int nestedOnePhaseCommit()
{
return TwoPhaseOutcome.FINISH_ERROR;
}
/**
* 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.
*
* @message com.arjuna.ats.internal.jta.resources.arjunacore.opcnulltx
* [com.arjuna.ats.internal.jta.resources.arjunacore.opcnulltx] {0} -
* null transaction!
* @message com.arjuna.ats.internal.jta.resources.arjunacore.opcerror
* [com.arjuna.ats.internal.jta.resources.arjunacore.opcerror] {0}
* caught: {1}
*/
public int topLevelOnePhaseCommit()
{
if (jtaLogger.logger.isDebugEnabled())
{
jtaLogger.logger.debug(DebugLevel.FUNCTIONS,
VisibilityLevel.VIS_PUBLIC,
com.arjuna.ats.jta.logging.FacilityCode.FAC_JTA,
"XAResourceRecord.topLevelOnePhaseCommit for " + _tranID);
}
if (_tranID == null)
{
if (jtaLogger.loggerI18N.isWarnEnabled())
{
jtaLogger.loggerI18N
.warn(
"com.arjuna.ats.internal.jta.resources.arjunacore.opcnulltx",
new Object[]
{ "XAResourceRecord.1pc" });
}
return TwoPhaseOutcome.FINISH_ERROR;
}
else
{
if (_theXAResource != null)
{
if (_heuristic != TwoPhaseOutcome.FINISH_OK)
return _heuristic;
try
{
/*
* TODO in Oracle the end is not needed. Is this common
* across other RMs?
*/
if (endAssociation())
{
_theXAResource.end(_tranID, XAResource.TMSUCCESS);
}
_theXAResource.commit(_tranID, true);
}
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))
{
return TwoPhaseOutcome.FINISH_ERROR;
}
switch (e1.errorCode)
{
case XAException.XA_HEURHAZ:
case XAException.XA_HEURMIX:
return TwoPhaseOutcome.HEURISTIC_HAZARD;
case XAException.XA_HEURCOM:
forget() ;
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:
forget() ;
return TwoPhaseOutcome.FINISH_ERROR;
case XAException.XAER_NOTA:
case XAException.XAER_PROTO:
break;
case XAException.XAER_INVAL:
case XAException.XAER_RMFAIL: // resource manager failed,
// did it rollback?
return TwoPhaseOutcome.FINISH_ERROR;
// return TwoPhaseOutcome.HEURISTIC_HAZARD;
default:
return TwoPhaseOutcome.FINISH_ERROR;
// return TwoPhaseOutcome.HEURISTIC_ROLLBACK;
}
}
catch (Exception e2)
{
if (jtaLogger.loggerI18N.isWarnEnabled())
{
jtaLogger.loggerI18N
.warn(
"com.arjuna.ats.internal.jta.resources.arjunacore.opcerror",
new Object[]
{ "XAResourceRecord.commit_one_phase",
e2 });
}
return TwoPhaseOutcome.FINISH_ERROR;
}
finally
{
removeConnection();
}
}
else
return TwoPhaseOutcome.FINISH_ERROR;
}
return TwoPhaseOutcome.FINISH_OK;
}
public boolean forgetHeuristic()
{
if (jtaLogger.logger.isDebugEnabled())
{
jtaLogger.logger.debug(DebugLevel.FUNCTIONS,
VisibilityLevel.VIS_PUBLIC,
com.arjuna.ats.jta.logging.FacilityCode.FAC_JTA,
"XAResourceRecord.forget for " + _tranID);
}
forget() ;
removeConnection();
return true;
}
private void forget()
{
if ((_theXAResource != null) && (_tranID != null))
{
try
{
_theXAResource.forget(_tranID);
}
catch (Exception e)
{
}
}
}
/*
* Independant recovery cannot occur. Must be driven by the recovery of the
* local transaction, i.e., top-down recovery.
*/
protected int recover()
{
if (jtaLogger.logger.isDebugEnabled())
{
jtaLogger.logger.debug(DebugLevel.FUNCTIONS,
VisibilityLevel.VIS_PROTECTED,
com.arjuna.ats.jta.logging.FacilityCode.FAC_JTA,
"XAResourceRecord.recover");
}
return XARecoveryResource.FAILED_TO_RECOVER;
}
public static AbstractRecord create()
{
return new XAResourceRecord();
}
public static void remove(AbstractRecord toDelete)
{
toDelete = null;
}
/**
* @message com.arjuna.ats.internal.jta.resources.arjunacore.savestate
* [com.arjuna.ats.internal.jta.resources.arjunacore.savestate]
* Could not serialize a Serializable XAResource!
*/
public boolean save_state(OutputObjectState os, int t)
{
boolean res = false;
try
{
os.packInt(_heuristic);
/*
* 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);
boolean shouldSerialize = false;
try
{
if (_theXAResource instanceof Serializable)
shouldSerialize = true;
ByteArrayOutputStream s = new ByteArrayOutputStream();
ObjectOutputStream o = new ObjectOutputStream(s);
// don't allow the packing of a null resource
if (_theXAResource == null)
throw new NotSerializableException();
o.writeObject(_theXAResource);
o.close();
os.packBoolean(true);
os.packBytes(s.toByteArray());
}
catch (NotSerializableException ex)
{
if (!shouldSerialize)
{
// have to rely upon XAResource.recover!
os.packBoolean(false);
}
else
{
if (jtaLogger.loggerI18N.isWarnEnabled())
{
jtaLogger.loggerI18N.warn("com.arjuna.ats.internal.jta.resources.arjunacore.savestate");
}
}
}
}
else
{
os.packInt(RecoverableXAConnection.AUTO_RECOVERY);
os.packString(_recoveryObject.getClass().getName());
_recoveryObject.packInto(os);
}
res = true;
}
catch (Exception e)
{
e.printStackTrace();
res = false;
}
if (res)
res = res && super.save_state(os, t);
return res;
}
/**
* @message com.arjuna.ats.internal.jta.resources.arjunacore.restorestate
* [com.arjuna.ats.internal.jta.resources.arjunacore.restorestate]
* Exception on attempting to restore XAResource
* @message com.arjuna.ats.internal.jta.resources.arjunacore.norecoveryxa
* [com.arjuna.ats.internal.jta.resources.arjunacore.norecoveryxa]
* Could not find new XAResource to use for recovering non-serializable XAResource {0}
*/
public boolean restore_state(InputObjectState os, int t)
{
boolean res = false;
try
{
_heuristic = os.unpackInt();
_tranID = XidImple.unpack(os);
_theXAResource = null;
_recoveryObject = null;
if (os.unpackInt() == RecoverableXAConnection.OBJECT_RECOVERY)
{
boolean haveXAResource = os.unpackBoolean();
if (haveXAResource)
{
try
{
byte[] b = os.unpackBytes();
ByteArrayInputStream s = new ByteArrayInputStream(b);
ObjectInputStream o = new ObjectInputStream(s);
_theXAResource = (XAResource) o.readObject();
o.close();
if (jtaLogger.logger.isDebugEnabled())
{
jtaLogger.logger
.debug(
DebugLevel.FUNCTIONS,
VisibilityLevel.VIS_PUBLIC,
com.arjuna.ats.jta.logging.FacilityCode.FAC_JTA,
"XAResourceRecord.restore_state - XAResource de-serialized");
}
}
catch (Exception ex)
{
// not serializable in the first place!
if (jtaLogger.loggerI18N.isWarnEnabled())
{
jtaLogger.loggerI18N.warn("com.arjuna.ats.internal.jta.resources.arjunacore.restorestate",
ex);
}
return false;
}
}
else
{
/*
* Lookup new XAResource via XARecoveryModule if possible.
*/
_theXAResource = getNewXAResource();
if (_theXAResource == null)
{
jtaLogger.loggerI18N.warn("com.arjuna.ats.internal.jta.resources.arjunacore.norecoveryxa",
new Object[]{_tranID});
}
}
}
else
{
String creatorName = os.unpackString();
Class c = Thread.currentThread().getContextClassLoader()
.loadClass(creatorName);
_recoveryObject = (RecoverableXAConnection) c.newInstance();
_recoveryObject.unpackFrom(os);
_theXAResource = _recoveryObject.getResource();
if (jtaLogger.logger.isDebugEnabled())
{
jtaLogger.logger.debug(DebugLevel.FUNCTIONS,
VisibilityLevel.VIS_PUBLIC,
com.arjuna.ats.jta.logging.FacilityCode.FAC_JTA,
"XAResourceRecord.restore_state - XAResource got from "
+ creatorName);
}
}
res = true;
}
catch (Exception e)
{
e.printStackTrace();
res = false;
}
if (res)
res = res && super.restore_state(os, t);
return res;
}
public String type()
{
return XAResourceRecord.typeName();
}
public static String typeName()
{
return "/StateManager/AbstractRecord/XAResourceRecord";
}
public boolean doSave()
{
return true;
}
public void merge(AbstractRecord a)
{
}
public void alter(AbstractRecord a)
{
}
public boolean shouldAdd(AbstractRecord a)
{
return false;
}
public boolean shouldAlter(AbstractRecord a)
{
return false;
}
public boolean shouldMerge(AbstractRecord a)
{
return false;
}
public boolean shouldReplace(AbstractRecord a)
{
return false;
}
protected XAResourceRecord()
{
super();
_theXAResource = null;
_recoveryObject = null;
_tranID = null;
_prepared = true;
_heuristic = TwoPhaseOutcome.FINISH_OK;
_valid = true;
_theTransaction = null;
}
protected XAResourceRecord(Uid u)
{
super(u, null, ObjectType.ANDPERSISTENT);
_theXAResource = null;
_recoveryObject = null;
_tranID = null;
_prepared = true;
_heuristic = TwoPhaseOutcome.FINISH_OK;
_valid = true;
_theTransaction = null;
}
/**
* For those objects where the original XAResource could not be saved.
*/
protected synchronized void setXAResource(XAResource res)
{
_theXAResource = res;
}
/**
* This routine finds the new XAResource for the transaction that used the
* old resource we couldn't serialize. It does this by looking up the
* XARecoveryModule in the recovery manager and asking it for the
* XAResource. The recovery manager will then look through its list of
* registered XARecoveryResource implementations for the appropriate
* instance. If the XARecoveryModule hasn't been initialised yet then this
* routine will fail, but on the next scan it should work.
*/
private final XAResource getNewXAResource()
{
RecoveryManager recMan = RecoveryManager.manager();
Vector recoveryModules = recMan.getModules();
if (recoveryModules != null)
{
Enumeration modules = recoveryModules.elements();
while (modules.hasMoreElements())
{
RecoveryModule m = (RecoveryModule) modules.nextElement();
if (m instanceof XARecoveryModule)
{
return ((XARecoveryModule) m).getNewXAResource(_tranID);
}
}
}
return null;
}
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;
}
/*
* 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 boolean endAssociation()
{
boolean doEnd = true;
if (_theTransaction != null)
{
if (_theTransaction.getXAResourceState(_theXAResource) == TxInfo.NOT_ASSOCIATED)
{
// end has been called so we don't need to do it again!
doEnd = false;
}
}
else
doEnd = false; // Recovery mode
return doEnd;
}
protected XAResource _theXAResource;
private RecoverableXAConnection _recoveryObject;
private Xid _tranID;
private boolean _prepared;
private boolean _valid;
private int _heuristic;
private TransactionImple _theTransaction;
private static boolean _rollbackOptimization = false;
static
{
String optimization = jtaPropertyManager.propertyManager.getProperty(
Environment.JTA_TM_IMPLEMENTATION, "OFF");
if (optimization.equals("ON"))
_rollbackOptimization = true;
}
}