
com.sun.messaging.jmq.jmsclient.XAResourceForMC Maven / Gradle / Ivy
/*
* Copyright (c) 2000, 2020 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2022 Contributors to the Eclipse Foundation
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package com.sun.messaging.jmq.jmsclient;
import java.util.Hashtable;
import java.util.logging.Level;
import java.util.logging.Logger;
import jakarta.jms.JMSException;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import com.sun.messaging.jmq.util.DebugConverters;
import com.sun.messaging.jmq.util.JMQXid;
import com.sun.messaging.jmq.util.XidImpl;
import com.sun.messaging.jms.ra.api.JMSRAManagedConnection;
import com.sun.messaging.jms.ra.api.JMSRAResourceAdapter;
import com.sun.messaging.jms.ra.api.JMSRASessionAdapter;
import com.sun.messaging.jms.ra.api.JMSRAXASession;
import com.sun.messaging.jmq.io.Status;
import com.sun.messaging.jmq.jmsclient.resources.ClientResources;
/**
* JMQ implementation class for XAResource
*
*
* The XAResource interface is a Java mapping of the industry standard XA interface based on the X/Open CAE
* Specification (Distributed Transaction Processing: The XA Specification).
*
*
* The XA interface defines the contract between a Resource Manager and a Transaction Manager in a distributed
* transaction processing (DTP) environment. A JDBC driver or a JMS provider implements this interface to support the
* association between a global transaction and a database or message service connection.
*
*
* The XAResource interface can be supported by any transactional resource that is intended to be used by application
* programs in an environment where transactions are controlled by an external transaction manager. An example of such a
* resource is a database management system. An application may access data through multiple database connections. Each
* database connection is enlisted with the transaction manager as a transactional resource. The transaction manager
* obtains an XAResource for each connection participating in a global transaction. The transaction manager uses the
* start
method to associate the global transaction with the resource, and it uses the end
* method to disassociate the transaction from the resource. The resource manager is responsible for associating the
* global transaction to all work performed on its data between the start and end method invocations.
*
*
* At transaction commit time, the resource managers are informed by the transaction manager to prepare, commit, or
* rollback a transaction according to the two-phase commit protocol.
*
*
* @see javax.transaction.xa.XAResource
*/
public class XAResourceForMC extends XAResourceForX implements XAResource, XAResourceForJMQ {
// Id of the MC that this is associated with
private JMSRAManagedConnection mc;
protected boolean active = false;
// xaTable to store xa transaction state. key/value=xid/xaState
private static Hashtable xaTable = new Hashtable();
/* Loggers */
private static final String _lgrName = "com.sun.messaging.jmq.jmsclient.XAResourceForMC";
private static final Logger _logger = Logger.getLogger(_lgrName);
private static final String _lgrMIDPrefix = "MQJMSRA_XARMC";
private static final String _lgrMID_INF = _lgrMIDPrefix + "1101: ";
public XAResourceForMC(JMSRAManagedConnection mc, ConnectionImpl raConnection, ConnectionImpl epConnection) throws JMSException {
this.mc = mc;
id = mc.getMCId();
this.epConnection = epConnection;
if (raConnection == null) {
System.err.println("MQRA:XARFMC:constr:raConnectionNull:unable to acquire RMId:assuming distinct");
} else {
if (raConnection._isClosed()) {
System.err.println("MQRA:XARFMC:constr:raConnectionClosed:unable to acquire RMId:assuming distinct");
} else {
try {
raConnection.getProtocolHandler().generateUID();
// System.out.println("MQRA:XAR4MC:Constr:id="+id);
// Debug.println("*=*=*=*=*=*=*=*=*=*=XAR:new:RMId="+resourceManagerId);
} catch (Exception e) {
System.err.println("MQRA:XARFMC:constr:Exc aquiring RMId:assume distinct");
}
}
}
}
/**
* Commits the global transaction specified by xid.
*
* @param foreignXid A global transaction identifier
*
* @param onePhase If true, the resource manager should use a one-phase commit protocol to commit the work done on
* behalf of xid.
*
* @exception XAException An error has occurred. Possible XAExceptions are XA_HEURHAZ, XA_HEURCOM, XA_HEURRB,
* XA_HEURMIX, XAER_RMERR, XAER_RMFAIL, XAER_NOTA, XAER_INVAL, or XAER_PROTO.
*
*
* If the resource manager did not commit the transaction and the parameter onePhase is set to true, the resource
* manager may throw one of the XA_RB* exceptions. Upon return, the resource manager has rolled back the branch's work
* and has released all held resources.
*/
@Override
public synchronized void commit(Xid foreignXid, boolean onePhase) throws XAException {
if (_logger.isLoggable(Level.FINE)) {
_logger.fine(_lgrMID_INF + "XAResourceForMC (" + this.hashCode() + ") Commit " + printXid(foreignXid) + " (onePhase=" + onePhase + ")");
}
// convert to jmq xid
JMQXid jmqXid = new JMQXid(foreignXid);
// System.out.println("MQRA:XAR4MC:commit():mcId="+mc.getMCId()+":onePhase="+onePhase+" tid="+transactionID+"
// xid="+jmqXid.toString());
// Debug.println("MQRA:XAR4RA:commit():onePhase="+onePhase+" tid="+transactionID+" xid="+jmqXid.toString());
boolean checkrollback = false;
boolean rbrollback = false;
Exception rbrollbackex = null;
try {
if (!epConnection._isClosed()) {
if (onePhase) {
if (epConnection.isConnectedToHABroker()) {
HAOnePhaseCommit(foreignXid, jmqXid);
} else {
try {
epConnection.getProtocolHandler().commit(0L, XAResource.TMONEPHASE, jmqXid);
} catch (JMSException e) {
checkrollback = true;
throw e;
}
}
} else {
if (epConnection.isConnectedToHABroker()) {
this.HATwoPhaseCommit(jmqXid);
} else {
epConnection.getProtocolHandler().commit(0L, XAResource.TMNOFLAGS, jmqXid);
}
}
JMSRASessionAdapter sa = mc.getConnectionAdapter().getJMSRASessionAdapter();
if (sa != null) {
// System.out.println("MQRA:XAR4MC:commit:sa!=null");
sa.getJMSRAXASession().finishXATransactionForMC();
}
active = false;
} else {
System.err.println("MQRA:XARFMC:commit:ConnectionClosed:throw XAExc txn:1p=" + onePhase + ":xid=" + jmqXid.toString());
// Debug.println("*=*=*=*=*=*=*=*=*=*=XAR:commit:XAException");
XAException xae = new XAException(XAException.XAER_RMFAIL);
throw xae;
}
} catch (XAException xaexception) {
throw xaexception;
} catch (Exception jmse) {
System.err.println("MQRA:XARFMC:commit:XAException-Exception=" + jmse.getMessage());
Debug.printStackTrace(jmse);
if (jmse instanceof RemoteAcknowledgeException) {
if (checkrollback) {
rbrollbackex = jmse;
rbrollback = true;
}
}
if (!rbrollback && checkrollback && (jmse instanceof JMSException)) {
if (((JMSException) jmse).getErrorCode().equals(ClientResources.X_SERVER_ERROR)) {
Exception e1 = ((JMSException) jmse).getLinkedException();
if (e1 instanceof JMSException && !((JMSException) e1).getErrorCode().equals(Status.getString(Status.NOT_FOUND))) {
SessionImpl.sessionLogger.log(Level.WARNING, "Exception on 1-phase commit transaction " + jmqXid + ", will rollback", jmse);
rbrollbackex = jmse;
rbrollback = true;
}
}
}
if (!rbrollback) {
XAException xae = new XAException(XAException.XAER_RMFAIL);
xae.initCause(jmse);
throw xae;
}
} finally {
if (!rbrollback) {
// finish up this resource and any others joined to it in this transaction
boolean throwExceptionIfNotFound = false;
XidImpl savedXid = this.jmqXid;
XAResourceForJMQ[] resources = XAResourceMapForRAMC.getXAResources(this.jmqXid, throwExceptionIfNotFound);
for (int i = 0; i < resources.length; i++) {
XAResourceForJMQ xari = resources[i];
try {
xari.clearTransactionInfo();
} catch (JMSException jmse) {
System.err.println("MQRA:XARFMC:commit:XAException-Exception=" + jmse.getMessage());
Debug.printStackTrace(jmse);
XAException xae = new XAException(XAException.XAER_RMFAIL);
xae.initCause(jmse);
throw xae;
}
}
XAResourceMapForRAMC.unregister(savedXid);
}
}
if (!rbrollback) {
this.removeXid(jmqXid);
return;
}
XAException xae;
try {
rollback(foreignXid, XAResourceMap.MAXROLLBACKS, XAResourceMap.DMQ_ON_MAXROLLBACKS);
xae = new XAException(XAException.XA_RBROLLBACK);
xae.initCause(rbrollbackex);
} catch (Exception e) {
SessionImpl.sessionLogger.log(Level.SEVERE, "Exception on rollback transaction " + jmqXid + " after 1-phase-commit failure", e);
xae = new XAException(XAException.XAER_RMFAIL);
xae.initCause(rbrollbackex);
}
throw xae;
}
private void HATwoPhaseCommit(JMQXid jmqXid) throws JMSException, XAException {
// if (this.twoPhasePrepared == false) {
// throw new XAException (XAException.XAER_PROTO);
// }
try {
epConnection.getProtocolHandler().commit(0L, XAResource.TMNOFLAGS, jmqXid);
} catch (JMSException jmse) {
if (this.isXATracking()) {
Integer ts = (Integer) xaTable.get(jmqXid);
// we must gaurantee that the transaction was indeed in prepared state.
if (ts != null && ts.intValue() == (Transaction.TRANSACTION_PREPARED)) {
this.checkCommitStatus(jmse, Transaction.TRANSACTION_PREPARED, jmqXid, false);
}
} else {
// propagate the exception
throw jmse;
}
}
// transaction has successfully committed
// this.twoPhasePrepared = false;
this.removeXid(jmqXid);
}
/**
* check prepared status
*
* @param jmqXid --
* @throws XAException if the transaction is not in prepared state.
*/
private void checkPrepareStatus(XAException xae, JMQXid jmqXid) throws XAException {
if (epConnection.imqReconnect == false) {
throw xae;
}
try {
SessionImpl.yield();
epConnection.checkReconnecting(null);
if (epConnection.isCloseCalled || epConnection.connectionIsBroken) {
throw xae;
}
// check failover broker to see the status
verifyPrepare(jmqXid);
} catch (XAException xae2) {
// if any xaexception we simply propagate up
throw xae2;
} catch (Exception e) {
// for any other exceptions, throws an XAException.
XAException xae3 = new XAException(XAException.XAER_RMFAIL);
xae3.initCause(e);
throw xae3;
}
}
private void verifyPrepare(JMQXid jmqXid) throws XAException, JMSException {
SessionImpl.sessionLogger.log(Level.INFO, "XA verifyPrepare(), jmqXid: " + jmqXid);
int state = epConnection.protocolHandler.verifyHATransaction(0L, Transaction.TRANSACTION_ENDED, jmqXid);
switch (state) {
case 6:
// transaction is in prepared state
SessionImpl.sessionLogger.log(Level.INFO, "transaction in prepared state: " + this.transactionID);
return;
case 8:
case 9:
default:
// for the rest of the state, the transaction was rolled back
// by the broker.
XAException xae = new XAException(XAException.XA_RBROLLBACK);
throw xae;
}
}
@Override
void checkCommitStatus(Exception cause, int tstate, JMQXid jmqXid, boolean onePhase) throws JMSException, XAException {
try {
doCheckCommitStatus(cause, tstate, jmqXid, onePhase);
} catch (Exception ex) {
// throw the original type of exception.
if (ex instanceof JMSException) {
throw (JMSException) ex;
} else if (ex instanceof XAException) {
throw (XAException) ex;
} else {
XAException xae = new XAException(XAException.XAER_RMFAIL);
xae.initCause(ex);
throw xae;
}
}
}
private void doCheckCommitStatus(Exception ex, int tstate, JMQXid jmqXid, boolean onePhase) throws Exception {
if (epConnection.imqReconnect == false) {
throw ex;
}
SessionImpl.yield();
epConnection.checkReconnecting(null);
if (epConnection.isCloseCalled || epConnection.connectionIsBroken) {
throw ex;
}
verifyTransaction(tstate, jmqXid, onePhase);
}
private void verifyTransaction(int tstate, JMQXid jmqXid, boolean onePhase) throws JMSException, XAException {
int state = epConnection.protocolHandler.verifyHATransaction(0L, tstate, jmqXid);
switch (state) {
case 7:
// committed
return;
case 6:
// transaction is in prepared state. ask broker to commit.
try {
// protocolHandler.rollback(this.transactionID);
SessionImpl.sessionLogger.log(Level.INFO,
"XA verifyTransaction(): transaction is in prepred state, committing the transaction: " + this.transactionID);
// epConnection.protocolHandler.commitHATransaction(this.transactionID);
epConnection.getProtocolHandler().commit(0L, XAResource.TMNOFLAGS, jmqXid, onePhase);
SessionImpl.sessionLogger.log(Level.INFO, "XA verifyTransaction(): prepared transaction committed successfully: " + this.transactionID);
// done if we can commit.
return;
} catch (JMSException jmse) {
// in case this failed, we try one more time.
// This is the third failure at this point.
// If failed again, we log this and throw Exception.
SessionImpl.yield();
epConnection.checkReconnecting(null);
// if ( epConnection.isCloseCalled || epConnection.connectionIsBroken) {
throw jmse;
// }
// we should rollback at this point.
// epConnection.protocolHandler.rollback(this.transactionID);
}
case 8:
case 9:
default:
// for the rest of the state, the transaction was rolled back
// by the broker.
// String errorString = AdministeredObject.cr.getKString(
// AdministeredObject.cr.X_TRANSACTION_FAILOVER_OCCURRED);
// TransactionRolledBackException tre =
// new TransactionRolledBackException(errorString,
// AdministeredObject.cr.
// X_TRANSACTION_FAILOVER_OCCURRED);
// throwRollbackException(tre);
XAException xae = new XAException(XAException.XA_RBROLLBACK);
throw xae;
}
}
/**
* Ends the work performed on behalf of a transaction branch. The resource manager disassociates the XA resource from
* the transaction branch specified and lets the transaction complete.
*
*
* If TMSUSPEND is specified in the flags, the transaction branch is temporarily suspended in an incomplete state. The
* transaction context is in a suspended state and must be resumed via the start
method with TMRESUME
* specified.
*
*
*
* If TMFAIL is specified, the portion of work has failed. The resource manager may mark the transaction as
* rollback-only
*
*
*
* If TMSUCCESS is specified, the portion of work has completed successfully.
*
*
* @param foreignXid A global transaction identifier that is the same as the identifier used previously in the
* start
method.
*
* @param flags One of TMSUCCESS, TMFAIL, or TMSUSPEND.
*
* @exception XAException An error has occurred. Possible XAException values are XAER_RMERR, XAER_RMFAILED, XAER_NOTA,
* XAER_INVAL, XAER_PROTO, or XA_RB*.
*/
@Override
public synchronized void end(Xid foreignXid, int flags) throws XAException {
if (_logger.isLoggable(Level.FINE)) {
_logger.fine(_lgrMID_INF + "XAResourceForMC (" + this.hashCode() + ") End " + printXid(foreignXid) + printFlags(flags));
}
// convert to jmq xid
JMQXid jmqXid = new JMQXid(foreignXid);
// System.out.println("MQRA:XAR4MC:end():mcId="+mc.getMCId()+":flags="+flags+" xid="+jmqXid.toString());
// Debug.println("MQRA:XAR4RA:end():flags="+flags+" tid="+transactionID+" xid="+jmqXid.toString());
// update the resource state
if (isFail(flags)) {
resourceState = FAILED;
} else if (isSuspend(flags)) {
resourceState = INCOMPLETE;
} else {
resourceState = COMPLETE;
}
if (JMSRAResourceAdapter.isRevert6882044()) {
// revert to pre-6882044 behaviour and forward all events to broker
sendEndToBroker(flags, false, jmqXid);
} else {
// now decide based on the resource state whether to send a real
// END packet, a noop END packet or to ignore the END packet altogether.
if (resourceState == COMPLETE) {
// This XAResource is complete. Send a real END packet if all
// other resources joined to this txn are complete, otherwise
// send a noop END packet to ensure that work associated with
// this XAResource has completed on the broker. See bug 12364646.
boolean allComplete = true;
XAResourceForJMQ[] resources = XAResourceMapForRAMC.getXAResources(jmqXid, true);
for (int i = 0; i < resources.length; i++) {
XAResourceForJMQ xari = resources[i];
if (!xari.isComplete()) {
allComplete = false;
}
}
if (allComplete) {
// All resources complete. Send real END packet.
sendEndToBroker(flags, false, jmqXid);
} else {
// One or more resources are not complete. Send a noop END
// packet to the broker.
sendEndToBroker(flags, true, jmqXid);
}
} else if (resourceState == FAILED) {
// This resource has failed. Send a real END packet regardless
// of the state of any other joined resources.
sendEndToBroker(flags, false, jmqXid);
} else if (resourceState == INCOMPLETE) {//NOPMD
// Don't send the END to the broker. See Glassfish issue 7118.
}
}
started = false;
if (isXATracking()) {
xaTable.put(jmqXid, XAResourceForRA.XA_END);
}
}
private void sendEndToBroker(int flags, boolean jmqnoop, JMQXid jmqXid) throws XAException {
try {
// System.out.println("MQRA:XAR4RA:end:sending 0L:tid="+transactionID+" xid="+jmqXid.toString());
epConnection.protocolHandler.endTransaction(0L, jmqnoop, flags, jmqXid);
JMSRASessionAdapter sa = mc.getConnectionAdapter().getJMSRASessionAdapter();
if (sa != null) {
// System.out.println("MQRA:XAR4MC:end:sa!=null");
sa.getJMSRAXASession().finishXATransactionForMC();
}
} catch (Exception jmse) {
System.err.println("MQRA:XARFMC:end:XAException-Exception=" + jmse.getMessage());
// Debug.println("*=*=*=*=*=*=*=*=*=*=XAR:end:XAException");
Debug.printStackTrace(jmse);
XAException xae = new XAException(XAException.XAER_RMFAIL);
xae.initCause(jmse);
throw xae;
}
}
/**
* Tells the resource manager to forget about a heuristically completed transaction branch.
*
* @param foreignXid A global transaction identifier.
*
* @exception XAException An error has occurred. Possible exception values are XAER_RMERR, XAER_RMFAIL, XAER_NOTA,
* XAER_INVAL, or XAER_PROTO.
*/
@Override
public void forget(Xid foreignXid) throws XAException {
// MQ does not support heuristically completed transaction branches
// This is a NOP
// convert to jmq xid
////// JMQXid jmqXid = new JMQXid(foreignXid);
// Debug.println("*=*=*=*=*=*=*=*=*=*=XAR:forget:txid=\n"+jmqXid.toString());
XidImpl xidToForget = new XidImpl(foreignXid);
XAResourceMapForRAMC.unregister(xidToForget);
if (jmqXid != null) {
if (jmqXid.equals(xidToForget)) {
try {
clearTransactionInfo();
} catch (JMSException jmse) {
System.err.println("MQRA:XARFMC:forget:XAException-Exception=" + jmse.getMessage());
Debug.printStackTrace(jmse);
XAException xae = new XAException(XAException.XAER_RMFAIL);
xae.initCause(jmse);
throw xae;
}
}
}
}
/**
* Obtains the current transaction timeout value set for this XAResource instance. If
* XAResource.setTransactionTimeout
was not used prior to invoking this method, the return value is the
* default timeout set for the resource manager; otherwise, the value used in the previous
* setTransactionTimeout
call is returned.
*
* @return the transaction timeout value in seconds.
*
* @exception XAException An error has occurred. Possible exception values are XAER_RMERR and XAER_RMFAIL.
*/
@Override
public int getTransactionTimeout() throws XAException {
// Debug.println("*=*=*=*=*=*=*=*=*=*=XAR:getTransactionTimeout");
return transactionTimeout;
}
/**
* This method is called to determine if the resource manager instance represented by the target object is the same as
* the resource manager instance represented by the parameter foreignXaRes.
*
* @param foreignXaRes An XAResource object whose resource manager instance is to be compared with the resource manager
* instance of the target object.
*
* @return true if it's the same RM instance; otherwise false.
*
* @exception XAException An error has occurred. Possible exception values are XAER_RMERR and XAER_RMFAIL.
*
*/
@Override
public boolean isSameRM(XAResource foreignXaRes) throws XAException {
// don't allow a XAResourceImpl to be joined to a XAResourceForMC or a XAResourceForRA
// (even though they all implement XAResourceForJMQ)
// as this would imply joining of JMSJCA and JMSRA resources, which has not been tested
if ((foreignXaRes instanceof XAResourceForMC) || (foreignXaRes instanceof XAResourceForRA)) {
return isSameJMQRM((XAResourceForJMQ) foreignXaRes);
} else {
return false;
}
}
/**
* Return whether this XAResourceForJMQ and the specified XAResourceForJMQ represent the same resource manager instance.
*
* This is determined by checking whether the two resources have the same brokerSessionID
*
* @param xaResource XAResourceForJMQ
* @return true if same RM instance, otherwise false.
*/
@Override
public boolean isSameJMQRM(XAResourceForJMQ xaResource) {
boolean result;
if (JMSRAResourceAdapter.isSameRMAllowed()) {
if ((getBrokerSessionID() != 0) && (getBrokerSessionID() == xaResource.getBrokerSessionID())) {
result = true;
} else {
result = false;
}
} else {
result = false;
}
return result;
}
/**
* Return the brokerSessionID of this object's connection
*/
@Override
public long getBrokerSessionID() {
return this.epConnection.getBrokerSessionID();
}
/**
* two-phase commit prepare for HA.
*
*
*/
@Override
public synchronized int prepare(Xid foreignXid) throws XAException {
// result code
int result = XA_OK;
// this.twoPhasePrepared = false;
try {
// two phase commit
this.prepare(foreignXid, false);
} catch (XAException xae) {
if (this.epConnection.isConnectedToHABroker) {
this.checkPrepareStatus(xae, jmqXid);
} else {
// non HA -- propagate exception
throw xae;
}
}
// set to true so that in case we need to verify transaction later.
// this.twoPhasePrepared = true;
if (isXATracking()) {
xaTable.put(jmqXid, XAResourceForRA.XA_PREPARE);
}
return result;
}
/**
* Ask the resource manager to prepare for a transaction commit of the transaction specified in xid.
*
* @param foreignXid A global transaction identifier.
*
* @exception XAException An error has occurred. Possible exception values are: XA_RB*, XAER_RMERR, XAER_RMFAIL,
* XAER_NOTA, XAER_INVAL, or XAER_PROTO.
*
* @return A value indicating the resource manager's vote on the outcome of the transaction. The possible values are:
* XA_RDONLY or XA_OK. If the resource manager wants to roll back the transaction, it should do so by raising an
* appropriate XAException in the prepare method.
*/
@Override
synchronized int prepare(Xid foreignXid, boolean onePhase) throws XAException {
if (_logger.isLoggable(Level.FINE)) {
_logger.fine(_lgrMID_INF + "XAResourceForMC (" + this.hashCode() + ") Prepare " + printXid(foreignXid));
}
// JMS does not do RDONLY transactions - right?
int result = XA_OK;
// convert to jmq xid
JMQXid jmqXid = new JMQXid(foreignXid);
// System.out.println("MQRA:XAR4MC:prepare():mcId="+mc.getMCId()+":xid="+jmqXid.toString());
// Debug.println("*=*=*=*=*=*=*=*=*=*=XAR:prepare:txid=\n"+jmqXid.toString());
try {
if (!epConnection._isClosed()) {
epConnection.getProtocolHandler().prepare(0L, jmqXid, onePhase);
} else {
System.err.println("MQRA:XARFMC:prepare:ConnectionClosed:Rollback txn:xid=" + jmqXid.toString());
// Debug.println("*=*=*=*=*=*=*=*=*=*=XAR:prepareXAException:RB");
XAException xae = new XAException(XAException.XAER_RMFAIL);
throw xae;
}
} catch (Exception jmse) {
System.err.println("MQRA:XARFMC:prepare:XAException-Exception=" + jmse.getMessage());
// Debug.println("*=*=*=*=*=*=*=*=*=*=XAR:prepareXAException");
Debug.printStackTrace(jmse);
XAException xae = new XAException(XAException.XAER_RMFAIL);
xae.initCause(jmse);
throw xae;
}
// update the resource state
resourceState = PREPARED;
return result;
}
/**
* Obtains a list of prepared transaction branches from a resource manager. The transaction manager calls this method
* during recovery to obtain the list of transaction branches that are currently in prepared or heuristically completed
* states.
*
* @param flags One of TMSTARTRSCAN, TMENDRSCAN, TMNOFLAGS. TMNOFLAGS must be used when no other flags are set in the
* parameter.
*
* @exception XAException An error has occurred. Possible values are XAER_RMERR, XAER_RMFAIL, XAER_INVAL, and
* XAER_PROTO.
*
* @return The resource manager returns zero or more XIDs of the transaction branches that are currently in a prepared
* or heuristically completed state. If an error occurs during the operation, the resource manager should throw the
* appropriate XAException.
*
*/
@Override
public Xid[] recover(int flags) throws XAException {
Xid[] result = null;
// Debug.println("*=*=*=*=*=*=*=*=*=*=XAR:recover:flags="+flags);
try {
if (!epConnection._isClosed()) {
result = epConnection.getProtocolHandler().recover(flags);
}
} catch (Exception jmse) {
System.err.println("MQRA:XARFMC:recover:XAException-Exception=" + jmse.getMessage());
// Debug.println("*=*=*=*=*=*=*=*=*=*=XAR:recoverXAException");
Debug.printStackTrace(jmse);
XAException xae = new XAException(XAException.XAER_RMFAIL);
xae.initCause(jmse);
throw xae;
}
return result;
}
/**
* Informs the resource manager to roll back work done on behalf of a transaction branch.
*
* @param foreignXid A global transaction identifier.
*
* @exception XAException An error has occurred.
*/
@Override
public synchronized void rollback(Xid foreignXid) throws XAException {
rollback(foreignXid, -1, false);
}
/**
*
* @param maxRollbacks maximum number of consecutive rollbacks allowed for active consumers
* @param dmqOnMaxRollbacks if true, place the consumed message on DMQ if maxRollbacks reached
*/
private synchronized void rollback(Xid foreignXid, int maxRollbacks, boolean dmqOnMaxRollbacks) throws XAException {
if (_logger.isLoggable(Level.FINE)) {
_logger.fine(_lgrMID_INF + "XAResourceForMC (" + this.hashCode() + ") Rollback " + printXid(foreignXid) + ")");
}
// convert to jmq xid
JMQXid jmqXid = new JMQXid(foreignXid);
// System.out.println("MQRA:XAR4MC:rollback():mcId="+mc.getMCId()+":xid="+jmqXid.toString());
// Debug.println("MQRA:XAR4RA:rollback():tid="+transactionID+" xid="+jmqXid.toString());
JMSRAXASession xas = null;
JMSRASessionAdapter sa = null;
try {
// send rollback w/redeliver
if (!epConnection._isClosed()) {
// System.out.println("MQRA:XAR4MC:rollback:Using epCon");
sa = mc.getConnectionAdapter().getJMSRASessionAdapter();
if (epConnection.isConnectedToHABroker()) {
// handle fail-over for HA connection
HARollback(jmqXid, maxRollbacks, dmqOnMaxRollbacks);
} else {
epConnection.getProtocolHandler().rollbackXA(0L, jmqXid, true, false, maxRollbacks, dmqOnMaxRollbacks);
}
if (sa != null) {
// System.out.println("MQRA:XAR4MC:rollback:sa!=null");
// sa.getXASession().finishXATransactionForMC();
sa.getJMSRAXASession().finishXATransactionForMC();
}
active = false;
} else {
System.err.println("MQRA:XARFMC:rollback:ConnectionClosed:Rollback txn:xid=" + jmqXid.toString());
// Debug.println("*=*=*=*=*=*=*=*=*=*=XAR:prepare:XAException:RMFAIL");
XAException xae = new XAException(XAException.XAER_RMFAIL);
throw xae;
}
} catch (Exception jmse) {
System.err.println("MQRA:XARFMC:rollback:XAException-Exception=" + jmse.getMessage());
// Debug.println("*=*=*=*=*=*=*=*=*=*=XAR:rollbackXAException");
Debug.printStackTrace(jmse);
XAException xae = new XAException(XAException.XAER_RMFAIL);
xae.initCause(jmse);
throw xae;
} finally {
if (sa != null) {
xas = sa.getJMSRAXASession();
if (xas != null) {
xas.setFailoverOccurred(false);
}
}
// finish up this resource and any others joined to it in this transaction
boolean throwExceptionIfNotFound = false;
XidImpl savedXid = this.jmqXid;
XAResourceForJMQ[] resources = XAResourceMapForRAMC.getXAResources(this.jmqXid, throwExceptionIfNotFound);
for (int i = 0; i < resources.length; i++) {
XAResourceForJMQ xari = resources[i];
try {
xari.clearTransactionInfo();
} catch (JMSException jmse) {
System.err.println("MQRA:XARFMC:rollback:XAException-Exception=" + jmse.getMessage());
Debug.printStackTrace(jmse);
XAException xae = new XAException(XAException.XAER_RMFAIL);
xae.initCause(jmse);
throw xae;
}
}
XAResourceMapForRAMC.unregister(savedXid);
}
this.removeXid(jmqXid);
}
private void HARollback(JMQXid jmqXid, int maxRollbacks, boolean dmqOnMaxRollbacks) throws JMSException, XAException {
try {
epConnection.getProtocolHandler().rollbackXA(0L, jmqXid, true, false, maxRollbacks, dmqOnMaxRollbacks);
} catch (JMSException jmse) {
// yield/pause
SessionImpl.yield();
// block until fail-over/re-connected/closed
this.epConnection.checkReconnecting(null);
// check if we still connected.
if (epConnection.isCloseCalled || epConnection.connectionIsBroken) {
throw jmse;
}
// re-send with I bit set to true
this.retryRollBack(jmqXid, maxRollbacks, dmqOnMaxRollbacks);
}
this.removeXid(jmqXid);
}
private void retryRollBack(JMQXid jmqXid, int maxRollbacks, boolean dmqOnMaxRollbacks) throws JMSException, XAException {
try {
epConnection.getProtocolHandler().rollbackXA(0L, jmqXid, true, true, maxRollbacks, dmqOnMaxRollbacks);
} catch (JMSException jmse) {
if (isXATracking()) {
Integer tstate = (Integer) xaTable.get(jmqXid);
if (tstate != null && tstate.intValue() != Transaction.TRANSACTION_PREPARED) {
// the transaction is not in prepared state, we log the info
// and continue.
ConnectionImpl.connectionLogger.log(Level.WARNING, jmse.toString());
} else {
throw jmse;
}
} else {
throw jmse;
}
}
}
/**
*
* Sets the current transaction timeout value for this XAResource
instance. Once set, this timeout value is
* effective until setTransactionTimeout
is invoked again with a different value. To reset the timeout
* value to the default value used by the resource manager, set the value to zero.
*
* If the timeout operation is performed successfully, the method returns true; otherwise false. If a
* resource manager does not support explicitly setting the transaction timeout value, this method returns false.
*
* @param transactionTimeout The transaction timeout value in seconds.
*
* @return true if the transaction timeout value is set successfully; otherwise false.
*
* @exception XAException An error has occurred. Possible exception values are XAER_RMERR, XAER_RMFAIL, or XAER_INVAL.
*/
@Override
public boolean setTransactionTimeout(int transactionTimeout) throws XAException {
// Debug.println("*=*=*=*=*=*=*=*=*=*=XAR:setTransactionTimeout:timeout="+transactionTimeout);
return false;
}
/**
* Starts work on behalf of a transaction branch specified in foreignXid
.
*
* If TMJOIN is specified, the start applies to joining a transaction previously seen by the resource manager. If
* TMRESUME is specified, the start applies to resuming a suspended transaction specified in the parameter
* foreignXid
.
*
* If neither TMJOIN nor TMRESUME is specified and the transaction specified by foreignXid
has previously
* been seen by the resource manager, the resource manager throws the XAException exception with XAER_DUPID error code.
*
* @param foreignXid A global transaction identifier to be associated with the resource.
*
* @param flags One of TMNOFLAGS, TMJOIN, or TMRESUME.
*
* @exception XAException An error has occurred. Possible exceptions are XA_RB*, XAER_RMERR, XAER_RMFAIL, XAER_DUPID,
* XAER_OUTSIDE, XAER_NOTA, XAER_INVAL, or XAER_PROTO.
*
*/
@Override
public synchronized void start(Xid foreignXid, int flags) throws XAException {
if (_logger.isLoggable(Level.FINE)) {
_logger.fine(_lgrMID_INF + "XAResourceForMC (" + this.hashCode() + ") Start " + printXid(foreignXid) + printFlags(flags));
}
// convert to jmq xid
JMQXid jmqXid = new JMQXid(foreignXid);
// System.out.println("MQRA:XAR4MC:start():mcId="+mc.getMCId()+":flags="+flags+" xid="+jmqXid.toString());
// Debug.println("MQRA:XAR4RA:start()
// reset flag in start transaction
// this.twoPhasePrepared = false;
// if we're reverting to the pre-6882044 behaviour always send the START to the broker
// otherwise send the START to the broker only if this is not a TMRESUME
if (!isResume(flags) || JMSRAResourceAdapter.isRevert6882044()) {
try {
transactionID = epConnection.protocolHandler.startTransaction(transactionID, flags, jmqXid);
this.jmqXid = jmqXid;
// System.out.println("MQRA:XAR4MC:start:transactionID="+transactionID+" xid="+jmqXid.toString());
JMSRASessionAdapter sa = mc.getConnectionAdapter().getJMSRASessionAdapter();
if (sa != null) {
// System.out.println("MQRA:XAR4MC:start():sa!=null");
sa.getJMSRAXASession().initXATransactionForMC(transactionID);
}
} catch (Exception jmse) {
System.err.println("MQRA:XARFMC:start:XAException-Exception=" + jmse.getMessage());
Debug.printStackTrace(jmse);
XAException xae = new XAException(XAException.XAER_RMFAIL);
xae.initCause(jmse);
throw xae;
}
XAResourceMapForRAMC.register(jmqXid, this, isJoin(flags));
}
started = true;
active = true;
// update the resource state
resourceState = STARTED;
// add xa state to table
if (isXATracking()) {
xaTable.put(jmqXid, XAResourceForRA.XA_START);
}
}
public int getId() {
return id;
}
public synchronized long getTransactionID() {
return transactionID;
}
public boolean started() {
return started;
}
public boolean isActive() {
return active;
}
@Override
public void clearTransactionInfo() throws JMSException {
mc.getConnectionAdapter().closeForPoolingIfClosed();
this.resourceState = CREATED;
}
/****** XaResourceImpl private methods *****/
private boolean isJoin(int flags) {
return ((flags & XAResource.TMJOIN) == XAResource.TMJOIN);
}
private boolean isResume(int flags) {
return ((flags & XAResource.TMRESUME) == XAResource.TMRESUME);
}
private boolean isFail(int flags) {
return ((flags & XAResource.TMFAIL) == XAResource.TMFAIL);
}
private boolean isSuspend(int flags) {
return ((flags & XAResource.TMSUSPEND) == XAResource.TMSUSPEND);
}
// Used for debugging only
private String printXid(Xid foreignXid) {
return DebugConverters.toString(foreignXid);
}
// Used for debugging only
private String printFlags(int flags) {
String result = ("(Flags: ");
if (isJoin(flags)) {
result = result + ("JOIN ");
}
if (isNoFlags(flags)) {
result = result + ("TMNOFLAGS ");
}
if (isFail(flags)) {
result = result + ("TMFAIL ");
}
if (isOnePhase(flags)) {
result = result + ("TMONEPHASE ");
}
if (isResume(flags)) {
result = result + ("TMRESUME ");
}
if (isSuccess(flags)) {
result = result + ("TMSUCCESS ");
}
if (isSuspend(flags)) {
result = result + ("TMSUSPEND ");
}
if (isTMENDRSCAN(flags)) {
result = result + ("TMENDRSCAN ");
}
if (TMSTARTRSCAN(flags)) {
result = result + ("TMSTARTRSCAN ");
}
result = result + (")");
return result;
}
// Used for debugging only
private boolean isNoFlags(int flags) {
return ((flags & XAResource.TMNOFLAGS) == XAResource.TMNOFLAGS);
}
// Used for debugging only
private boolean isOnePhase(int flags) {
return ((flags & XAResource.TMONEPHASE) == XAResource.TMONEPHASE);
}
// Used for debugging only
private boolean isSuccess(int flags) {
return ((flags & XAResource.TMSUCCESS) == XAResource.TMSUCCESS);
}
// Used for debugging only
private boolean isTMENDRSCAN(int flags) {
return ((flags & XAResource.TMENDRSCAN) == XAResource.TMENDRSCAN);
}
// Used for debugging only
private boolean TMSTARTRSCAN(int flags) {
return ((flags & XAResource.TMSTARTRSCAN) == XAResource.TMSTARTRSCAN);
}
@Override
public boolean isComplete() {
return this.resourceState == COMPLETE;
}
@Override
void xaTablePut(JMQXid jmqXid2, Integer xaPrepare) {
xaTable.put(jmqXid2, xaPrepare);
}
@Override
void xaTableRemove(JMQXid jmqXid2) {
xaTable.remove(jmqXid);
}
}