/*
* Bitronix Transaction Manager
*
* Copyright (c) 2010, Bitronix Software.
*
* 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, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY 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
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package bitronix.tm.mock;
import java.lang.reflect.Proxy;
import java.sql.*;
import java.util.List;
import javax.sql.XAConnection;
import javax.transaction.*;
import javax.transaction.xa.*;
import bitronix.tm.*;
import bitronix.tm.mock.events.*;
import bitronix.tm.mock.resource.MockXAResource;
import bitronix.tm.mock.resource.jdbc.*;
import bitronix.tm.mock.resource.jms.MockConnectionFactory;
import bitronix.tm.resource.jdbc.*;
import bitronix.tm.resource.jdbc.lrc.LrcXADataSource;
import bitronix.tm.resource.jms.PoolingConnectionFactory;
import bitronix.tm.resource.jms.lrc.LrcXAConnectionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author lorban
*/
public class NewJdbcWrongUsageMockTest extends AbstractMockJdbcTest {
private final static Logger log = LoggerFactory.getLogger(NewJdbcWrongUsageMockTest.class);
public void testPrepareXAFailureCase() throws Exception {
BitronixTransactionManager tm = TransactionManagerServices.getTransactionManager();
tm.begin();
Connection connection1 = poolingDataSource1.getConnection();
JdbcConnectionHandle handle = (JdbcConnectionHandle) Proxy.getInvocationHandler(connection1);
JdbcPooledConnection pc1 = handle.getPooledConnection();
XAConnection xaConnection1 = (XAConnection) getWrappedXAConnectionOf(pc1);
MockXAResource mockXAResource = (MockXAResource) xaConnection1.getXAResource();
XAException xaException = new XAException("resource failed");
xaException.errorCode = XAException.XAER_RMERR;
mockXAResource.setPrepareException(xaException);
connection1.createStatement();
Connection connection2 = poolingDataSource2.getConnection();
connection2.createStatement();
connection1.close();
connection2.close();
try {
tm.commit();
fail("TM should have thrown rollback exception");
} catch (RollbackException ex) {
assertTrue("Got: " + ex.getMessage(), ex.getMessage().matches("transaction failed to prepare: a Bitronix Transaction with GTRID (.*?) status=ROLLEDBACK, 2 resource\\(s\\) enlisted (.*?)"));
assertTrue("Got: " + ex.getCause().getMessage(), ex.getCause().getMessage().matches("transaction failed during prepare of a Bitronix Transaction with GTRID (.*?) status=PREPARING, 2 resource\\(s\\) enlisted (.*?) resource\\(s\\) \\[pds1\\] threw unexpected exception"));
assertEquals("collected 1 exception(s):" + System.getProperty("line.separator") +
" [pds1 - javax.transaction.xa.XAException(XAER_RMERR) - resource failed]", ex.getCause().getCause().getMessage());
}
// check flow
List orderedEvents = EventRecorder.getOrderedEvents();
log.info(EventRecorder.dumpToString());
assertEquals(16, orderedEvents.size());
int i=0;
assertEquals(Status.STATUS_ACTIVE, ((JournalLogEvent) orderedEvents.get(i++)).getStatus());
assertEquals(DATASOURCE1_NAME, ((ConnectionDequeuedEvent) orderedEvents.get(i++)).getPooledConnectionImpl().getPoolingDataSource().getUniqueName());
assertEquals(XAResource.TMNOFLAGS, ((XAResourceStartEvent) orderedEvents.get(i++)).getFlag());
assertEquals(DATASOURCE2_NAME, ((ConnectionDequeuedEvent) orderedEvents.get(i++)).getPooledConnectionImpl().getPoolingDataSource().getUniqueName());
assertEquals(XAResource.TMNOFLAGS, ((XAResourceStartEvent) orderedEvents.get(i++)).getFlag());
assertEquals(XAResource.TMSUCCESS, ((XAResourceEndEvent) orderedEvents.get(i++)).getFlag());
assertEquals(XAResource.TMSUCCESS, ((XAResourceEndEvent) orderedEvents.get(i++)).getFlag());
assertEquals(Status.STATUS_PREPARING, ((JournalLogEvent) orderedEvents.get(i++)).getStatus());
XAResourcePrepareEvent prepareEvent1 = (XAResourcePrepareEvent) orderedEvents.get(i++);
assertEquals("resource failed", prepareEvent1.getException().getMessage());
XAResourcePrepareEvent prepareEvent2 = (XAResourcePrepareEvent) orderedEvents.get(i++);
assertEquals(XAResource.XA_OK, prepareEvent2.getReturnCode());
assertEquals(Status.STATUS_ROLLING_BACK, ((JournalLogEvent) orderedEvents.get(i++)).getStatus());
XAResourceRollbackEvent rollbackEvent1 = (XAResourceRollbackEvent) orderedEvents.get(i++);
assertTrue(prepareEvent2.getSource() == rollbackEvent1.getSource());
XAResourceRollbackEvent rollbackEvent2 = (XAResourceRollbackEvent) orderedEvents.get(i++);
assertTrue(prepareEvent1.getSource() == rollbackEvent2.getSource());
assertEquals(Status.STATUS_ROLLEDBACK, ((JournalLogEvent) orderedEvents.get(i++)).getStatus());
assertEquals(DATASOURCE1_NAME, ((ConnectionQueuedEvent) orderedEvents.get(i++)).getPooledConnectionImpl().getPoolingDataSource().getUniqueName());
assertEquals(DATASOURCE2_NAME, ((ConnectionQueuedEvent) orderedEvents.get(i++)).getPooledConnectionImpl().getPoolingDataSource().getUniqueName());
}
public void testPrepareRuntimeFailureCase() throws Exception {
BitronixTransactionManager tm = TransactionManagerServices.getTransactionManager();
tm.begin();
Connection connection1 = poolingDataSource1.getConnection();
JdbcConnectionHandle handle = (JdbcConnectionHandle) Proxy.getInvocationHandler(connection1);
JdbcPooledConnection pc1 = handle.getPooledConnection();
XAConnection xaConnection1 = (XAConnection) getWrappedXAConnectionOf(pc1);
MockXAResource mockXAResource = (MockXAResource) xaConnection1.getXAResource();
mockXAResource.setPrepareException(new RuntimeException("driver error"));
connection1.createStatement();
Connection connection2 = poolingDataSource2.getConnection();
connection2.createStatement();
connection1.close();
connection2.close();
try {
tm.commit();
fail("TM should have thrown exception");
} catch (RollbackException ex) {
assertTrue("Got: " + ex.getMessage(), ex.getMessage().matches("transaction failed to prepare: a Bitronix Transaction with GTRID (.*?) status=ROLLEDBACK, 2 resource\\(s\\) enlisted (.*?)"));
assertTrue("Got: " + ex.getCause().getMessage(), ex.getCause().getMessage().matches("transaction failed during prepare of a Bitronix Transaction with GTRID (.*?) status=PREPARING, 2 resource\\(s\\) enlisted (.*?) resource\\(s\\) \\[pds1\\] threw unexpected exception"));
assertEquals("collected 1 exception(s):" + System.getProperty("line.separator") +
" [pds1 - java.lang.RuntimeException - driver error]", ex.getCause().getCause().getMessage());
}
// check flow
List orderedEvents = EventRecorder.getOrderedEvents();
log.info(EventRecorder.dumpToString());
assertEquals(16, orderedEvents.size());
int i=0;
assertEquals(Status.STATUS_ACTIVE, ((JournalLogEvent) orderedEvents.get(i++)).getStatus());
assertEquals(DATASOURCE1_NAME, ((ConnectionDequeuedEvent) orderedEvents.get(i++)).getPooledConnectionImpl().getPoolingDataSource().getUniqueName());
assertEquals(XAResource.TMNOFLAGS, ((XAResourceStartEvent) orderedEvents.get(i++)).getFlag());
assertEquals(DATASOURCE2_NAME, ((ConnectionDequeuedEvent) orderedEvents.get(i++)).getPooledConnectionImpl().getPoolingDataSource().getUniqueName());
assertEquals(XAResource.TMNOFLAGS, ((XAResourceStartEvent) orderedEvents.get(i++)).getFlag());
assertEquals(XAResource.TMSUCCESS, ((XAResourceEndEvent) orderedEvents.get(i++)).getFlag());
assertEquals(XAResource.TMSUCCESS, ((XAResourceEndEvent) orderedEvents.get(i++)).getFlag());
assertEquals(Status.STATUS_PREPARING, ((JournalLogEvent) orderedEvents.get(i++)).getStatus());
XAResourcePrepareEvent prepareEvent1 = (XAResourcePrepareEvent) orderedEvents.get(i++);
assertEquals("driver error", prepareEvent1.getException().getMessage());
XAResourcePrepareEvent prepareEvent2 = (XAResourcePrepareEvent) orderedEvents.get(i++);
assertEquals(XAResource.XA_OK, prepareEvent2.getReturnCode());
assertEquals(Status.STATUS_ROLLING_BACK, ((JournalLogEvent) orderedEvents.get(i++)).getStatus());
XAResourceRollbackEvent rollbackEvent1 = (XAResourceRollbackEvent) orderedEvents.get(i++);
assertTrue(prepareEvent2.getSource() == rollbackEvent1.getSource());
XAResourceRollbackEvent rollbackEvent2 = (XAResourceRollbackEvent) orderedEvents.get(i++);
assertTrue(prepareEvent1.getSource() == rollbackEvent2.getSource());
assertEquals(Status.STATUS_ROLLEDBACK, ((JournalLogEvent) orderedEvents.get(i++)).getStatus());
assertEquals(DATASOURCE1_NAME, ((ConnectionQueuedEvent) orderedEvents.get(i++)).getPooledConnectionImpl().getPoolingDataSource().getUniqueName());
assertEquals(DATASOURCE2_NAME, ((ConnectionQueuedEvent) orderedEvents.get(i++)).getPooledConnectionImpl().getPoolingDataSource().getUniqueName());
}
public void testIncorrectSuspendResume() throws Exception {
BitronixTransactionManager tm = TransactionManagerServices.getTransactionManager();
tm.begin();
Connection connection1 = poolingDataSource1.getConnection();
connection1.createStatement();
Connection connection2 = poolingDataSource2.getConnection();
connection2.createStatement();
Transaction tx = tm.suspend();
assertNull(tm.suspend());
try {
tm.resume(null);
fail("TM has allowed resuming a null TX context");
} catch (InvalidTransactionException ex) {
assertEquals("resumed transaction cannot be null", ex.getMessage());
}
tm.resume(tx);
try {
tm.resume(tx);
fail("TM has allowed resuming a TX context when another one is still running");
} catch (IllegalStateException ex) {
assertEquals("a transaction is already running on this thread", ex.getMessage());
}
connection1.close();
connection2.close();
tm.commit();
}
public void testEagerEnding() throws Exception {
BitronixTransactionManager tm = TransactionManagerServices.getTransactionManager();
try {
tm.rollback();
fail("TM allowed rollback with no TX started");
} catch (IllegalStateException ex) {
assertEquals("no transaction started on this thread", ex.getMessage());
}
try {
tm.commit();
fail("TM allowed commit with no TX started");
} catch (IllegalStateException ex) {
assertEquals("no transaction started on this thread", ex.getMessage());
}
}
public void testRegisterTwoLrc() throws Exception {
BitronixTransactionManager tm = TransactionManagerServices.getTransactionManager();
PoolingDataSource lrcDs1 = new PoolingDataSource();
lrcDs1.setClassName(LrcXADataSource.class.getName());
lrcDs1.setUniqueName(DATASOURCE1_NAME + "_lrc");
lrcDs1.setMinPoolSize(POOL_SIZE);
lrcDs1.setMaxPoolSize(POOL_SIZE);
lrcDs1.setAllowLocalTransactions(true);
lrcDs1.getDriverProperties().setProperty("driverClassName", MockDriver.class.getName());
lrcDs1.getDriverProperties().setProperty("url", "");
lrcDs1.init();
PoolingDataSource lrcDs2 = new PoolingDataSource();
lrcDs2.setClassName(LrcXADataSource.class.getName());
lrcDs2.setUniqueName(DATASOURCE2_NAME + "_lrc");
lrcDs2.setMinPoolSize(POOL_SIZE);
lrcDs2.setMaxPoolSize(POOL_SIZE);
lrcDs2.setAllowLocalTransactions(true);
lrcDs2.getDriverProperties().setProperty("driverClassName", MockDriver.class.getName());
lrcDs2.getDriverProperties().setProperty("url", "");
lrcDs2.init();
tm.begin();
Connection c1 = lrcDs1.getConnection();
c1.createStatement();
c1.close();
Connection c2 = lrcDs2.getConnection();
try {
c2.createStatement();
fail("expected SQLException");
} catch (SQLException ex) {
assertTrue(ex.getMessage().startsWith("error enlisting a JdbcConnectionHandle of a JdbcPooledConnection from datasource pds2_lrc in state ACCESSIBLE with usage count 1 wrapping a JDBC LrcXAConnection on Mock for Connection"));
assertTrue(ex.getCause().getMessage().matches("cannot enlist more than one non-XA resource, tried enlisting an XAResourceHolderState with uniqueName=pds2_lrc XAResource=a JDBC LrcXAResource in state NO_TX with XID null, already enlisted: an XAResourceHolderState with uniqueName=pds1_lrc XAResource=a JDBC LrcXAResource in state STARTED \\(started\\) with XID a Bitronix XID .*"));
}
c2.close();
tm.commit();
lrcDs2.close();
lrcDs1.close();
}
public void testRegisterTwoLrcJms() throws Exception {
BitronixTransactionManager tm = TransactionManagerServices.getTransactionManager();
PoolingConnectionFactory pcf = new PoolingConnectionFactory();
pcf.setClassName(LrcXAConnectionFactory.class.getName());
pcf.setUniqueName("pcf_lrc");
pcf.setMaxPoolSize(1);
pcf.getDriverProperties().setProperty("connectionFactoryClassName", MockConnectionFactory.class.getName());
pcf.init();
PoolingDataSource lrcDs2 = new PoolingDataSource();
lrcDs2.setClassName(LrcXADataSource.class.getName());
lrcDs2.setUniqueName(DATASOURCE2_NAME + "_lrc");
lrcDs2.setMinPoolSize(POOL_SIZE);
lrcDs2.setMaxPoolSize(POOL_SIZE);
lrcDs2.setAllowLocalTransactions(true);
lrcDs2.getDriverProperties().setProperty("driverClassName", MockDriver.class.getName());
lrcDs2.getDriverProperties().setProperty("url", "");
lrcDs2.init();
tm.begin();
javax.jms.Connection c = pcf.createConnection();
javax.jms.Session s = c.createSession(true, 0);
javax.jms.MessageProducer p = s.createProducer(null);
p.send(null);
c.close();
Connection c2 = lrcDs2.getConnection();
try {
c2.createStatement();
fail("expected SQLException");
} catch (SQLException ex) {
assertTrue(ex.getMessage().startsWith("error enlisting a JdbcConnectionHandle of a JdbcPooledConnection from datasource pds2_lrc in state ACCESSIBLE with usage count 1 wrapping a JDBC LrcXAConnection on Mock for Connection"));
assertTrue(ex.getCause().getMessage().startsWith("cannot enlist more than one non-XA resource, tried enlisting an XAResourceHolderState with uniqueName=pds2_lrc XAResource=a JDBC LrcXAResource in state NO_TX with XID null, already enlisted: an XAResourceHolderState with uniqueName=pcf_lrc XAResource=a JMS LrcXAResource in state STARTED of session Mock for Session"));
}
c2.close();
tm.commit();
lrcDs2.close();
pcf.close();
}
}