/* * JBoss, Home of Professional Open Source * Copyright 2013, Red Hat, Inc. and/or its affiliates, and individual * contributors by the @authors tag. See the copyright.txt in the * distribution for a full listing of individual contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.hp.mwtests.ts.jta.commitmarkable.integration; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Date; import java.util.Enumeration; import java.util.Vector; import java.util.concurrent.atomic.AtomicInteger; import javax.annotation.Resource; import javax.inject.Inject; import javax.sql.DataSource; import javax.transaction.HeuristicMixedException; import javax.transaction.HeuristicRollbackException; import javax.transaction.NotSupportedException; import javax.transaction.RollbackException; import javax.transaction.Status; import javax.transaction.SystemException; import javax.transaction.TransactionManager; import javax.transaction.UserTransaction; import org.h2.jdbcx.JdbcDataSource; import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.junit.Arquillian; import org.jboss.shrinkwrap.api.ArchivePaths; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.asset.EmptyAsset; import org.jboss.shrinkwrap.api.asset.StringAsset; import org.jboss.shrinkwrap.api.spec.JavaArchive; import org.junit.Test; import org.junit.runner.RunWith; import com.arjuna.ats.arjuna.exceptions.ObjectStoreException; import com.arjuna.ats.arjuna.recovery.RecoveryManager; import com.arjuna.ats.arjuna.recovery.RecoveryModule; import com.arjuna.ats.internal.jta.recovery.arjunacore.CommitMarkableResourceRecordRecoveryModule; import com.arjuna.ats.jta.common.JTAEnvironmentBean; import com.arjuna.common.internal.util.propertyservice.BeanPopulator; import com.hp.mwtests.ts.jta.commitmarkable.DummyXAResource; import com.hp.mwtests.ts.jta.commitmarkable.Utils; @RunWith(Arquillian.class) public class CMRIntegrationTest { private static final String DEPENDENCIES = "Dependencies: com.h2database.h2, org.jboss.jts, org.jboss.jboss-transaction-spi\n"; @Deployment public static JavaArchive createTestArchive() { return ShrinkWrap .create(JavaArchive.class, "test.jar") .addClasses(DummyXAResource.class, Utils.class) .addPackage("io.narayana.connectableresource") .addAsManifestResource(new StringAsset(DEPENDENCIES), "MANIFEST.MF") .addAsManifestResource(EmptyAsset.INSTANCE, ArchivePaths.create("beans.xml")); } @Resource(mappedName = "java:jboss/datasources/ExampleDS") private javax.sql.DataSource ds; @Inject private UserTransaction userTransaction; @Resource(mappedName = "java:jboss/TransactionManager") private TransactionManager tm; @Test public void testCMR() throws Exception { Utils.createTables(ds.getConnection()); doTest(ds); } // @Test public void testCMR1() throws Exception { Utils.createTables(ds.getConnection()); try { userTransaction.begin(); tm.getTransaction().enlistResource(new DummyXAResource()); Connection connection = ds.getConnection(); Statement createStatement = connection.createStatement(); createStatement.execute("INSERT INTO foo (bar) VALUES (1)"); createStatement.close(); userTransaction.commit(); } catch (Exception e) { System.out.printf("XXX txn excp: %s%n", e.getCause()); } finally { try { if (userTransaction.getStatus() == Status.STATUS_ACTIVE || userTransaction.getStatus() == Status.STATUS_MARKED_ROLLBACK) userTransaction.rollback(); } catch (Throwable e) { System.out.printf("XXX txn did not finish: %s%n", e.getCause()); } } } private int threadCount = 5; private int iterationCount = 20; private int waiting; private boolean go; private final Object waitLock = new Object(); private AtomicInteger totalExecuted = new AtomicInteger(); public void doTest(final DataSource dataSource) throws Exception { // Test code Thread[] threads = new Thread[threadCount]; for (int i = 0; i < threads.length; i++) { threads[i] = new Thread(new Runnable() { public void run() { synchronized (waitLock) { waiting++; waitLock.notify(); } synchronized (CMRIntegrationTest.this) { while (!go) { try { CMRIntegrationTest.this.wait(); } catch (InterruptedException e) { e.printStackTrace(); return; } } } int success = 0; Connection connection = null; int faultType = Integer.getInteger( "com.hp.mwtests.ts.jta.commitmarkable.integration.CMRIntegrationTest", 0); for (int i = 0; i < iterationCount; i++) { try { userTransaction.begin(); tm.getTransaction().enlistResource( new DummyXAResource()); connection = dataSource.getConnection(); Statement createStatement = connection .createStatement(); createStatement .execute("INSERT INTO foo (bar) VALUES (1)"); // System.out.printf("XXX txn close%n"); if (faultType == 1) Runtime.getRuntime().halt(0); userTransaction.commit(); connection.close(); // This wouldn't work for a // none-JCA code as commit has // closed the connection - it // helps us though as JCA seems // to rely on finalize // System.out // .printf("committed txn iteration %d%n", i); success++; } catch (SQLException e) { System.err.println("boom"); e.printStackTrace(); if (e.getCause() != null) { e.getCause().printStackTrace(); } SQLException nextException = e.getNextException(); while (nextException != null) { nextException.printStackTrace(); nextException = nextException .getNextException(); } Throwable[] suppressed = e.getSuppressed(); for (int j = 0; j < suppressed.length; j++) { suppressed[j].printStackTrace(); } try { userTransaction.rollback(); } catch (IllegalStateException | SecurityException | SystemException e1) { e1.printStackTrace(); fail("Problem with transaction"); } } catch (NotSupportedException | SystemException | IllegalStateException | RollbackException | SecurityException | HeuristicMixedException | HeuristicRollbackException e) { e.printStackTrace(); fail("Problem with transaction"); } finally { if (connection != null) try { connection.close(); } catch (SQLException e) { e.printStackTrace(); // To change body of // catch statement // use File | // Settings | File // Templates. } } } totalExecuted.addAndGet(success); } }); threads[i].start(); } synchronized (waitLock) { while (waiting < threads.length) { waitLock.wait(); } } long startTime = -1; synchronized (CMRIntegrationTest.this) { go = true; CMRIntegrationTest.this.notifyAll(); startTime = System.currentTimeMillis(); } for (int i = 0; i < threads.length; i++) { threads[i].join(); } long endTime = System.currentTimeMillis(); System.out.println(new Date() + " Number of transactions: " + totalExecuted.intValue()); long additionalCleanuptime = 0L; // postRunCleanup(dataSource); long timeInMillis = (endTime - startTime) + additionalCleanuptime; System.out.printf(" Total time millis: %d%n", timeInMillis); System.out.printf(" Average transaction time: %d%n", timeInMillis / totalExecuted.intValue()); System.out .printf(" Transactions per second: %d%n", Math.round((totalExecuted.intValue() / (timeInMillis / 1000d)))); checkFooSize(dataSource); } private void checkSize(String string, Statement statement, int expected) throws SQLException { ResultSet result = statement.executeQuery("select count(*) from " + string); result.next(); int actual = result.getInt(1); result.close(); assertEquals(expected, actual); } public void checkFooSize(DataSource dataSource) throws SQLException, HeuristicRollbackException, RollbackException, HeuristicMixedException, SystemException, NotSupportedException { userTransaction.begin(); Connection connection = dataSource.getConnection(); String tableToCheck = "foo"; Statement statement = connection.createStatement(); checkSize(tableToCheck, statement, threadCount * iterationCount); statement.close(); userTransaction.commit(); connection.close(); } private CommitMarkableResourceRecordRecoveryModule getCRRRM() { CommitMarkableResourceRecordRecoveryModule crrrm = null; RecoveryManager recMan = RecoveryManager.manager(); Vector recoveryModules = recMan.getModules(); if (recoveryModules != null) { Enumeration modules = recoveryModules.elements(); while (modules.hasMoreElements()) { RecoveryModule m = (RecoveryModule) modules.nextElement(); if (m instanceof CommitMarkableResourceRecordRecoveryModule) { return (CommitMarkableResourceRecordRecoveryModule) m; } } } return null; } public long postRunCleanup(DataSource dataSource) throws SQLException, ObjectStoreException { Connection connection = dataSource.getConnection(); CommitMarkableResourceRecordRecoveryModule crrrm = getCRRRM(); int expectedReapableConnectableResourceRecords = BeanPopulator .getDefaultInstance(JTAEnvironmentBean.class) .isPerformImmediateCleanupOfCommitMarkableResourceBranches() ? 0 : threadCount * iterationCount; try { Statement statement = connection.createStatement(); checkSize("xids", statement, expectedReapableConnectableResourceRecords); if (expectedReapableConnectableResourceRecords > 0) { // The recovery module has to perform lookups // new InitialContext().rebind("connectableresource", // recoveryDataSource); long startTime = System.currentTimeMillis(); crrrm.periodicWorkFirstPass(); crrrm.periodicWorkSecondPass(); long endTime = System.currentTimeMillis(); checkSize("xids", statement, 0); statement.close(); System.out.println(" Total cleanup time: " + (endTime - startTime) + " Average cleanup time: " + (endTime - startTime) / expectedReapableConnectableResourceRecords); return endTime - startTime; } else { statement.close(); } } finally { connection.close(); } return 0; } }