/* * JBoss, Home of Professional Open Source * Copyright 2010, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. * See the copyright.txt in the distribution for a * full listing of individual contributors. * This copyrighted material is made available to anyone wishing to use, * modify, copy, or redistribute it subject to the terms and conditions * of the GNU Lesser General Public License, v. 2.1. * This program is distributed in the hope that it will be useful, but WITHOUT A * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public License, * v.2.1 along with this distribution; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. * * (C) 2010 * @author JBoss Inc. */ package org.jboss.jbossts.star.test; import org.jboss.jbossts.star.provider.HttpResponseException; import org.jboss.jbossts.star.util.*; import org.jboss.jbossts.star.util.media.txstatusext.*; import org.junit.BeforeClass; import org.junit.Test; import org.junit.Assert; import javax.net.ssl.HttpsURLConnection; import java.net.HttpURLConnection; import java.util.*; /* * The comments that are preceded by line numbers refer to text in version 8 of the specification */ public class OptionalSpecTest extends BaseTest { @BeforeClass public static void beforeClass() throws Exception { startContainer(TXN_MGR_URL); } @Test public void testStatusWithoutParticipants() throws Exception { TxSupport txn = new TxSupport(); txn.startTx(); /* 288Additional information about the transaction, such as the number of participants and their 289individual URIs, MAY be returned if the client specifies the application/txstatusext+xml and the 290implementation supports that type. */ CoordinatorElement elem = txn.getTransactionInfo(); if (txn.getStatus() != HttpURLConnection.HTTP_UNSUPPORTED_TYPE) { // the server supports extended status information Assert.assertNotNull(elem); Assert.assertEquals(elem.getStatus(), TransactionStatusElement.TransactionActive); Assert.assertEquals(elem.getTxnURI(), txn.getTxnUri()); //TODO Assert.assertEquals(elem.getTerminatorURI(), txn.getTerminatorURI()); Assert.assertEquals(elem.getDurableParticipantEnlistmentURI(), txn.getDurableParticipantEnlistmentURI()); Assert.assertEquals(elem.getVolatileParticipantEnlistmentURI(), txn.getVolatileParticipantEnlistmentURI()); } else { log.warn("Not testing extended transaction information (reason not supported)"); } } @Test public void testStatusWithParticipants() throws Exception { TxSupport txn = new TxSupport(); txn.startTx(); // enlist two Transactional Participants with the transaction for (int i = 0; i < 2; i++) txn.enlistTestResource(PURL, false); /* 288Additional information about the transaction, such as the number of participants and their 289individual URIs, MAY be returned if the client specifies the application/txstatusext+xml and the 290implementation supports that type. */ CoordinatorElement elem = txn.getTransactionInfo(); if (txn.getStatus() != HttpURLConnection.HTTP_UNSUPPORTED_TYPE) { // the server supports extended status information Assert.assertNotNull(elem); Assert.assertEquals(elem.getStatus(), TransactionStatusElement.TransactionActive); Assert.assertEquals(elem.getTxnURI(), txn.getTxnUri()); //TODO Assert.assertEquals(elem.getTerminatorURI(), txn.getTerminatorURI()); Assert.assertEquals(elem.getDurableParticipantEnlistmentURI(), txn.getDurableParticipantEnlistmentURI()); List<TwoPhaseAwareParticipantElement> participants = elem.getTwoPhaseAware(); Assert.assertEquals(participants.size(), 2); TwoPhaseAwareParticipantElement p1 = participants.get(0); TwoPhaseAwareParticipantElement p2 = participants.get(1); Assert.assertFalse(p1.getTerminatorURI().equals(p2.getTerminatorURI())); Assert.assertFalse(p1.getRecoveryURI().equals(p2.getRecoveryURI())); Assert.assertFalse(p1.getResourceURI().equals(p2.getResourceURI())); } else { log.warn("Not testing extended transaction information (reason not supported)"); } } @Test public void testTransactionStatistics() throws Exception { TxSupport txn = new TxSupport(); txn.startTx(); /* 262Performing a GET on the /transaction-manager URI returns a list of all transaction -coordinator 263URIs known to the coordinator (active and in recovery). The returned response MAY include a 264link header with rel attribute "statistics" linking to a resource that contains statistical information 265such as the number of transactions that have committed and aborted. The link MAY contain a */ TransactionStatisticsElement stats1 = txn.getTransactionStatistics(); txn.commitTx(); TransactionStatisticsElement stats2 = txn.getTransactionStatistics(); // although stats are optional this implementation supports them if (stats1 != null) { Assert.assertEquals(stats1.getRolledback(), stats2.getRolledback()); Assert.assertEquals(stats1.getActive(), stats2.getActive() + 1); Assert.assertEquals(stats1.getPrepared(), stats2.getPrepared()); Assert.assertEquals(stats1.getCommitted(), stats2.getCommitted() - 1); } else { log.warn("Not testing transaction statistics (reason not supported)"); } } @Test public void testTMExtMediaType() throws Exception { TxSupport txn1 = new TxSupport(); TxSupport txn2 = new TxSupport(); TransactionManagerElement tme1, tme2; TransactionStatisticsElement stats1, stats2; txn1.startTx(); txn2.startTx(); /* 269Performing a GET on the transaction-manager URI with media type application/txstatusext+xml 270returns extended information about the transaction-manager resource such as how long it has been 271up and all transaction-coordinator URIs. */ tme1 = txn1.getTransactionManagerInfo(); if (txn1.getStatus() == HttpsURLConnection.HTTP_UNSUPPORTED_TYPE) { log.warn("Not testing extended transaction manager info (reason not supported)"); return; } Assert.assertEquals(TxStatusMediaType.TX_COMMITTED, txn2.commitTx()); Assert.assertEquals(TxStatusMediaType.TX_ROLLEDBACK, txn1.rollbackTx()); tme2 = txn1.getTransactionManagerInfo(); Date d1 = tme1.getCreated(); Date d2 = new Date(); // sanity check the creation time of the transaction manager Assert.assertNotNull(d1); Assert.assertEquals(d1, tme2.getCreated()); Assert.assertTrue(d2.after(d1)); stats1 = tme1.getStatistics(); stats2 = tme2.getStatistics(); // test that the number of active transactions decreased by 2 Assert.assertEquals(stats1.getActive(), stats2.getActive() + 2); // test that the number of prepared transactions us zero Assert.assertEquals(stats1.getPrepared(), stats2.getPrepared()); // test that the number of committed transactions increased by 1 Assert.assertEquals(stats1.getCommitted(), stats2.getCommitted() - 1); // test that the number of aborted transactions increased by 1 Assert.assertEquals(stats1.getRolledback(), stats2.getRolledback() - 1); List<String> txns1 = tme1.getCoordinatorURIs(); List<String> txns2 = tme2.getCoordinatorURIs(); // test that the number of transactions has decreased by 2 Assert.assertEquals(txns1.size(), txns2.size() + 2); // test that the first list contains both transactions Assert.assertTrue(txns1.contains(txn1.getTxnUri())); Assert.assertTrue(txns1.contains(txn2.getTxnUri())); } @Test public void testEnlistVolatileParticipant() { String enlistUrl = PURL + "?isVolatile=true"; String[] workIds = new String[1]; TxSupport txn = new TxSupport(60000); txn.startTx(); // enlist Transactional Participants and volatile participants with the transaction for (int i = 0; i < workIds.length; i++) { /* * the resource implementation will enlist in the volatile protocol twice: * - once directly using the coordinator volatile-participant registration link * - and then indirectly during participant enlistment by including a link header with * value TxLinkRel.VOLATILE_PARTICIPANT * This will mean that two before and after synchronisations will be called resulting in a * total of 4 synchronisations */ workIds[i] = txn.enlistTestResource(enlistUrl, true); } txn.commitTx(); for (int i = 0; i < workIds.length; i++) { String syncCount = getResourceProperty(txn, PURL, workIds[i], "syncCount"); // there should have been 2 before and 2 after synchronisation calls: Assert.assertEquals(syncCount, "4"); } } @Test public void testEnlistVolatileParticipantWithRollbackOnly() { String enlistUrl = PURL + "?isVolatile=true"; String[] workIds = new String[1]; TxSupport txn = new TxSupport(60000); txn.startTx(); Assert.assertEquals(TxStatusMediaType.TX_ROLLBACK_ONLY, txn.markTxRollbackOnly()); // enlisting a participant into a transaction that is marked rollback only should fail: // 385If the transaction is not TransactionActive when registration is attempted, then the implementation // 386MUST return a 412 status code. for (int i = 0; i < workIds.length; i++) { /* * the resource implementation will enlist in the volatile protocol twice: * - once directly using the coordinator volatile-participant registration link * - and then indirectly during participant enlistment by including a link header with * value TxLinkRel.VOLATILE_PARTICIPANT * This will mean that two before and after synchronisations will be called resulting in a * total of 4 synchronisations */ try { workIds[i] = txn.enlistTestResource(enlistUrl, true); Assert.fail("Should have thrown 412"); } catch (HttpResponseException e) { if (e.getActualResponse() != HttpURLConnection.HTTP_PRECON_FAILED) Assert.fail("Should have thrown 412 but actual response was " + e.getActualResponse()); } } Assert.assertEquals(TxStatusMediaType.TX_ROLLEDBACK, txn.rollbackTx()); } @Test public void testVolatilePrepareFail() { String enlistUrl = PURL + "?isVolatile=true&fault=V_PREPARE"; String[] workIds = new String[1]; TxSupport txn = new TxSupport(60000); txn.startTx(); // enlist Transactional Participants and volatile participants with the transaction for (int i = 0; i < workIds.length; i++) { /* * the resource implementation will enlist in the volatile protocol twice: * - once directly using the coordinator volatile-participant registration link * - and then indirectly during participant enlistment by including a link header with * value TxLinkRel.VOLATILE_PARTICIPANT * This will mean that two before and after synchronisations will be called resulting in a * total of 4 synchronisations */ workIds[i] = txn.enlistTestResource(enlistUrl, true); } /* 555In this case the Volatile prepare phase executes prior to the Durable prepare where the 556transaction-coordinator sends a PUT request to the registered volatile-participant: only if this 557prepare succeeds will the Durable protocol be executed */ // the volatile participants should have failed the volatile prepare phase ( "?fault=V_PREPARE" in the url) Assert.assertEquals(TxStatusMediaType.TX_ROLLEDBACK, txn.commitTx()); for (int i = 0; i < workIds.length; i++) { String syncCount = getResourceProperty(txn, PURL, workIds[i], "syncCount"); // there should have been 2 before and 2 after synchronisation calls: Assert.assertEquals(syncCount, "4"); } } }