mirror of https://github.com/apache/activemq.git
fix - AMQ-2034 - have close in XA transaction deferred to synchronisation after completion, have rollback call beforeEnd to propagate acknowledgements; add a bunch of tests
git-svn-id: https://svn.apache.org/repos/asf/activemq/trunk@726764 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
7aec12eaca
commit
50a98e3b0e
|
@ -590,11 +590,27 @@ public class ActiveMQMessageConsumer implements MessageAvailableConsumer, StatsC
|
||||||
*/
|
*/
|
||||||
public void close() throws JMSException {
|
public void close() throws JMSException {
|
||||||
if (!unconsumedMessages.isClosed()) {
|
if (!unconsumedMessages.isClosed()) {
|
||||||
dispose();
|
if (session.isTransacted() && session.getTransactionContext().getTransactionId() != null) {
|
||||||
this.session.asyncSendPacket(info.createRemoveCommand());
|
session.getTransactionContext().addSynchronization(new Synchronization() {
|
||||||
|
public void afterCommit() throws Exception {
|
||||||
|
doClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void afterRollback() throws Exception {
|
||||||
|
doClose();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
doClose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void doClose() throws JMSException {
|
||||||
|
dispose();
|
||||||
|
this.session.asyncSendPacket(info.createRemoveCommand());
|
||||||
|
}
|
||||||
|
|
||||||
void clearMessagesInProgress() {
|
void clearMessagesInProgress() {
|
||||||
// we are called from inside the transport reconnection logic
|
// we are called from inside the transport reconnection logic
|
||||||
// which involves us clearing all the connections' consumers
|
// which involves us clearing all the connections' consumers
|
||||||
|
@ -653,10 +669,14 @@ public class ActiveMQMessageConsumer implements MessageAvailableConsumer, StatsC
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// Do we have any acks we need to send out before closing?
|
// Do we have any acks we need to send out before closing?
|
||||||
// Ack any delivered messages now. (session may still
|
// Ack any delivered messages now.
|
||||||
// commit/rollback the acks).
|
|
||||||
// only processes optimized acknowledgements
|
// only processes optimized acknowledgements
|
||||||
deliverAcks();
|
if (!session.isTransacted()) {
|
||||||
|
deliverAcks();
|
||||||
|
if (session.isDupsOkAcknowledge()) {
|
||||||
|
acknowledge();
|
||||||
|
}
|
||||||
|
}
|
||||||
if (executorService != null) {
|
if (executorService != null) {
|
||||||
executorService.shutdown();
|
executorService.shutdown();
|
||||||
try {
|
try {
|
||||||
|
@ -665,9 +685,7 @@ public class ActiveMQMessageConsumer implements MessageAvailableConsumer, StatsC
|
||||||
Thread.currentThread().interrupt();
|
Thread.currentThread().interrupt();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (session.isTransacted() || session.isDupsOkAcknowledge()) {
|
|
||||||
acknowledge();
|
|
||||||
}
|
|
||||||
if (session.isClientAcknowledge()) {
|
if (session.isClientAcknowledge()) {
|
||||||
if (!this.info.isBrowser()) {
|
if (!this.info.isBrowser()) {
|
||||||
// rollback duplicates that aren't acknowledged
|
// rollback duplicates that aren't acknowledged
|
||||||
|
|
|
@ -27,6 +27,7 @@ import javax.jms.XATopicSession;
|
||||||
import javax.transaction.xa.XAResource;
|
import javax.transaction.xa.XAResource;
|
||||||
|
|
||||||
import org.apache.activemq.command.SessionId;
|
import org.apache.activemq.command.SessionId;
|
||||||
|
import org.apache.activemq.transaction.Synchronization;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The XASession interface extends the capability of Session by adding access
|
* The XASession interface extends the capability of Session by adding access
|
||||||
|
@ -96,6 +97,24 @@ public class ActiveMQXASession extends ActiveMQSession implements QueueSession,
|
||||||
return new ActiveMQTopicSession(this);
|
return new ActiveMQTopicSession(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws JMSException {
|
||||||
|
if (getTransactionContext().isInXATransaction()) {
|
||||||
|
getTransactionContext().addSynchronization(new Synchronization() {
|
||||||
|
public void afterCommit() throws Exception {
|
||||||
|
doClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void afterRollback() throws Exception {
|
||||||
|
doClose();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void doClose() throws JMSException {
|
||||||
|
super.close();
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* This is called before transacted work is done by
|
* This is called before transacted work is done by
|
||||||
* the session. XA Work can only be done when this
|
* the session. XA Work can only be done when this
|
||||||
|
|
|
@ -224,6 +224,7 @@ public class TransactionContext implements XAResource {
|
||||||
throw new TransactionInProgressException("Cannot rollback() if an XA transaction is already in progress ");
|
throw new TransactionInProgressException("Cannot rollback() if an XA transaction is already in progress ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
beforeEnd();
|
||||||
if (transactionId != null) {
|
if (transactionId != null) {
|
||||||
TransactionInfo info = new TransactionInfo(getConnectionId(), transactionId, TransactionInfo.ROLLBACK);
|
TransactionInfo info = new TransactionInfo(getConnectionId(), transactionId, TransactionInfo.ROLLBACK);
|
||||||
this.transactionId = null;
|
this.transactionId = null;
|
||||||
|
|
|
@ -16,27 +16,39 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.activemq;
|
package org.apache.activemq;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
|
|
||||||
import javax.jms.Connection;
|
import javax.jms.Connection;
|
||||||
|
import javax.jms.Destination;
|
||||||
import javax.jms.JMSException;
|
import javax.jms.JMSException;
|
||||||
|
import javax.jms.Message;
|
||||||
|
import javax.jms.MessageConsumer;
|
||||||
|
import javax.jms.MessageProducer;
|
||||||
|
import javax.jms.TextMessage;
|
||||||
import javax.jms.XAConnection;
|
import javax.jms.XAConnection;
|
||||||
import javax.jms.XAQueueConnection;
|
import javax.jms.XAQueueConnection;
|
||||||
import javax.jms.XASession;
|
import javax.jms.XASession;
|
||||||
import javax.jms.XATopicConnection;
|
import javax.jms.XATopicConnection;
|
||||||
import javax.transaction.xa.XAException;
|
import javax.transaction.xa.XAException;
|
||||||
import javax.transaction.xa.XAResource;
|
import javax.transaction.xa.XAResource;
|
||||||
|
import javax.transaction.xa.Xid;
|
||||||
|
|
||||||
import org.apache.activemq.broker.BrokerRegistry;
|
import org.apache.activemq.broker.BrokerRegistry;
|
||||||
import org.apache.activemq.broker.BrokerService;
|
import org.apache.activemq.broker.BrokerService;
|
||||||
import org.apache.activemq.broker.TransportConnector;
|
import org.apache.activemq.broker.TransportConnector;
|
||||||
|
import org.apache.activemq.command.ActiveMQQueue;
|
||||||
|
import org.apache.activemq.command.ActiveMQTextMessage;
|
||||||
import org.apache.activemq.transport.stomp.StompTransportFilter;
|
import org.apache.activemq.transport.stomp.StompTransportFilter;
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
public class ActiveMQXAConnectionFactoryTest extends CombinationTestSupport {
|
public class ActiveMQXAConnectionFactoryTest extends CombinationTestSupport {
|
||||||
private static final Log LOG = LogFactory.getLog(ActiveMQXAConnectionFactoryTest.class);
|
private static final Log LOG = LogFactory.getLog(ActiveMQXAConnectionFactoryTest.class);
|
||||||
|
long txGenerator = System.currentTimeMillis();
|
||||||
|
|
||||||
public void testCopy() throws URISyntaxException, JMSException {
|
public void testCopy() throws URISyntaxException, JMSException {
|
||||||
ActiveMQXAConnectionFactory cf = new ActiveMQXAConnectionFactory("vm://localhost?");
|
ActiveMQXAConnectionFactory cf = new ActiveMQXAConnectionFactory("vm://localhost?");
|
||||||
|
@ -117,6 +129,126 @@ public class ActiveMQXAConnectionFactoryTest extends CombinationTestSupport {
|
||||||
connection2.close();
|
connection2.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testVanilaTransactionalProduceReceive() throws Exception {
|
||||||
|
|
||||||
|
ActiveMQXAConnectionFactory cf1 = new ActiveMQXAConnectionFactory("vm://localhost?broker.persistent=false");
|
||||||
|
XAConnection connection1 = (XAConnection)cf1.createConnection();
|
||||||
|
connection1.start();
|
||||||
|
XASession session = connection1.createXASession();
|
||||||
|
XAResource resource = session.getXAResource();
|
||||||
|
Destination dest = new ActiveMQQueue(getName());
|
||||||
|
|
||||||
|
// publish a message
|
||||||
|
Xid tid = createXid();
|
||||||
|
resource.start(tid, XAResource.TMNOFLAGS);
|
||||||
|
MessageProducer producer = session.createProducer(dest);
|
||||||
|
ActiveMQTextMessage message = new ActiveMQTextMessage();
|
||||||
|
message.setText(getName());
|
||||||
|
producer.send(message);
|
||||||
|
resource.end(tid, XAResource.TMSUCCESS);
|
||||||
|
resource.commit(tid, true);
|
||||||
|
session.close();
|
||||||
|
|
||||||
|
session = connection1.createXASession();
|
||||||
|
MessageConsumer consumer = session.createConsumer(dest);
|
||||||
|
tid = createXid();
|
||||||
|
resource = session.getXAResource();
|
||||||
|
resource.start(tid, XAResource.TMNOFLAGS);
|
||||||
|
TextMessage receivedMessage = (TextMessage) consumer.receive(1000);
|
||||||
|
assertNotNull(receivedMessage);
|
||||||
|
assertEquals(getName(), receivedMessage.getText());
|
||||||
|
resource.end(tid, XAResource.TMSUCCESS);
|
||||||
|
resource.commit(tid, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testConsumerCloseTransactionalSendReceive() throws Exception {
|
||||||
|
|
||||||
|
ActiveMQXAConnectionFactory cf1 = new ActiveMQXAConnectionFactory("vm://localhost?broker.persistent=false");
|
||||||
|
XAConnection connection1 = (XAConnection)cf1.createConnection();
|
||||||
|
connection1.start();
|
||||||
|
XASession session = connection1.createXASession();
|
||||||
|
XAResource resource = session.getXAResource();
|
||||||
|
Destination dest = new ActiveMQQueue(getName());
|
||||||
|
|
||||||
|
// publish a message
|
||||||
|
Xid tid = createXid();
|
||||||
|
resource.start(tid, XAResource.TMNOFLAGS);
|
||||||
|
MessageProducer producer = session.createProducer(dest);
|
||||||
|
ActiveMQTextMessage message = new ActiveMQTextMessage();
|
||||||
|
message.setText(getName());
|
||||||
|
producer.send(message);
|
||||||
|
producer.close();
|
||||||
|
resource.end(tid, XAResource.TMSUCCESS);
|
||||||
|
resource.commit(tid, true);
|
||||||
|
session.close();
|
||||||
|
|
||||||
|
session = connection1.createXASession();
|
||||||
|
MessageConsumer consumer = session.createConsumer(dest);
|
||||||
|
tid = createXid();
|
||||||
|
resource = session.getXAResource();
|
||||||
|
resource.start(tid, XAResource.TMNOFLAGS);
|
||||||
|
TextMessage receivedMessage = (TextMessage) consumer.receive(1000);
|
||||||
|
consumer.close();
|
||||||
|
assertNotNull(receivedMessage);
|
||||||
|
assertEquals(getName(), receivedMessage.getText());
|
||||||
|
resource.end(tid, XAResource.TMSUCCESS);
|
||||||
|
resource.commit(tid, true);
|
||||||
|
|
||||||
|
session = connection1.createXASession();
|
||||||
|
consumer = session.createConsumer(dest);
|
||||||
|
tid = createXid();
|
||||||
|
resource = session.getXAResource();
|
||||||
|
resource.start(tid, XAResource.TMNOFLAGS);
|
||||||
|
assertNull(consumer.receive(1000));
|
||||||
|
resource.end(tid, XAResource.TMSUCCESS);
|
||||||
|
resource.commit(tid, true);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSessionCloseTransactionalSendReceive() throws Exception {
|
||||||
|
|
||||||
|
ActiveMQXAConnectionFactory cf1 = new ActiveMQXAConnectionFactory("vm://localhost?broker.persistent=false");
|
||||||
|
XAConnection connection1 = (XAConnection)cf1.createConnection();
|
||||||
|
connection1.start();
|
||||||
|
XASession session = connection1.createXASession();
|
||||||
|
XAResource resource = session.getXAResource();
|
||||||
|
Destination dest = new ActiveMQQueue(getName());
|
||||||
|
|
||||||
|
// publish a message
|
||||||
|
Xid tid = createXid();
|
||||||
|
resource.start(tid, XAResource.TMNOFLAGS);
|
||||||
|
MessageProducer producer = session.createProducer(dest);
|
||||||
|
ActiveMQTextMessage message = new ActiveMQTextMessage();
|
||||||
|
message.setText(getName());
|
||||||
|
producer.send(message);
|
||||||
|
session.close();
|
||||||
|
resource.end(tid, XAResource.TMSUCCESS);
|
||||||
|
resource.commit(tid, true);
|
||||||
|
|
||||||
|
|
||||||
|
session = connection1.createXASession();
|
||||||
|
MessageConsumer consumer = session.createConsumer(dest);
|
||||||
|
tid = createXid();
|
||||||
|
resource = session.getXAResource();
|
||||||
|
resource.start(tid, XAResource.TMNOFLAGS);
|
||||||
|
TextMessage receivedMessage = (TextMessage) consumer.receive(1000);
|
||||||
|
session.close();
|
||||||
|
assertNotNull(receivedMessage);
|
||||||
|
assertEquals(getName(), receivedMessage.getText());
|
||||||
|
resource.end(tid, XAResource.TMSUCCESS);
|
||||||
|
resource.commit(tid, true);
|
||||||
|
|
||||||
|
session = connection1.createXASession();
|
||||||
|
consumer = session.createConsumer(dest);
|
||||||
|
tid = createXid();
|
||||||
|
resource = session.getXAResource();
|
||||||
|
resource.start(tid, XAResource.TMNOFLAGS);
|
||||||
|
assertNull(consumer.receive(1000));
|
||||||
|
resource.end(tid, XAResource.TMSUCCESS);
|
||||||
|
resource.commit(tid, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
protected void assertCreateConnection(String uri) throws Exception {
|
protected void assertCreateConnection(String uri) throws Exception {
|
||||||
// Start up a broker with a tcp connector.
|
// Start up a broker with a tcp connector.
|
||||||
BrokerService broker = new BrokerService();
|
BrokerService broker = new BrokerService();
|
||||||
|
@ -161,5 +293,29 @@ public class ActiveMQXAConnectionFactoryTest extends CombinationTestSupport {
|
||||||
assertTrue("Should be an XATopicConnection", connection instanceof XATopicConnection);
|
assertTrue("Should be an XATopicConnection", connection instanceof XATopicConnection);
|
||||||
assertTrue("Should be an XAQueueConnection", connection instanceof XAQueueConnection);
|
assertTrue("Should be an XAQueueConnection", connection instanceof XAQueueConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Xid createXid() throws IOException {
|
||||||
|
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
DataOutputStream os = new DataOutputStream(baos);
|
||||||
|
os.writeLong(++txGenerator);
|
||||||
|
os.close();
|
||||||
|
final byte[] bs = baos.toByteArray();
|
||||||
|
|
||||||
|
return new Xid() {
|
||||||
|
public int getFormatId() {
|
||||||
|
return 86;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getGlobalTransactionId() {
|
||||||
|
return bs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getBranchQualifier() {
|
||||||
|
return bs;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -652,5 +652,35 @@ public class JMSConsumerTest extends JmsTestSupport {
|
||||||
assertNull(redispatchConsumer.receive(500));
|
assertNull(redispatchConsumer.receive(500));
|
||||||
redispatchSession.close();
|
redispatchSession.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void testRedispatchOfRolledbackTx() throws Exception {
|
||||||
|
|
||||||
|
connection.start();
|
||||||
|
Session session = connection.createSession(true, Session.SESSION_TRANSACTED);
|
||||||
|
destination = createDestination(session, ActiveMQDestination.QUEUE_TYPE);
|
||||||
|
|
||||||
|
sendMessages(connection, destination, 1);
|
||||||
|
|
||||||
|
MessageConsumer consumer = session.createConsumer(destination);
|
||||||
|
assertNotNull(consumer.receive(1000));
|
||||||
|
|
||||||
|
// install another consumer while message dispatch is unacked/uncommitted
|
||||||
|
Session redispatchSession = connection.createSession(true, Session.SESSION_TRANSACTED);
|
||||||
|
MessageConsumer redispatchConsumer = redispatchSession.createConsumer(destination);
|
||||||
|
|
||||||
|
session.rollback();
|
||||||
|
session.close();
|
||||||
|
|
||||||
|
Message msg = redispatchConsumer.receive(1000);
|
||||||
|
assertNotNull(msg);
|
||||||
|
assertTrue(msg.getJMSRedelivered());
|
||||||
|
// should have re-delivery of 2, one for re-dispatch, one for rollback which is a little too much!
|
||||||
|
assertEquals(3, msg.getLongProperty("JMSXDeliveryCount"));
|
||||||
|
redispatchSession.commit();
|
||||||
|
|
||||||
|
assertNull(redispatchConsumer.receive(500));
|
||||||
|
redispatchSession.close();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -157,7 +157,8 @@ public class JmsQueueTransactionTest extends JmsTransactionTestSupport {
|
||||||
// Get the first.
|
// Get the first.
|
||||||
assertEquals(outbound[0], consumer.receive(1000));
|
assertEquals(outbound[0], consumer.receive(1000));
|
||||||
consumer.close();
|
consumer.close();
|
||||||
|
session.commit();
|
||||||
|
|
||||||
QueueBrowser browser = session.createBrowser((Queue)destination);
|
QueueBrowser browser = session.createBrowser((Queue)destination);
|
||||||
Enumeration enumeration = browser.getEnumeration();
|
Enumeration enumeration = browser.getEnumeration();
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ public class CloseRollbackRedeliveryQueueTest extends EmbeddedBrokerTestSupport
|
||||||
protected int numberOfMessagesOnQueue = 1;
|
protected int numberOfMessagesOnQueue = 1;
|
||||||
private Connection connection;
|
private Connection connection;
|
||||||
|
|
||||||
public void testVerifyCloseRedeliveryWithFailoverTransport() throws Throwable {
|
public void testVerifySessionCloseRedeliveryWithFailoverTransport() throws Throwable {
|
||||||
Session session = connection.createSession(true, Session.SESSION_TRANSACTED);
|
Session session = connection.createSession(true, Session.SESSION_TRANSACTED);
|
||||||
MessageConsumer consumer = session.createConsumer(destination);
|
MessageConsumer consumer = session.createConsumer(destination);
|
||||||
|
|
||||||
|
@ -57,7 +57,46 @@ public class CloseRollbackRedeliveryQueueTest extends EmbeddedBrokerTestSupport
|
||||||
assertEquals("redelivered message", id, message.getJMSMessageID());
|
assertEquals("redelivered message", id, message.getJMSMessageID());
|
||||||
assertEquals(3, message.getLongProperty("JMSXDeliveryCount"));
|
assertEquals(3, message.getLongProperty("JMSXDeliveryCount"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testVerifyConsumerAndSessionCloseRedeliveryWithFailoverTransport() throws Throwable {
|
||||||
|
Session session = connection.createSession(true, Session.SESSION_TRANSACTED);
|
||||||
|
MessageConsumer consumer = session.createConsumer(destination);
|
||||||
|
|
||||||
|
Message message = consumer.receive(1000);
|
||||||
|
String id = message.getJMSMessageID();
|
||||||
|
assertNotNull(message);
|
||||||
|
LOG.info("got message " + message);
|
||||||
|
consumer.close();
|
||||||
|
session.close();
|
||||||
|
session = connection.createSession(true, Session.SESSION_TRANSACTED);
|
||||||
|
consumer = session.createConsumer(destination);
|
||||||
|
|
||||||
|
message = consumer.receive(1000);
|
||||||
|
session.commit();
|
||||||
|
assertNotNull(message);
|
||||||
|
assertEquals("redelivered message", id, message.getJMSMessageID());
|
||||||
|
assertEquals(3, message.getLongProperty("JMSXDeliveryCount"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testVerifyConsumerCloseSessionRollbackRedeliveryWithFailoverTransport() throws Throwable {
|
||||||
|
Session session = connection.createSession(true, Session.SESSION_TRANSACTED);
|
||||||
|
MessageConsumer consumer = session.createConsumer(destination);
|
||||||
|
|
||||||
|
Message message = consumer.receive(1000);
|
||||||
|
String id = message.getJMSMessageID();
|
||||||
|
assertNotNull(message);
|
||||||
|
LOG.info("got message " + message);
|
||||||
|
consumer.close();
|
||||||
|
session.rollback();
|
||||||
|
|
||||||
|
consumer = session.createConsumer(destination);
|
||||||
|
message = consumer.receive(1000);
|
||||||
|
session.commit();
|
||||||
|
assertNotNull(message);
|
||||||
|
assertEquals("redelivered message", id, message.getJMSMessageID());
|
||||||
|
assertEquals(3, message.getLongProperty("JMSXDeliveryCount"));
|
||||||
|
}
|
||||||
|
|
||||||
protected void setUp() throws Exception {
|
protected void setUp() throws Exception {
|
||||||
super.setUp();
|
super.setUp();
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,9 @@ import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import javax.jms.Connection;
|
import javax.jms.Connection;
|
||||||
|
import javax.jms.JMSException;
|
||||||
import javax.jms.Message;
|
import javax.jms.Message;
|
||||||
|
import javax.jms.MessageConsumer;
|
||||||
import javax.jms.MessageListener;
|
import javax.jms.MessageListener;
|
||||||
import javax.jms.MessageProducer;
|
import javax.jms.MessageProducer;
|
||||||
import javax.jms.Queue;
|
import javax.jms.Queue;
|
||||||
|
@ -188,6 +190,94 @@ public class MDBTest extends TestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void testMessageExceptionReDelivery() throws Exception {
|
||||||
|
|
||||||
|
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("vm://localhost?broker.persistent=false");
|
||||||
|
Connection connection = factory.createConnection();
|
||||||
|
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
|
||||||
|
|
||||||
|
ActiveMQResourceAdapter adapter = new ActiveMQResourceAdapter();
|
||||||
|
adapter.setServerUrl("vm://localhost?broker.persistent=false");
|
||||||
|
adapter.start(new StubBootstrapContext());
|
||||||
|
|
||||||
|
final CountDownLatch messageDelivered = new CountDownLatch(2);
|
||||||
|
|
||||||
|
final StubMessageEndpoint endpoint = new StubMessageEndpoint() {
|
||||||
|
public void onMessage(Message message) {
|
||||||
|
super.onMessage(message);
|
||||||
|
try {
|
||||||
|
messageDelivered.countDown();
|
||||||
|
if (!messageDelivered.await(1, TimeUnit.MILLISECONDS)) {
|
||||||
|
throw new RuntimeException(getName() + " ex on first delivery");
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
assertTrue(message.getJMSRedelivered());
|
||||||
|
} catch (JMSException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (InterruptedException ignored) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public void afterDelivery() throws ResourceException {
|
||||||
|
try {
|
||||||
|
if (!messageDelivered.await(1, TimeUnit.MILLISECONDS)) {
|
||||||
|
xaresource.end(xid, XAResource.TMFAIL);
|
||||||
|
xaresource.rollback(xid);
|
||||||
|
} else {
|
||||||
|
xaresource.end(xid, XAResource.TMSUCCESS);
|
||||||
|
xaresource.prepare(xid);
|
||||||
|
xaresource.commit(xid, false);
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
throw new ResourceException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ActiveMQActivationSpec activationSpec = new ActiveMQActivationSpec();
|
||||||
|
activationSpec.setDestinationType(Queue.class.getName());
|
||||||
|
activationSpec.setDestination("TEST");
|
||||||
|
activationSpec.setResourceAdapter(adapter);
|
||||||
|
activationSpec.validate();
|
||||||
|
|
||||||
|
MessageEndpointFactory messageEndpointFactory = new MessageEndpointFactory() {
|
||||||
|
public MessageEndpoint createEndpoint(XAResource resource) throws UnavailableException {
|
||||||
|
endpoint.xaresource = resource;
|
||||||
|
return endpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDeliveryTransacted(Method method) throws NoSuchMethodException {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Activate an Endpoint
|
||||||
|
adapter.endpointActivation(messageEndpointFactory, activationSpec);
|
||||||
|
|
||||||
|
// Give endpoint a chance to setup and register its listeners
|
||||||
|
try {
|
||||||
|
Thread.sleep(1000);
|
||||||
|
} catch (Exception e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the broker a message to that endpoint
|
||||||
|
MessageProducer producer = session.createProducer(new ActiveMQQueue("TEST"));
|
||||||
|
producer.send(session.createTextMessage("Hello!"));
|
||||||
|
connection.close();
|
||||||
|
|
||||||
|
// Wait for the message to be delivered twice.
|
||||||
|
assertTrue(messageDelivered.await(10000, TimeUnit.MILLISECONDS));
|
||||||
|
|
||||||
|
// Shut the Endpoint down.
|
||||||
|
adapter.endpointDeactivation(messageEndpointFactory, activationSpec);
|
||||||
|
adapter.stop();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public Xid createXid() throws IOException {
|
public Xid createXid() throws IOException {
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
DataOutputStream os = new DataOutputStream(baos);
|
DataOutputStream os = new DataOutputStream(baos);
|
||||||
|
|
Loading…
Reference in New Issue