/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.activemq.transport.amqp.interop;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
import org.apache.activemq.broker.jmx.QueueViewMBean;
import org.apache.activemq.transport.amqp.client.AmqpClient;
import org.apache.activemq.transport.amqp.client.AmqpClientTestSupport;
import org.apache.activemq.transport.amqp.client.AmqpConnection;
import org.apache.activemq.transport.amqp.client.AmqpMessage;
import org.apache.activemq.transport.amqp.client.AmqpReceiver;
import org.apache.activemq.transport.amqp.client.AmqpSender;
import org.apache.activemq.transport.amqp.client.AmqpSession;
import org.apache.qpid.proton.amqp.messaging.Accepted;
import org.apache.qpid.proton.amqp.messaging.Modified;
import org.apache.qpid.proton.amqp.messaging.Outcome;
import org.apache.qpid.proton.amqp.messaging.Rejected;
import org.apache.qpid.proton.amqp.messaging.Released;
import org.junit.Test;
/**
* Test various aspects of Transaction support.
*/
public class AmqpTransactionTest extends AmqpClientTestSupport {
@Test(timeout = 30000)
public void testBeginAndCommitTransaction() throws Exception {
AmqpClient client = createAmqpClient();
AmqpConnection connection = trackConnection(client.connect());
AmqpSession session = connection.createSession();
assertNotNull(session);
session.begin();
assertTrue(session.isInTransaction());
session.commit();
connection.close();
}
@Test(timeout = 30000)
public void testBeginAndRollbackTransaction() throws Exception {
AmqpClient client = createAmqpClient();
AmqpConnection connection = trackConnection(client.connect());
AmqpSession session = connection.createSession();
assertNotNull(session);
session.begin();
assertTrue(session.isInTransaction());
session.rollback();
connection.close();
}
@Test(timeout = 60000)
public void testSendMessageToQueueWithCommit() throws Exception {
AmqpClient client = createAmqpClient();
AmqpConnection connection = trackConnection(client.connect());
AmqpSession session = connection.createSession();
AmqpSender sender = session.createSender("queue://" + getTestName());
final QueueViewMBean queue = getProxyToQueue(getTestName());
session.begin();
AmqpMessage message = new AmqpMessage();
message.setText("Test-Message");
sender.send(message);
assertEquals(0, queue.getQueueSize());
session.commit();
assertEquals(1, queue.getQueueSize());
connection.close();
}
@Test(timeout = 60000)
public void testSendMessageToQueueWithRollback() throws Exception {
AmqpClient client = createAmqpClient();
AmqpConnection connection = trackConnection(client.connect());
AmqpSession session = connection.createSession();
AmqpSender sender = session.createSender("queue://" + getTestName());
final QueueViewMBean queue = getProxyToQueue(getTestName());
session.begin();
AmqpMessage message = new AmqpMessage();
message.setText("Test-Message");
sender.send(message);
assertEquals(0, queue.getQueueSize());
session.rollback();
assertEquals(0, queue.getQueueSize());
connection.close();
}
@Test(timeout = 60000)
public void testReceiveMessageWithCommit() throws Exception {
AmqpClient client = createAmqpClient();
AmqpConnection connection = trackConnection(client.connect());
AmqpSession session = connection.createSession();
AmqpSender sender = session.createSender("queue://" + getTestName());
final QueueViewMBean queue = getProxyToQueue(getTestName());
AmqpMessage message = new AmqpMessage();
message.setText("Test-Message");
sender.send(message);
assertEquals(1, queue.getQueueSize());
AmqpReceiver receiver = session.createReceiver("queue://" + getTestName());
session.begin();
receiver.flow(1);
AmqpMessage received = receiver.receive(5, TimeUnit.SECONDS);
assertNotNull(received);
received.accept();
session.commit();
assertEquals(0, queue.getQueueSize());
connection.close();
}
@Test(timeout = 60000)
public void testReceiveAfterConnectionClose() throws Exception {
AmqpClient client = createAmqpClient();
AmqpConnection connection = trackConnection(client.connect());
AmqpSession session = connection.createSession();
AmqpSender sender = session.createSender(getTestName());
final QueueViewMBean queue = getProxyToQueue(getTestName());
AmqpMessage message = new AmqpMessage();
message.setText("Test-Message");
sender.send(message);
assertEquals(1, queue.getQueueSize());
AmqpReceiver receiver = session.createReceiver(getTestName());
session.begin();
receiver.flow(1);
AmqpMessage received = receiver.receive(5, TimeUnit.SECONDS);
assertNotNull(received);
received.accept();
// this will force a rollback on the TX (It should at least)
connection.close();
connection = client.connect();
session = connection.createSession();
receiver = session.createReceiver(getTestName());
session.begin();
receiver.flow(1);
received = receiver.receive(5, TimeUnit.SECONDS);
assertNotNull(received);
received.accept();
session.commit();
assertEquals(0, queue.getQueueSize());
connection.close();
}
@Test(timeout = 60000)
public void testReceiveMessageWithRollback() throws Exception {
AmqpClient client = createAmqpClient();
AmqpConnection connection = trackConnection(client.connect());
AmqpSession session = connection.createSession();
AmqpSender sender = session.createSender("queue://" + getTestName());
final QueueViewMBean queue = getProxyToQueue(getTestName());
AmqpMessage message = new AmqpMessage();
message.setText("Test-Message");
sender.send(message);
assertEquals(1, queue.getQueueSize());
AmqpReceiver receiver = session.createReceiver("queue://" + getTestName());
session.begin();
receiver.flow(1);
AmqpMessage received = receiver.receive(5, TimeUnit.SECONDS);
assertNotNull(received);
received.accept();
session.rollback();
assertEquals(1, queue.getQueueSize());
connection.close();
}
@Test(timeout = 60000)
public void testMultipleSessionReceiversInSingleTXNWithCommit() throws Exception {
AmqpClient client = createAmqpClient();
AmqpConnection connection = trackConnection(client.connect());
// Load up the Queue with some messages
{
AmqpSession session = connection.createSession();
AmqpSender sender = session.createSender("queue://" + getTestName());
AmqpMessage message = new AmqpMessage();
message.setText("Test-Message");
sender.send(message);
sender.send(message);
sender.send(message);
sender.close();
}
// Root TXN session controls all TXN send lifetimes.
AmqpSession txnSession = connection.createSession();
// Create some sender sessions
AmqpSession session1 = connection.createSession();
AmqpSession session2 = connection.createSession();
AmqpSession session3 = connection.createSession();
// Sender linked to each session
AmqpReceiver receiver1 = session1.createReceiver("queue://" + getTestName());
AmqpReceiver receiver2 = session2.createReceiver("queue://" + getTestName());
AmqpReceiver receiver3 = session3.createReceiver("queue://" + getTestName());
final QueueViewMBean queue = getProxyToQueue(getTestName());
assertEquals(3, queue.getQueueSize());
// Begin the transaction that all senders will operate in.
txnSession.begin();
assertTrue(txnSession.isInTransaction());
receiver1.flow(1);
receiver2.flow(1);
receiver3.flow(1);
AmqpMessage message1 = receiver1.receive(5, TimeUnit.SECONDS);
AmqpMessage message2 = receiver2.receive(5, TimeUnit.SECONDS);
AmqpMessage message3 = receiver3.receive(5, TimeUnit.SECONDS);
message1.accept(txnSession);
message2.accept(txnSession);
message3.accept(txnSession);
assertEquals(3, queue.getQueueSize());
txnSession.commit();
assertEquals(0, queue.getQueueSize());
}
@Test(timeout = 60000)
public void testMultipleSessionReceiversInSingleTXNWithRollback() throws Exception {
AmqpClient client = createAmqpClient();
AmqpConnection connection = trackConnection(client.connect());
// Load up the Queue with some messages
{
AmqpSession session = connection.createSession();
AmqpSender sender = session.createSender("queue://" + getTestName());
AmqpMessage message = new AmqpMessage();
message.setText("Test-Message");
sender.send(message);
sender.send(message);
sender.send(message);
sender.close();
}
// Root TXN session controls all TXN send lifetimes.
AmqpSession txnSession = connection.createSession();
// Create some sender sessions
AmqpSession session1 = connection.createSession();
AmqpSession session2 = connection.createSession();
AmqpSession session3 = connection.createSession();
// Sender linked to each session
AmqpReceiver receiver1 = session1.createReceiver("queue://" + getTestName());
AmqpReceiver receiver2 = session2.createReceiver("queue://" + getTestName());
AmqpReceiver receiver3 = session3.createReceiver("queue://" + getTestName());
final QueueViewMBean queue = getProxyToQueue(getTestName());
assertEquals(3, queue.getQueueSize());
// Begin the transaction that all senders will operate in.
txnSession.begin();
assertTrue(txnSession.isInTransaction());
receiver1.flow(1);
receiver2.flow(1);
receiver3.flow(1);
AmqpMessage message1 = receiver1.receive(5, TimeUnit.SECONDS);
AmqpMessage message2 = receiver2.receive(5, TimeUnit.SECONDS);
AmqpMessage message3 = receiver3.receive(5, TimeUnit.SECONDS);
message1.accept(txnSession);
message2.accept(txnSession);
message3.accept(txnSession);
assertEquals(3, queue.getQueueSize());
txnSession.rollback();
assertEquals(3, queue.getQueueSize());
}
@Test(timeout = 60000)
public void testMultipleSessionSendersInSingleTXNWithCommit() throws Exception {
AmqpClient client = createAmqpClient();
AmqpConnection connection = trackConnection(client.connect());
// Root TXN session controls all TXN send lifetimes.
AmqpSession txnSession = connection.createSession();
// Create some sender sessions
AmqpSession session1 = connection.createSession();
AmqpSession session2 = connection.createSession();
AmqpSession session3 = connection.createSession();
// Sender linked to each session
AmqpSender sender1 = session1.createSender("queue://" + getTestName());
AmqpSender sender2 = session2.createSender("queue://" + getTestName());
AmqpSender sender3 = session3.createSender("queue://" + getTestName());
final QueueViewMBean queue = getProxyToQueue(getTestName());
assertEquals(0, queue.getQueueSize());
// Begin the transaction that all senders will operate in.
txnSession.begin();
AmqpMessage message = new AmqpMessage();
message.setText("Test-Message");
assertTrue(txnSession.isInTransaction());
sender1.send(message, txnSession.getTransactionId());
sender2.send(message, txnSession.getTransactionId());
sender3.send(message, txnSession.getTransactionId());
assertEquals(0, queue.getQueueSize());
txnSession.commit();
assertEquals(3, queue.getQueueSize());
}
@Test(timeout = 60000)
public void testMultipleSessionSendersInSingleTXNWithRollback() throws Exception {
AmqpClient client = createAmqpClient();
AmqpConnection connection = trackConnection(client.connect());
// Root TXN session controls all TXN send lifetimes.
AmqpSession txnSession = connection.createSession();
// Create some sender sessions
AmqpSession session1 = connection.createSession();
AmqpSession session2 = connection.createSession();
AmqpSession session3 = connection.createSession();
// Sender linked to each session
AmqpSender sender1 = session1.createSender("queue://" + getTestName());
AmqpSender sender2 = session2.createSender("queue://" + getTestName());
AmqpSender sender3 = session3.createSender("queue://" + getTestName());
final QueueViewMBean queue = getProxyToQueue(getTestName());
assertEquals(0, queue.getQueueSize());
// Begin the transaction that all senders will operate in.
txnSession.begin();
AmqpMessage message = new AmqpMessage();
message.setText("Test-Message");
assertTrue(txnSession.isInTransaction());
sender1.send(message, txnSession.getTransactionId());
sender2.send(message, txnSession.getTransactionId());
sender3.send(message, txnSession.getTransactionId());
assertEquals(0, queue.getQueueSize());
txnSession.rollback();
assertEquals(0, queue.getQueueSize());
}
@Test(timeout = 60000)
public void testAcceptedButNotSettledInTXRemainsAquiredCanBeAccepted() throws Exception {
doTestAcceptedButNotSettledInTXRemainsAquired(Accepted.getInstance());
}
@Test(timeout = 60000)
public void testAcceptedButNotSettledInTXRemainsAquiredCanBeReleased() throws Exception {
doTestAcceptedButNotSettledInTXRemainsAquired(Released.getInstance());
}
@Test(timeout = 60000)
public void testAcceptedButNotSettledInTXRemainsAquiredCanBeRejected() throws Exception {
doTestAcceptedButNotSettledInTXRemainsAquired(new Rejected());
}
@Test(timeout = 60000)
public void testAcceptedButNotSettledInTXRemainsAquiredCanBeModifiedAsFailed() throws Exception {
Modified outcome = new Modified();
outcome.setDeliveryFailed(true);
doTestAcceptedButNotSettledInTXRemainsAquired(outcome);
}
@Test(timeout = 60000)
public void testAcceptedButNotSettledInTXRemainsAquiredCanBeModifiedAsUndeliverable() throws Exception {
Modified outcome = new Modified();
outcome.setDeliveryFailed(true);
outcome.setUndeliverableHere(true);
doTestAcceptedButNotSettledInTXRemainsAquired(outcome);
}
private void doTestAcceptedButNotSettledInTXRemainsAquired(Outcome outcome) throws Exception {
AmqpClient client = createAmqpClient();
AmqpConnection connection = trackConnection(client.connect());
AmqpSession session = connection.createSession();
AmqpSender sender = session.createSender("queue://" + getTestName());
AmqpReceiver receiver = session.createReceiver("queue://" + getTestName());
final QueueViewMBean queue = getProxyToQueue(getTestName());
AmqpMessage message = new AmqpMessage();
message.setText("Test-Message");
sender.send(message);
session.begin();
receiver.flow(10);
AmqpMessage received = receiver.receive(5, TimeUnit.SECONDS);
assertNotNull(received);
received.accept(false);
session.rollback();
// Message should remain acquired an not be redelivered.
assertEquals(1, queue.getQueueSize());
assertNull(receiver.receive(2, TimeUnit.SECONDS));
if (outcome instanceof Released || outcome instanceof Rejected) {
// Receiver should be able to release the still acquired message and the
// broker should redispatch it to the client again.
received.release();
received = receiver.receive(3, TimeUnit.SECONDS);
assertNotNull(received);
received.accept();
received = receiver.receive(2, TimeUnit.SECONDS);
assertNull(received);
assertEquals(0, queue.getQueueSize());
} else if (outcome instanceof Accepted) {
// Receiver should be able to accept the still acquired message and the
// broker should then mark it as consumed.
received.accept();
received = receiver.receive(2, TimeUnit.SECONDS);
assertNull(received);
assertEquals(0, queue.getQueueSize());
} else if (outcome instanceof Modified) {
// Depending on the undeliverable here state the message will either be
// redelivered or DLQ'd
Modified modified = (Modified) outcome;
received.modified(Boolean.TRUE.equals(modified.getDeliveryFailed()), Boolean.TRUE.equals(modified.getUndeliverableHere()));
if (Boolean.TRUE.equals(modified.getUndeliverableHere())) {
received = receiver.receive(2, TimeUnit.SECONDS);
assertNull(received);
assertEquals(0, queue.getQueueSize());
} else {
received = receiver.receive(3, TimeUnit.SECONDS);
assertNotNull(received);
received.accept();
received = receiver.receive(2, TimeUnit.SECONDS);
assertNull(received);
assertEquals(0, queue.getQueueSize());
}
}
connection.close();
}
@Test(timeout = 60000)
public void testTransactionallyAcquiredMessageCanBeTransactionallyConsumed() throws Exception {
AmqpClient client = createAmqpClient();
AmqpConnection connection = trackConnection(client.connect());
AmqpSession session = connection.createSession();
AmqpSender sender = session.createSender("queue://" + getTestName());
AmqpReceiver receiver = session.createReceiver("queue://" + getTestName());
final QueueViewMBean queue = getProxyToQueue(getTestName());
AmqpMessage message = new AmqpMessage();
message.setText("Test-Message");
sender.send(message);
session.begin();
receiver.flow(10);
AmqpMessage received = receiver.receive(5, TimeUnit.SECONDS);
assertNotNull(received);
received.accept(false);
session.rollback();
// Message should remain acquired an not be redelivered.
assertEquals(1, queue.getQueueSize());
assertNull(receiver.receive(1, TimeUnit.SECONDS));
// Consume under TX but settle this time
session.begin();
received.accept(false);
session.rollback();
// Should still be acquired
assertEquals(1, queue.getQueueSize());
assertNull(receiver.receive(1, TimeUnit.SECONDS));
// Consume under TX and settle but rollback, message should be redelivered.
session.begin();
received.accept();
session.rollback();
assertEquals(1, queue.getQueueSize());
received = receiver.receive(1, TimeUnit.SECONDS);
assertNotNull(received);
// Consume under TX and commit it this time.
session.begin();
received.accept(false);
session.commit();
// Check that it is now consumed and no more message available
assertTrue(received.getWrappedDelivery().remotelySettled());
assertEquals(0, queue.getQueueSize());
assertNull(receiver.receive(1, TimeUnit.SECONDS));
connection.close();
}
//----- Tests Ported from AmqpNetLite client -----------------------------//
@Test(timeout = 60000)
public void testSendersCommitAndRollbackWithMultipleSessionsInSingleTX() throws Exception {
final int NUM_MESSAGES = 5;
AmqpClient client = createAmqpClient();
AmqpConnection connection = trackConnection(client.connect());
// Root TXN session controls all TXN send lifetimes.
AmqpSession txnSession = connection.createSession();
// Normal Session which won't create an TXN itself
AmqpSession session = connection.createSession();
AmqpSender sender = session.createSender("queue://" + getTestName());
// Commit TXN work from a sender.
txnSession.begin();
for (int i = 0; i < NUM_MESSAGES; ++i) {
AmqpMessage message = new AmqpMessage();
message.setText("Test-Message");
sender.send(message, txnSession.getTransactionId());
}
txnSession.commit();
// Rollback an additional batch of TXN work from a sender.
txnSession.begin();
for (int i = 0; i < NUM_MESSAGES; ++i) {
AmqpMessage message = new AmqpMessage();
message.setText("Test-Message");
sender.send(message, txnSession.getTransactionId());
}
txnSession.rollback();
// Commit more TXN work from a sender.
txnSession.begin();
for (int i = 0; i < NUM_MESSAGES; ++i) {
AmqpMessage message = new AmqpMessage();
message.setText("Test-Message");
sender.send(message, txnSession.getTransactionId());
}
txnSession.commit();
AmqpReceiver receiver = session.createReceiver("queue://" + getTestName());
receiver.flow(NUM_MESSAGES * 2);
for (int i = 0; i < NUM_MESSAGES * 2; ++i) {
AmqpMessage message = receiver.receive(5, TimeUnit.SECONDS);
assertNotNull(message);
message.accept(txnSession);
}
connection.close();
}
@Test(timeout = 60000)
public void testReceiversCommitAndRollbackWithMultipleSessionsInSingleTX() throws Exception {
final int NUM_MESSAGES = 10;
AmqpClient client = createAmqpClient();
AmqpConnection connection = trackConnection(client.connect());
// Root TXN session controls all TXN send lifetimes.
AmqpSession txnSession = connection.createSession();
// Normal Session which won't create an TXN itself
AmqpSession session = connection.createSession();
AmqpSender sender = session.createSender("queue://" + getTestName());
for (int i = 0; i < NUM_MESSAGES + 1; ++i) {
AmqpMessage message = new AmqpMessage();
message.setText("Test-Message");
message.setApplicationProperty("msgId", i);
sender.send(message, txnSession.getTransactionId());
}
// Read all messages from the Queue, do not accept them yet.
AmqpReceiver receiver = session.createReceiver("queue://" + getTestName());
ArrayList<AmqpMessage> messages = new ArrayList<>(NUM_MESSAGES);
receiver.flow((NUM_MESSAGES + 2) * 2);
for (int i = 0; i < NUM_MESSAGES; ++i) {
AmqpMessage message = receiver.receive(5, TimeUnit.SECONDS);
assertNotNull(message);
messages.add(message);
}
// Commit half the consumed messages
txnSession.begin();
for (int i = 0; i < NUM_MESSAGES / 2; ++i) {
messages.get(i).accept(txnSession);
}
txnSession.commit();
// Rollback the other half the consumed messages
txnSession.begin();
for (int i = NUM_MESSAGES / 2; i < NUM_MESSAGES; ++i) {
messages.get(i).accept(txnSession);
}
txnSession.rollback();
{
AmqpMessage message = receiver.receive(5, TimeUnit.SECONDS);
assertNotNull(message);
assertEquals(NUM_MESSAGES, message.getApplicationProperty("msgId"));
message.release();
}
// Commit the other half the consumed messages
// This is a variation from the .NET client tests which doesn't settle the
// messages in the TX until commit is called but on ActiveMQ they will be
// redispatched regardless and not stay in the acquired state.
txnSession.begin();
for (int i = NUM_MESSAGES / 2; i < NUM_MESSAGES; ++i) {
AmqpMessage message = receiver.receive(5, TimeUnit.SECONDS);
assertNotNull(message);
message.accept();
}
txnSession.commit();
// The final message should still be pending.
{
AmqpMessage message = receiver.receive(5, TimeUnit.SECONDS);
receiver.flow(1);
assertNotNull(message);
assertEquals(NUM_MESSAGES, message.getApplicationProperty("msgId"));
message.release();
}
connection.close();
}
@Test(timeout = 60000)
public void testCommitAndRollbackWithMultipleSessionsInSingleTX() throws Exception {
final int NUM_MESSAGES = 10;
AmqpClient client = createAmqpClient();
AmqpConnection connection = trackConnection(client.connect());
// Root TXN session controls all TXN send lifetimes.
AmqpSession txnSession = connection.createSession();
// Normal Session which won't create an TXN itself
AmqpSession session = connection.createSession();
AmqpSender sender = session.createSender("queue://" + getTestName());
for (int i = 0; i < NUM_MESSAGES; ++i) {
AmqpMessage message = new AmqpMessage();
message.setText("Test-Message");
message.setApplicationProperty("msgId", i);
sender.send(message, txnSession.getTransactionId());
}
// Read all messages from the Queue, do not accept them yet.
AmqpReceiver receiver = session.createReceiver("queue://" + getTestName());
receiver.flow(2);
AmqpMessage message1 = receiver.receive(5, TimeUnit.SECONDS);
AmqpMessage message2 = receiver.receive(5, TimeUnit.SECONDS);
// Accept the first one in a TXN and send a new message in that TXN as well
txnSession.begin();
{
message1.accept(txnSession);
AmqpMessage message = new AmqpMessage();
message.setText("Test-Message");
message.setApplicationProperty("msgId", NUM_MESSAGES);
sender.send(message, txnSession.getTransactionId());
}
txnSession.commit();
// Accept the second one in a TXN and send a new message in that TXN as well but rollback
txnSession.begin();
{
message2.accept(txnSession);
AmqpMessage message = new AmqpMessage();
message.setText("Test-Message");
message.setApplicationProperty("msgId", NUM_MESSAGES + 1);
sender.send(message, txnSession.getTransactionId());
}
txnSession.rollback();
// Variation here from .NET code, the client settles the accepted message where
// the .NET client does not and instead releases here to have it redelivered.
receiver.flow(NUM_MESSAGES);
for (int i = 1; i <= NUM_MESSAGES; ++i) {
AmqpMessage message = receiver.receive(5, TimeUnit.SECONDS);
assertNotNull(message);
assertEquals(i, message.getApplicationProperty("msgId"));
message.accept();
}
// Should be nothing left.
assertNull(receiver.receive(1, TimeUnit.SECONDS));
connection.close();
}
@Test(timeout = 60000)
public void testReceiversCommitAndRollbackWithMultipleSessionsInSingleTXNoSettlement() throws Exception {
final int NUM_MESSAGES = 10;
AmqpClient client = createAmqpClient();
AmqpConnection connection = trackConnection(client.connect());
// Root TXN session controls all TXN send lifetimes.
AmqpSession txnSession = connection.createSession();
// Normal Session which won't create an TXN itself
AmqpSession session = connection.createSession();
AmqpSender sender = session.createSender("queue://" + getTestName());
for (int i = 0; i < NUM_MESSAGES + 1; ++i) {
AmqpMessage message = new AmqpMessage();
message.setText("Test-Message");
message.setApplicationProperty("msgId", i);
sender.send(message, txnSession.getTransactionId());
}
// Read all messages from the Queue, do not accept them yet.
AmqpReceiver receiver = session.createReceiver("queue://" + getTestName());
ArrayList<AmqpMessage> messages = new ArrayList<>(NUM_MESSAGES);
receiver.flow((NUM_MESSAGES + 2) * 2);
for (int i = 0; i < NUM_MESSAGES; ++i) {
AmqpMessage message = receiver.receive(5, TimeUnit.SECONDS);
assertNotNull(message);
messages.add(message);
}
// Commit half the consumed messages
txnSession.begin();
for (int i = 0; i < NUM_MESSAGES / 2; ++i) {
messages.get(i).accept(txnSession, false);
}
txnSession.commit();
// Rollback the other half the consumed messages
txnSession.begin();
for (int i = NUM_MESSAGES / 2; i < NUM_MESSAGES; ++i) {
messages.get(i).accept(txnSession, false);
}
txnSession.rollback();
// After rollback message should still be acquired so we read last sent message.
{
AmqpMessage message = receiver.receive(5, TimeUnit.SECONDS);
assertNotNull(message);
assertEquals(NUM_MESSAGES, message.getApplicationProperty("msgId"));
message.release();
}
// Commit the other half the consumed messages
txnSession.begin();
for (int i = NUM_MESSAGES / 2; i < NUM_MESSAGES; ++i) {
messages.get(i).accept(txnSession);
}
txnSession.commit();
// The final message should still be pending.
{
AmqpMessage message = receiver.receive(5, TimeUnit.SECONDS);
receiver.flow(1);
assertNotNull(message);
assertEquals(NUM_MESSAGES, message.getApplicationProperty("msgId"));
message.accept();
}
// We should have now drained the Queue
AmqpMessage message = receiver.receive(5, TimeUnit.SECONDS);
receiver.flow(1);
assertNull(message);
connection.close();
}
@Test(timeout = 60000)
public void testCommitAndRollbackWithMultipleSessionsInSingleTXNoSettlement() throws Exception {
final int NUM_MESSAGES = 10;
AmqpClient client = createAmqpClient();
AmqpConnection connection = trackConnection(client.connect());
// Root TXN session controls all TXN send lifetimes.
AmqpSession txnSession = connection.createSession();
// Normal Session which won't create an TXN itself
AmqpSession session = connection.createSession();
AmqpSender sender = session.createSender("queue://" + getTestName());
for (int i = 0; i < NUM_MESSAGES; ++i) {
AmqpMessage message = new AmqpMessage();
message.setText("Test-Message");
message.setApplicationProperty("msgId", i);
sender.send(message, txnSession.getTransactionId());
}
// Read all messages from the Queue, do not accept them yet.
AmqpReceiver receiver = session.createReceiver("queue://" + getTestName());
receiver.flow(2);
AmqpMessage message1 = receiver.receive(5, TimeUnit.SECONDS);
AmqpMessage message2 = receiver.receive(5, TimeUnit.SECONDS);
// Accept the first one in a TXN and send a new message in that TXN as well
txnSession.begin();
{
message1.accept(txnSession, false);
AmqpMessage message = new AmqpMessage();
message.setText("Test-Message");
message.setApplicationProperty("msgId", NUM_MESSAGES);
sender.send(message, txnSession.getTransactionId());
}
txnSession.commit();
// Accept the second one in a TXN and send a new message in that TXN as well but rollback
txnSession.begin();
{
message2.accept(txnSession, false);
AmqpMessage message = new AmqpMessage();
message.setText("Test-Message");
message.setApplicationProperty("msgId", NUM_MESSAGES + 1);
sender.send(message, txnSession.getTransactionId());
}
txnSession.rollback();
message2.release();
// Should be ten message available for dispatch given that we sent and committed one, and
// releases another we had previously received.
receiver.flow(10);
for (int i = 1; i <= NUM_MESSAGES; ++i) {
AmqpMessage message = receiver.receive(5, TimeUnit.SECONDS);
assertNotNull("Expected a message for: " + i, message);
assertEquals(i, message.getApplicationProperty("msgId"));
message.accept();
}
// Should be nothing left.
receiver.flow(1);
assertNull(receiver.receive(1, TimeUnit.SECONDS));
connection.close();
}
}