diff options
author | Arnaud Simon <arnaudsimon@apache.org> | 2008-05-28 11:50:54 +0000 |
---|---|---|
committer | Arnaud Simon <arnaudsimon@apache.org> | 2008-05-28 11:50:54 +0000 |
commit | 55289d4a7a9ae310cca5146db5b99e42d7835297 (patch) | |
tree | bdc48d87493140fcf8baddf4ad8c6114225d4a93 | |
parent | 2366341dd03a1c66c4bf62a579cfc59603bd5d53 (diff) | |
download | qpid-python-55289d4a7a9ae310cca5146db5b99e42d7835297.tar.gz |
QPID-1094: Implement XA resource exception handling and add corresponding tests
git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid@660911 13f79535-47bb-0310-9956-ffa450edef68
8 files changed, 694 insertions, 183 deletions
diff --git a/java/010ExcludeList b/java/010ExcludeList index 13ae556017..ca0ad31412 100644 --- a/java/010ExcludeList +++ b/java/010ExcludeList @@ -44,4 +44,6 @@ org.apache.qpid.test.testcases.ImmediateMessageTest#test_QPID_517_ImmediateFails org.apache.qpid.test.testcases.MandatoryMessageTest#test_QPID_508_MandatoryFailsNoRouteNoTxP2P org.apache.qpid.test.testcases.MandatoryMessageTest#test_QPID_508_MandatoryFailsNoRouteTxP2P org.apache.qpid.test.testcases.MandatoryMessageTest#test_QPID_508_MandatoryFailsNoRouteNoTxPubSub -org.apache.qpid.test.testcases.MandatoryMessageTest#test_QPID_508_MandatoryFailsNoRouteTxPubSub
\ No newline at end of file +org.apache.qpid.test.testcases.MandatoryMessageTest#test_QPID_508_MandatoryFailsNoRouteTxPubSub +// Those tests are failing, they must be removed from this list once QPID-1095 is fixed. +org.apache.qpid.test.unit.xa.FaultTest#*
\ No newline at end of file diff --git a/java/010ExcludeList-store b/java/010ExcludeList-store index 94777dc44b..1692fd2345 100644 --- a/java/010ExcludeList-store +++ b/java/010ExcludeList-store @@ -39,4 +39,6 @@ org.apache.qpid.test.testcases.ImmediateMessageTest#test_QPID_517_ImmediateFails org.apache.qpid.test.testcases.MandatoryMessageTest#test_QPID_508_MandatoryFailsNoRouteNoTxP2P org.apache.qpid.test.testcases.MandatoryMessageTest#test_QPID_508_MandatoryFailsNoRouteTxP2P org.apache.qpid.test.testcases.MandatoryMessageTest#test_QPID_508_MandatoryFailsNoRouteNoTxPubSub -org.apache.qpid.test.testcases.MandatoryMessageTest#test_QPID_508_MandatoryFailsNoRouteTxPubSub
\ No newline at end of file +org.apache.qpid.test.testcases.MandatoryMessageTest#test_QPID_508_MandatoryFailsNoRouteTxPubSub +// Those tests are failing, they must be removed from this list once QPID-1095 is fixed. +org.apache.qpid.test.unit.xa.FaultTest#*
\ No newline at end of file diff --git a/java/08ExcludeList b/java/08ExcludeList index c97c968816..1ca9279271 100644 --- a/java/08ExcludeList +++ b/java/08ExcludeList @@ -1,5 +1,6 @@ org.apache.qpid.test.unit.xa.QueueTest#* org.apache.qpid.test.unit.xa.TopicTest#* +org.apache.qpid.test.unit.xa.FaultTest#* org.apache.qpid.test.unit.ct.DurableSubscriberTests#* // Those tests are not finished org.apache.qpid.test.testcases.TTLTest#* diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_10.java b/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_10.java index cf5e1bd8ac..e2767e2c9d 100644 --- a/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_10.java +++ b/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_10.java @@ -71,6 +71,8 @@ public class AMQSession_0_10 extends AMQSession private Object _currentExceptionLock = new Object(); private QpidException _currentException; + // a ref on the qpidity connection + protected org.apache.qpidity.nclient.Connection _qpidConnection; //--- constructors /** @@ -92,7 +94,7 @@ public class AMQSession_0_10 extends AMQSession super(con, channelId, transacted, acknowledgeMode, messageFactoryRegistry, defaultPrefetchHighMark, defaultPrefetchLowMark); - + _qpidConnection = qpidConnection; // create the qpid session with an expiry <= 0 so that the session does not expire _qpidSession = qpidConnection.createSession(0); // set the exception listnere for this session diff --git a/java/client/src/main/java/org/apache/qpid/client/XAResourceImpl.java b/java/client/src/main/java/org/apache/qpid/client/XAResourceImpl.java index fd50417fc7..27ec445436 100644 --- a/java/client/src/main/java/org/apache/qpid/client/XAResourceImpl.java +++ b/java/client/src/main/java/org/apache/qpid/client/XAResourceImpl.java @@ -73,54 +73,25 @@ public class XAResourceImpl implements XAResource { if (_logger.isDebugEnabled()) { - _logger.debug("commit ", xid); + _logger.debug("commit tx branch with xid: ", xid); } - if (xid == null) - { - throw new XAException(XAException.XAER_PROTO); - } - Future<XaResult> future; + Future<XaResult> future = + _xaSession.getQpidSession().dtxCommit(convertXid(xid), b ? Option.ONE_PHASE : Option.NO_OPTION); + + // now wait on the future for the result + XaResult result = null; try { - future = _xaSession.getQpidSession() - .dtxCommit(XidImpl.convert(xid), b ? Option.ONE_PHASE : Option.NO_OPTION); + result = future.get(); } - catch (QpidException e) + catch (SessionException e) { - if (_logger.isDebugEnabled()) - { - _logger.debug("Cannot convert Xid into String format ", e); - } - throw new XAException(XAException.XAER_PROTO); - } - // now wait on the future for the result - XaResult result = future.get(); - DtxXaStatus status = result.getStatus(); - switch (status) - { - case XA_OK: - // do nothing this ok - break; - case XA_HEURHAZ: - throw new XAException(XAException.XA_HEURHAZ); - case XA_HEURCOM: - throw new XAException(XAException.XA_HEURCOM); - case XA_HEURRB: - throw new XAException(XAException.XA_HEURRB); - case XA_HEURMIX: - throw new XAException(XAException.XA_HEURMIX); - case XA_RBROLLBACK: - throw new XAException(XAException.XA_RBROLLBACK); - case XA_RBTIMEOUT: - throw new XAException(XAException.XA_RBTIMEOUT); - default: - // this should not happen - if (_logger.isDebugEnabled()) - { - _logger.debug("got unexpected status value: ", status); - } - throw new XAException(XAException.XAER_PROTO); + // we need to restore the qpidity session that has been closed + _xaSession.createSession(); + // we should get a single exception + convertExecutionErrorToXAErr(e.getExceptions().get(0).getErrorCode()); } + checkStatus(result.getStatus()); } /** @@ -143,50 +114,29 @@ public class XAResourceImpl implements XAResource { if (_logger.isDebugEnabled()) { - _logger.debug("end ", xid); + _logger.debug("end tx branch with xid: ", xid); } - if (xid == null) - { - throw new XAException(XAException.XAER_PROTO); - } - Future<XaResult> future; + Future<XaResult> future = _xaSession.getQpidSession() + .dtxEnd(convertXid(xid), + flag == XAResource.TMFAIL ? Option.FAIL : Option.NO_OPTION, + flag == XAResource.TMSUSPEND ? Option.SUSPEND : Option.NO_OPTION); + // now wait on the future for the result + XaResult result = null; try { - future = _xaSession.getQpidSession() - .dtxEnd(XidImpl.convert(xid), - flag == XAResource.TMFAIL ? Option.FAIL : Option.NO_OPTION, - flag == XAResource.TMSUSPEND ? Option.SUSPEND : Option.NO_OPTION); - } - catch (QpidException e) - { - if (_logger.isDebugEnabled()) - { - _logger.debug("Cannot convert Xid into String format ", e); - } - throw new XAException(XAException.XAER_PROTO); + result = future.get(); } - // now wait on the future for the result - XaResult result = future.get(); - DtxXaStatus status = result.getStatus(); - switch (status) + catch (SessionException e) { - case XA_OK: - // do nothing this ok - break; - case XA_RBROLLBACK: - throw new XAException(XAException.XA_RBROLLBACK); - case XA_RBTIMEOUT: - throw new XAException(XAException.XA_RBTIMEOUT); - default: - // this should not happen - if (_logger.isDebugEnabled()) - { - _logger.debug("got unexpected status value: ", status); - } - throw new XAException(XAException.XAER_PROTO); + // we need to restore the qpidity session that has been closed + _xaSession.createSession(); + // we should get a single exception + convertExecutionErrorToXAErr(e.getExceptions().get(0).getErrorCode()); } + checkStatus(result.getStatus()); } + /** * Tells the resource manager to forget about a heuristically completed transaction branch. * @@ -198,16 +148,23 @@ public class XAResourceImpl implements XAResource { if (_logger.isDebugEnabled()) { - _logger.debug("forget ", xid); + _logger.debug("forget tx branch with xid: ", xid); } - if (xid == null) + _xaSession.getQpidSession().dtxForget(convertXid(xid)); + try + { + _xaSession.getQpidSession().sync(); + } + catch (SessionException e) { - throw new XAException(XAException.XAER_PROTO); + // we need to restore the qpidity session that has been closed + _xaSession.createSession(); + // we should get a single exception + convertExecutionErrorToXAErr(e.getExceptions().get(0).getErrorCode()); } - _xaSession.getQpidSession().dtxForget(new org.apache.qpidity.transport.Xid() - .setGlobalId((xid.getGlobalTransactionId()))); } + /** * Obtains the current transaction timeout value set for this XAResource instance. * If XAResource.setTransactionTimeout was not used prior to invoking this method, @@ -222,19 +179,18 @@ public class XAResourceImpl implements XAResource int result = 0; if (_xid != null) { + Future<GetTimeoutResult> future = + _xaSession.getQpidSession().dtxGetTimeout(convertXid(_xid)); try { - Future<GetTimeoutResult> future = - _xaSession.getQpidSession().dtxGetTimeout(XidImpl.convert(_xid)); result = (int) future.get().getTimeout(); } - catch (QpidException e) + catch (SessionException e) { - if (_logger.isDebugEnabled()) - { - _logger.debug("Cannot convert Xid into String format ", e); - } - throw new XAException(XAException.XAER_PROTO); + // we need to restore the qpidity session that has been closed + _xaSession.createSession(); + // we should get a single exception + convertExecutionErrorToXAErr(e.getExceptions().get(0).getErrorCode()); } } return result; @@ -270,46 +226,30 @@ public class XAResourceImpl implements XAResource { _logger.debug("prepare ", xid); } - if (xid == null) - { - throw new XAException(XAException.XAER_PROTO); - } - Future<XaResult> future; + Future<XaResult> future = _xaSession.getQpidSession().dtxPrepare(convertXid(xid)); + XaResult result = null; try { - future = _xaSession.getQpidSession() - .dtxPrepare(XidImpl.convert(xid)); + result = future.get(); } - catch (QpidException e) + catch (SessionException e) { - if (_logger.isDebugEnabled()) - { - _logger.debug("Cannot convert Xid into String format ", e); - } - throw new XAException(XAException.XAER_PROTO); + // we need to restore the qpidity session that has been closed + _xaSession.createSession(); + // we should get a single exception + convertExecutionErrorToXAErr(e.getExceptions().get(0).getErrorCode()); } - XaResult result = future.get(); DtxXaStatus status = result.getStatus(); - int outcome; + int outcome = XAResource.XA_OK; switch (status) { case XA_OK: - outcome = XAResource.XA_OK; break; case XA_RDONLY: outcome = XAResource.XA_RDONLY; break; - case XA_RBROLLBACK: - throw new XAException(XAException.XA_RBROLLBACK); - case XA_RBTIMEOUT: - throw new XAException(XAException.XA_RBTIMEOUT); default: - // this should not happen - if (_logger.isDebugEnabled()) - { - _logger.debug("got unexpected status value: ", status); - } - throw new XAException(XAException.XAER_PROTO); + checkStatus(status); } return outcome; } @@ -351,53 +291,26 @@ public class XAResourceImpl implements XAResource */ public void rollback(Xid xid) throws XAException { - if (xid == null) + if (_logger.isDebugEnabled()) { - throw new XAException(XAException.XAER_PROTO); + _logger.debug("rollback tx branch with xid: ", xid); } - // the flag is ignored - Future<XaResult> future; + + Future<XaResult> future = _xaSession.getQpidSession().dtxRollback(convertXid(xid)); + // now wait on the future for the result + XaResult result = null; try { - future = _xaSession.getQpidSession() - .dtxRollback(XidImpl.convert(xid)); + result = future.get(); } - catch (QpidException e) + catch (SessionException e) { - if (_logger.isDebugEnabled()) - { - _logger.debug("Cannot convert Xid into String format ", e); - } - throw new XAException(XAException.XAER_PROTO); - } - // now wait on the future for the result - XaResult result = future.get(); - DtxXaStatus status = result.getStatus(); - switch (status) - { - case XA_OK: - // do nothing this ok - break; - case XA_HEURHAZ: - throw new XAException(XAException.XA_HEURHAZ); - case XA_HEURCOM: - throw new XAException(XAException.XA_HEURCOM); - case XA_HEURRB: - throw new XAException(XAException.XA_HEURRB); - case XA_HEURMIX: - throw new XAException(XAException.XA_HEURMIX); - case XA_RBROLLBACK: - throw new XAException(XAException.XA_RBROLLBACK); - case XA_RBTIMEOUT: - throw new XAException(XAException.XA_RBTIMEOUT); - default: - // this should not happen - if (_logger.isDebugEnabled()) - { - _logger.debug("got unexpected status value: ", status); - } - throw new XAException(XAException.XAER_PROTO); + // we need to restore the qpidity session that has been closed + _xaSession.createSession(); + // we should get a single exception + convertExecutionErrorToXAErr( e.getExceptions().get(0).getErrorCode()); } + checkStatus(result.getStatus()); } /** @@ -451,48 +364,141 @@ public class XAResourceImpl implements XAResource { if (_logger.isDebugEnabled()) { - _logger.debug("start ", xid); + _logger.debug("start tx branch with xid: ", xid); } - if (xid == null) - { - throw new XAException(XAException.XAER_PROTO); - } - _xid = xid; - Future<XaResult> future; + Future<XaResult> future = _xaSession.getQpidSession() + .dtxStart(convertXid(xid), + flag == XAResource.TMJOIN ? Option.JOIN : Option.NO_OPTION, + flag == XAResource.TMRESUME ? Option.RESUME : Option.NO_OPTION); + // now wait on the future for the result + XaResult result = null; try { - future = _xaSession.getQpidSession() - .dtxStart(XidImpl.convert(xid), - flag == XAResource.TMJOIN ? Option.JOIN : Option.NO_OPTION, - flag == XAResource.TMRESUME ? Option.RESUME : Option.NO_OPTION); + result = future.get(); } - catch (QpidException e) + catch (SessionException e) { - if (_logger.isDebugEnabled()) - { - _logger.debug("Cannot convert Xid into String format ", e); - } - throw new XAException(XAException.XAER_PROTO); + // we need to restore the qpidity session that has been closed + _xaSession.createSession(); + // we should get a single exception + convertExecutionErrorToXAErr(e.getExceptions().get(0).getErrorCode()); + // TODO: The amqp spec does not allow to make the difference + // between an already known XID and a wrong arguments (join and resume are set) + // TODO: make sure amqp addresses that } - // now wait on the future for the result - XaResult result = future.get(); - DtxXaStatus status = result.getStatus(); + checkStatus(result.getStatus()); + _xid = xid; + } + + //------------------------------------------------------------------------ + // Private methods + //------------------------------------------------------------------------ + + /** + * Check xa method outcome and, when required, convert the status into the corresponding xa exception + * @param status method status code + * @throws XAException corresponding XA Exception when required + */ + private void checkStatus(DtxXaStatus status) throws XAException + { switch (status) { case XA_OK: - // do nothing this ok + // Do nothing this ok break; case XA_RBROLLBACK: + // The tx has been rolled back for an unspecified reason. throw new XAException(XAException.XA_RBROLLBACK); case XA_RBTIMEOUT: + // The transaction branch took too long. throw new XAException(XAException.XA_RBTIMEOUT); + case XA_HEURHAZ: + // The transaction branch may have been heuristically completed. + throw new XAException(XAException.XA_HEURHAZ); + case XA_HEURCOM: + // The transaction branch has been heuristically committed. + throw new XAException(XAException.XA_HEURCOM); + case XA_HEURRB: + // The transaction branch has been heuristically rolled back. + throw new XAException(XAException.XA_HEURRB); + case XA_HEURMIX: + // The transaction branch has been heuristically committed and rolled back. + throw new XAException(XAException.XA_HEURMIX); + case XA_RDONLY: + // The transaction branch was read-only and has been committed. + throw new XAException(XAException.XA_RDONLY); default: // this should not happen if (_logger.isDebugEnabled()) { _logger.debug("got unexpected status value: ", status); } + //A resource manager error has occured in the transaction branch. + throw new XAException(XAException.XAER_RMERR); + } + } + + /** + * Convert execution error to xa exception. + * @param error the execution error code + * @throws XAException + */ + private void convertExecutionErrorToXAErr(ExecutionErrorCode error) throws XAException + { + switch (error) + { + case NOT_ALLOWED: + // The XID already exists. + throw new XAException(XAException.XAER_DUPID); + case NOT_FOUND: + // The XID is not valid. + throw new XAException(XAException.XAER_NOTA); + case ILLEGAL_STATE: + // Routine was invoked in an inproper context. throw new XAException(XAException.XAER_PROTO); + case NOT_IMPLEMENTED: + // the command is not implemented + throw new XAException(XAException.XAER_RMERR); + case COMMAND_INVALID: + // Invalid call + throw new XAException(XAException.XAER_INVAL); + default: + // this should not happen + if (_logger.isDebugEnabled()) + { + _logger.debug("Got unexpected error: " + error); + } + //A resource manager error has occured in the transaction branch. + throw new XAException(XAException.XAER_RMERR); } } + + /** + * convert a generic xid into qpid format + * @param xid xid to be converted + * @return the qpid formated xid + * @throws XAException when xid is null or when it cannot be converted. + */ + private org.apache.qpidity.transport.Xid convertXid(Xid xid) throws XAException + { + if (xid == null) + { + // Invalid arguments were given. + throw new XAException(XAException.XAER_INVAL); + } + try + { + return XidImpl.convert(xid); + } + catch (QpidException e) + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Cannot convert Xid into String format ", e); + } + //A resource manager error has occured in the transaction branch. + throw new XAException(XAException.XAER_RMERR); + } + } + } diff --git a/java/client/src/main/java/org/apache/qpid/client/XASessionImpl.java b/java/client/src/main/java/org/apache/qpid/client/XASessionImpl.java index 97f1098e43..1eda71cc63 100644 --- a/java/client/src/main/java/org/apache/qpid/client/XASessionImpl.java +++ b/java/client/src/main/java/org/apache/qpid/client/XASessionImpl.java @@ -54,10 +54,21 @@ public class XASessionImpl extends AMQSession_0_10 implements XASession, XATopic super(qpidConnection, con, channelId, false, // this is not a transacted session Session.AUTO_ACKNOWLEDGE, // the ack mode is transacted MessageFactoryRegistry.newDefaultRegistry(), defaultPrefetchHigh, defaultPrefetchLow); - _qpidDtxSession = qpidConnection.createDTXSession(0); + createSession(); _xaResource = new XAResourceImpl(this); } + //-- public methods + + /** + * Create a qpidity session. + */ + public void createSession() + { + _qpidDtxSession = _qpidConnection.createDTXSession(0); + } + + //--- javax.njms.XASEssion API /** diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/xa/AbstractXATestCase.java b/java/client/src/test/java/org/apache/qpid/test/unit/xa/AbstractXATestCase.java index 7c03e16258..c8ec62d059 100644 --- a/java/client/src/test/java/org/apache/qpid/test/unit/xa/AbstractXATestCase.java +++ b/java/client/src/test/java/org/apache/qpid/test/unit/xa/AbstractXATestCase.java @@ -23,6 +23,7 @@ import org.apache.qpid.testutil.QpidTestCase; import javax.transaction.xa.Xid; import javax.transaction.xa.XAResource; import javax.jms.*; +import java.util.Random; /** * @@ -55,7 +56,7 @@ public abstract class AbstractXATestCase extends QpidTestCase /** * xid counter */ - private static int _xidCounter = 0; + private static int _xidCounter = (new Random()).nextInt(1000000); protected void setUp() throws Exception diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/xa/FaultTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/xa/FaultTest.java new file mode 100644 index 0000000000..2c99ce707d --- /dev/null +++ b/java/client/src/test/java/org/apache/qpid/test/unit/xa/FaultTest.java @@ -0,0 +1,486 @@ +package org.apache.qpid.test.unit.xa; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jms.*; +import javax.transaction.xa.Xid; +import javax.transaction.xa.XAResource; +import javax.transaction.xa.XAException; + +import junit.framework.TestSuite; + + +public class FaultTest extends AbstractXATestCase +{ + /* this clas logger */ + private static final Logger _logger = LoggerFactory.getLogger(FaultTest.class); + + /** + * the queue use by all the tests + */ + private static Queue _queue = null; + /** + * the queue connection factory used by all tests + */ + private static XAQueueConnectionFactory _queueFactory = null; + + /** + * standard xa queue connection + */ + private static XAQueueConnection _xaqueueConnection = null; + + /** + * standard xa queue connection + */ + private static QueueConnection _queueConnection = null; + + + /** + * standard queue session created from the standard connection + */ + private static QueueSession _nonXASession = null; + + /** + * the queue name + */ + private static final String QUEUENAME = "xaQueue"; + + /** ----------------------------------------------------------------------------------- **/ + /** + * ----------------------------- JUnit support ----------------------------------------- * + */ + + /** + * Gets the test suite tests + * + * @return the test suite tests + */ + public static TestSuite getSuite() + { + return new TestSuite(QueueTest.class); + } + + /** + * Run the test suite. + * + * @param args Any command line arguments specified to this class. + */ + public static void main(String args[]) + { + junit.textui.TestRunner.run(getSuite()); + } + + public void tearDown() throws Exception + { + if (!isBroker08()) + { + try + { + _xaqueueConnection.close(); + _queueConnection.close(); + } + catch (Exception e) + { + fail("Exception thrown when cleaning standard connection: " + e); + } + } + super.tearDown(); + } + + /** + * Initialize standard actors + */ + public void init() + { + if (!isBroker08()) + { + // lookup test queue + try + { + _queue = (Queue) getInitialContext().lookup(QUEUENAME); + } + catch (Exception e) + { + fail("cannot lookup test queue " + e.getMessage()); + } + // lookup connection factory + try + { + _queueFactory = getConnectionFactory(); + } + catch (Exception e) + { + fail("enable to lookup connection factory "); + } + // create standard connection + try + { + _xaqueueConnection = _queueFactory.createXAQueueConnection("guest", "guest"); + } + catch (JMSException e) + { + fail("cannot create queue connection: " + e.getMessage()); + } + // create xa session + XAQueueSession session = null; + try + { + session = _xaqueueConnection.createXAQueueSession(); + } + catch (JMSException e) + { + fail("cannot create queue session: " + e.getMessage()); + } + // create a standard session + try + { + _queueConnection = _queueFactory.createQueueConnection(); + _nonXASession = _queueConnection.createQueueSession(true, Session.AUTO_ACKNOWLEDGE); + } + catch (JMSException e) + { + fail("cannot create queue session: " + e.getMessage()); + } + init(session, _queue); + } + } + + /** -------------------------------------------------------------------------------------- **/ + /** ----------------------------- Test Suite -------------------------------------------- **/ + /** -------------------------------------------------------------------------------------- **/ + + /** + * Strategy: + * Invoke start twice with the same xid on an XA resource. + * Check that the second + * invocation is throwing the expected XA exception. + */ + public void testSameXID() + { + _logger.debug("running testSameXID"); + Xid xid = getNewXid(); + try + { + _xaResource.start(xid, XAResource.TMNOFLAGS); + } + catch (XAException e) + { + fail("cannot start the transaction with xid: " + e.getMessage()); + } + // we now exepct this operation to fail + try + { + _xaResource.start(xid, XAResource.TMNOFLAGS); + fail("We managed to start a transaction with the same xid"); + } + catch (XAException e) + { + assertEquals("Wrong error code: ", XAException.XAER_DUPID, e.errorCode); + } + catch (Exception ex) + { + fail("Caught wrong exception, expected XAException, got: " + ex); + } + } + + /** + * Strategy: + * Invoke start on a XA resource with flag other than TMNOFLAGS, TMJOIN, or TMRESUME. + * Check that a XA Exception is thrown. + */ + public void testWrongStartFlag() + { + _logger.debug("running testWrongStartFlag"); + Xid xid = getNewXid(); + try + { + _xaResource.start(xid, XAResource.TMONEPHASE); + fail("We managed to start a transaction with a wrong flag"); + } + catch (XAException e) + { + assertEquals("Wrong error code: ", XAException.XAER_INVAL, e.errorCode); + } + catch (Exception ex) + { + fail("Caught wrong exception, expected XAException, got: " + ex); + } + } + + /** + * Strategy: + * Check that a XA exception is thrown when: + * A non started xid is ended + */ + public void testEnd() + { + _logger.debug("running testEnd"); + Xid xid = getNewXid(); + try + { + _xaResource.end(xid, XAResource.TMSUCCESS); + fail("We managed to end a transaction before it is started"); + } + catch (XAException e) + { + assertEquals("Wrong error code: ", XAException.XAER_PROTO, e.errorCode); + } + catch (Exception ex) + { + fail("Caught wrong exception, expected XAException, got: " + ex); + } + } + + + /** + * Strategy: + * Check that a XA exception is thrown when: + * Call forget on an unknown xid + * call forget on a started xid + * A non started xid is prepared + * A non ended xis is prepared + */ + public void testForget() + { + _logger.debug("running testForget"); + Xid xid = getNewXid(); + try + { + _xaResource.forget(xid); + fail("We managed to forget an unknown xid"); + } + catch (XAException e) + { + // assertEquals("Wrong error code: ", XAException.XAER_NOTA, e.errorCode); + } + catch (Exception ex) + { + fail("Caught wrong exception, expected XAException, got: " + ex); + } + xid = getNewXid(); + try + { + _xaResource.start(xid, XAResource.TMNOFLAGS); + _xaResource.forget(xid); + fail("We managed to forget a started xid"); + } + catch (XAException e) + { + assertEquals("Wrong error code: ", XAException.XAER_PROTO, e.errorCode); + } + catch (Exception ex) + { + fail("Caught wrong exception, expected XAException, got: " + ex); + } + } + + /** + * Strategy: + * Check that a XA exception is thrown when: + * A non started xid is prepared + * A non ended xid is prepared + */ + public void testPrepare() + { + _logger.debug("running testPrepare"); + Xid xid = getNewXid(); + try + { + _xaResource.prepare(xid); + fail("We managed to prepare an unknown xid"); + } + catch (XAException e) + { + assertEquals("Wrong error code: ", XAException.XAER_NOTA, e.errorCode); + } + catch (Exception ex) + { + fail("Caught wrong exception, expected XAException, got: " + ex); + } + xid = getNewXid(); + try + { + _xaResource.start(xid, XAResource.TMNOFLAGS); + _xaResource.prepare(xid); + fail("We managed to prepare a started xid"); + } + catch (XAException e) + { + assertEquals("Wrong error code: ", XAException.XAER_PROTO, e.errorCode); + } + catch (Exception ex) + { + fail("Caught wrong exception, expected XAException, got: " + ex); + } + } + + /** + * Strategy: + * Check that the expected XA exception is thrown when: + * A non started xid is committed + * A non ended xid is committed + * A non prepared xid is committed with one phase set to false. + * A prepared xid is committed with one phase set to true. + */ + public void testCommit() + { + _logger.debug("running testCommit"); + Xid xid = getNewXid(); + try + { + _xaResource.commit(xid, true); + fail("We managed to commit an unknown xid"); + } + catch (XAException e) + { + assertEquals("Wrong error code: ", XAException.XAER_NOTA, e.errorCode); + } + catch (Exception ex) + { + fail("Caught wrong exception, expected XAException, got: " + ex); + } + xid = getNewXid(); + try + { + _xaResource.start(xid, XAResource.TMNOFLAGS); + _xaResource.commit(xid, true); + fail("We managed to commit a not ended xid"); + } + catch (XAException e) + { + assertEquals("Wrong error code: ", XAException.XAER_PROTO, e.errorCode); + } + catch (Exception ex) + { + fail("Caught wrong exception, expected XAException, got: " + ex); + } + xid = getNewXid(); + try + { + _xaResource.start(xid, XAResource.TMNOFLAGS); + _xaResource.end(xid, XAResource.TMNOFLAGS); + _xaResource.commit(xid, false); + fail("We managed to commit a not prepared xid"); + } + catch (XAException e) + { + assertEquals("Wrong error code: ", XAException.XAER_PROTO, e.errorCode); + } + catch (Exception ex) + { + fail("Caught wrong exception, expected XAException, got: " + ex); + } + xid = getNewXid(); + try + { + _xaResource.start(xid, XAResource.TMNOFLAGS); + _xaResource.end(xid, XAResource.TMNOFLAGS); + _xaResource.prepare(xid); + _xaResource.commit(xid, true); + fail("We managed to commit a prepared xid"); + } + catch (XAException e) + { + assertEquals("Wrong error code: ", XAException.XAER_PROTO, e.errorCode); + } + catch (Exception ex) + { + fail("Caught wrong exception, expected XAException, got: " + ex); + } + } + + /** + * Strategy: + * Check that the expected XA exception is thrown when: + * A non started xid is rolled back + * A non ended xid is rolled back + */ + public void testRollback() + { + _logger.debug("running testRollback"); + Xid xid = getNewXid(); + try + { + _xaResource.rollback(xid); + fail("We managed to rollback an unknown xid"); + } + catch (XAException e) + { + assertEquals("Wrong error code: ", XAException.XAER_NOTA, e.errorCode); + } + catch (Exception ex) + { + fail("Caught wrong exception, expected XAException, got: " + ex); + } + xid = getNewXid(); + try + { + _xaResource.start(xid, XAResource.TMNOFLAGS); + _xaResource.rollback(xid); + fail("We managed to rollback a not ended xid"); + } + catch (XAException e) + { + assertEquals("Wrong error code: ", XAException.XAER_PROTO, e.errorCode); + } + catch (Exception ex) + { + fail("Caught wrong exception, expected XAException, got: " + ex); + } + } + + /** + * Strategy: + * Check that the timeout is set correctly + */ + public void testTransactionTimeoutvalue() + { + _logger.debug("running testRollback"); + Xid xid = getNewXid(); + try + { + _xaResource.start(xid, XAResource.TMNOFLAGS); + assertEquals("Wrong timeout", _xaResource.getTransactionTimeout(), 0); + _xaResource.setTransactionTimeout(1000); + assertEquals("Wrong timeout", _xaResource.getTransactionTimeout(), 1000); + _xaResource.end(xid, XAResource.TMNOFLAGS); + xid = getNewXid(); + _xaResource.start(xid, XAResource.TMNOFLAGS); + assertEquals("Wrong timeout", _xaResource.getTransactionTimeout(), 0); + } + catch (Exception ex) + { + fail("Caught wrong exception, expected XAException, got: " + ex); + } + } + + /** + * Strategy: + * Check that a transaction timeout as expected + * - set timeout to 10ms + * - sleep 1000ms + * - call end and check that the expected exception is thrown + */ + public void testTransactionTimeout() + { + _logger.debug("running testRollback"); + Xid xid = getNewXid(); + try + { + _xaResource.start(xid, XAResource.TMNOFLAGS); + assertEquals("Wrong timeout", _xaResource.getTransactionTimeout(), 0); + _xaResource.setTransactionTimeout(10); + Thread.sleep(1000); + _xaResource.end(xid, XAResource.TMNOFLAGS); + } + catch (XAException e) + { + assertEquals("Wrong error code: ", XAException.XA_RBTIMEOUT, e.errorCode); + } + catch (Exception ex) + { + fail("Caught wrong exception, expected XAException, got: " + ex); + } + } +} |