You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by ar...@apache.org on 2008/05/28 13:50:54 UTC

svn commit: r660911 - in /incubator/qpid/trunk/qpid/java: ./ client/src/main/java/org/apache/qpid/client/ client/src/test/java/org/apache/qpid/test/unit/xa/

Author: arnaudsimon
Date: Wed May 28 04:50:54 2008
New Revision: 660911

URL: http://svn.apache.org/viewvc?rev=660911&view=rev
Log:
 QPID-1094: Implement XA resource exception handling and add corresponding tests

Added:
    incubator/qpid/trunk/qpid/java/client/src/test/java/org/apache/qpid/test/unit/xa/FaultTest.java
Modified:
    incubator/qpid/trunk/qpid/java/010ExcludeList
    incubator/qpid/trunk/qpid/java/010ExcludeList-store
    incubator/qpid/trunk/qpid/java/08ExcludeList
    incubator/qpid/trunk/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_10.java
    incubator/qpid/trunk/qpid/java/client/src/main/java/org/apache/qpid/client/XAResourceImpl.java
    incubator/qpid/trunk/qpid/java/client/src/main/java/org/apache/qpid/client/XASessionImpl.java
    incubator/qpid/trunk/qpid/java/client/src/test/java/org/apache/qpid/test/unit/xa/AbstractXATestCase.java

Modified: incubator/qpid/trunk/qpid/java/010ExcludeList
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/java/010ExcludeList?rev=660911&r1=660910&r2=660911&view=diff
==============================================================================
--- incubator/qpid/trunk/qpid/java/010ExcludeList (original)
+++ incubator/qpid/trunk/qpid/java/010ExcludeList Wed May 28 04:50:54 2008
@@ -44,4 +44,6 @@
 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

Modified: incubator/qpid/trunk/qpid/java/010ExcludeList-store
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/java/010ExcludeList-store?rev=660911&r1=660910&r2=660911&view=diff
==============================================================================
--- incubator/qpid/trunk/qpid/java/010ExcludeList-store (original)
+++ incubator/qpid/trunk/qpid/java/010ExcludeList-store Wed May 28 04:50:54 2008
@@ -39,4 +39,6 @@
 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

Modified: incubator/qpid/trunk/qpid/java/08ExcludeList
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/java/08ExcludeList?rev=660911&r1=660910&r2=660911&view=diff
==============================================================================
--- incubator/qpid/trunk/qpid/java/08ExcludeList (original)
+++ incubator/qpid/trunk/qpid/java/08ExcludeList Wed May 28 04:50:54 2008
@@ -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#*

Modified: incubator/qpid/trunk/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_10.java
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_10.java?rev=660911&r1=660910&r2=660911&view=diff
==============================================================================
--- incubator/qpid/trunk/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_10.java (original)
+++ incubator/qpid/trunk/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_10.java Wed May 28 04:50:54 2008
@@ -71,6 +71,8 @@
     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 @@
 
         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

Modified: incubator/qpid/trunk/qpid/java/client/src/main/java/org/apache/qpid/client/XAResourceImpl.java
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/java/client/src/main/java/org/apache/qpid/client/XAResourceImpl.java?rev=660911&r1=660910&r2=660911&view=diff
==============================================================================
--- incubator/qpid/trunk/qpid/java/client/src/main/java/org/apache/qpid/client/XAResourceImpl.java (original)
+++ incubator/qpid/trunk/qpid/java/client/src/main/java/org/apache/qpid/client/XAResourceImpl.java Wed May 28 04:50:54 2008
@@ -73,54 +73,25 @@
     {
         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 @@
     {
         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 @@
     {
         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 @@
         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 @@
         {
             _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 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 @@
     {
         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);
+        }
+    }
+
 }

Modified: incubator/qpid/trunk/qpid/java/client/src/main/java/org/apache/qpid/client/XASessionImpl.java
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/java/client/src/main/java/org/apache/qpid/client/XASessionImpl.java?rev=660911&r1=660910&r2=660911&view=diff
==============================================================================
--- incubator/qpid/trunk/qpid/java/client/src/main/java/org/apache/qpid/client/XASessionImpl.java (original)
+++ incubator/qpid/trunk/qpid/java/client/src/main/java/org/apache/qpid/client/XASessionImpl.java Wed May 28 04:50:54 2008
@@ -54,10 +54,21 @@
         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
 
     /**

Modified: incubator/qpid/trunk/qpid/java/client/src/test/java/org/apache/qpid/test/unit/xa/AbstractXATestCase.java
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/java/client/src/test/java/org/apache/qpid/test/unit/xa/AbstractXATestCase.java?rev=660911&r1=660910&r2=660911&view=diff
==============================================================================
--- incubator/qpid/trunk/qpid/java/client/src/test/java/org/apache/qpid/test/unit/xa/AbstractXATestCase.java (original)
+++ incubator/qpid/trunk/qpid/java/client/src/test/java/org/apache/qpid/test/unit/xa/AbstractXATestCase.java Wed May 28 04:50:54 2008
@@ -23,6 +23,7 @@
 import javax.transaction.xa.Xid;
 import javax.transaction.xa.XAResource;
 import javax.jms.*;
+import java.util.Random;
 
 /**
  *
@@ -55,7 +56,7 @@
      /**
      * xid counter
      */
-    private static int _xidCounter = 0;
+    private static int _xidCounter = (new Random()).nextInt(1000000);
 
 
      protected void setUp() throws Exception

Added: incubator/qpid/trunk/qpid/java/client/src/test/java/org/apache/qpid/test/unit/xa/FaultTest.java
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/java/client/src/test/java/org/apache/qpid/test/unit/xa/FaultTest.java?rev=660911&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/java/client/src/test/java/org/apache/qpid/test/unit/xa/FaultTest.java (added)
+++ incubator/qpid/trunk/qpid/java/client/src/test/java/org/apache/qpid/test/unit/xa/FaultTest.java Wed May 28 04:50:54 2008
@@ -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);
+        }
+    }
+}