summaryrefslogtreecommitdiff
path: root/qpid/java/systests/src/test/java/org/apache/qpid/test
diff options
context:
space:
mode:
Diffstat (limited to 'qpid/java/systests/src/test/java/org/apache/qpid/test')
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/client/CloseOnNoRouteForMandatoryMessageTest.java241
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/client/DupsOkTest.java167
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/client/FlowControlTest.java220
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/client/ImmediateAndMandatoryPublishingTest.java237
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/client/QueueBrowserAutoAckTest.java440
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/client/QueueBrowserClientAckTest.java34
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/client/QueueBrowserDupsOkTest.java31
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/client/QueueBrowserNoAckTest.java33
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/client/QueueBrowserPreAckTest.java32
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/client/QueueBrowserTransactedTest.java31
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/client/RollbackOrderTest.java189
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/client/UnroutableMessageTestExceptionListener.java178
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/client/destination/AddressBasedDestinationTest.java1464
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/client/failover/FailoverTest.java349
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/client/message/JMSDestinationTest.java359
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/client/message/JMSReplyToTest.java169
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/client/message/MessageToStringTest.java255
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/client/message/ObjectMessageTest.java157
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/client/message/SelectorTest.java314
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/client/queue/LVQTest.java84
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/client/queue/QueuePolicyTest.java120
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/ack/Acknowledge2ConsumersTest.java193
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/ack/AcknowledgeOnMessageTest.java226
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/ack/AcknowledgeTest.java188
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/ack/ClientAcknowledgeTest.java82
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/ack/RecoverTest.java483
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/BytesMessageTest.java324
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/FieldTableMessageTest.java165
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/InvalidDestinationTest.java171
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/LargeMessageTest.java190
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/MapMessageTest.java1269
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/MultipleConnectionTest.java223
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/ObjectMessageTest.java276
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/PropertyValueTest.java386
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/PubSubTwoConnectionTest.java75
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/SessionStartTest.java115
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/TextMessageTest.java246
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/close/CloseTest.java58
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/AMQSessionTest.java104
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/DynamicQueueExchangeCreateTest.java258
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/MaxDeliveryCountTest.java660
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/QueueSessionFactoryTest.java113
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/TopicSessionFactoryTest.java98
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/channelclose/CloseWithBlockingReceiveTest.java73
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/connection/BrokerClosesClientConnectionTest.java215
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/connection/ConnectionFactoryTest.java78
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/connection/ConnectionStartTest.java157
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/connection/ConnectionTest.java378
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/connection/ExceptionListenerTest.java244
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/message/ObjectMessageTest.java334
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/protocol/AMQProtocolSessionTest.java198
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/temporaryqueue/TemporaryQueueTest.java166
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/close/JavaServerCloseRaceConditionTest.java118
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/close/MessageConsumerCloseTest.java77
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/close/MessageRequeueTest.java371
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/close/TopicPublisherCloseTest.java69
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/ct/DurableSubscriberTest.java503
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/message/JMSPropertiesTest.java206
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/message/StreamMessageTest.java165
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/message/UTF8Test.java97
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java1063
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/topic/TemporaryTopicTest.java182
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/topic/TopicPublisherTest.java76
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/topic/TopicSessionTest.java388
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/transacted/CommitRollbackTest.java544
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/transacted/TransactedTest.java389
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/transacted/TransactionTimeoutDisabledTest.java77
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/transacted/TransactionTimeoutTest.java350
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/transacted/TransactionTimeoutTestCase.java244
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/xa/AbstractXATestCase.java138
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/xa/FaultTest.java414
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/xa/QueueTest.java669
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/unit/xa/TopicTest.java1742
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/utils/BrokerCommandHelperTest.java79
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/utils/ConversationFactory.java484
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/utils/FailoverBaseCase.java94
-rw-r--r--qpid/java/systests/src/test/java/org/apache/qpid/test/utils/QpidClientConnection.java288
77 files changed, 21677 insertions, 0 deletions
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/client/CloseOnNoRouteForMandatoryMessageTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/client/CloseOnNoRouteForMandatoryMessageTest.java
new file mode 100644
index 0000000000..deb8e4f12b
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/client/CloseOnNoRouteForMandatoryMessageTest.java
@@ -0,0 +1,241 @@
+/*
+ * 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.qpid.test.client;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.jms.Connection;
+import javax.jms.Destination;
+import javax.jms.IllegalStateException;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+import javax.naming.NamingException;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.jms.ConnectionURL;
+import org.apache.qpid.protocol.AMQConstant;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+import org.apache.qpid.url.URLSyntaxException;
+
+/**
+ * Tests the broker's connection-closing behaviour when it receives an unroutable message
+ * on a transactional session.
+ *
+ * @see ImmediateAndMandatoryPublishingTest for more general tests of mandatory and immediate publishing
+ */
+public class CloseOnNoRouteForMandatoryMessageTest extends QpidBrokerTestCase
+{
+ private static final Logger _logger = Logger.getLogger(CloseOnNoRouteForMandatoryMessageTest.class);
+
+ private Connection _connection;
+ private UnroutableMessageTestExceptionListener _testExceptionListener = new UnroutableMessageTestExceptionListener();
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+ }
+
+ public void testNoRoute_brokerClosesConnection() throws Exception
+ {
+ createConnectionWithCloseWhenNoRoute(true);
+
+ Session transactedSession = _connection.createSession(true, Session.SESSION_TRANSACTED);
+ String testQueueName = getTestQueueName();
+ MessageProducer mandatoryProducer = ((AMQSession<?, ?>) transactedSession).createProducer(
+ transactedSession.createQueue(testQueueName),
+ true, // mandatory
+ false); // immediate
+
+ Message message = transactedSession.createMessage();
+ mandatoryProducer.send(message);
+ try
+ {
+ transactedSession.commit();
+ fail("Expected exception not thrown");
+ }
+ catch (IllegalStateException ise)
+ {
+ _logger.debug("Caught exception", ise);
+ //The session was marked closed even before we had a chance to call commit on it
+ assertTrue("ISE did not indicate closure", ise.getMessage().contains("closed"));
+ }
+ catch(JMSException e)
+ {
+ _logger.debug("Caught exception", e);
+ _testExceptionListener.assertNoRoute(e, testQueueName);
+ }
+ _testExceptionListener.assertReceivedNoRoute(testQueueName);
+
+ forgetConnection(_connection);
+ }
+
+ public void testCloseOnNoRouteWhenExceptionMessageLengthIsGreater255() throws Exception
+ {
+ createConnectionWithCloseWhenNoRoute(true);
+
+ AMQSession<?, ?> transactedSession = (AMQSession<?, ?>) _connection.createSession(true, Session.SESSION_TRANSACTED);
+
+ StringBuilder longExchangeName = getLongExchangeName();
+
+ AMQShortString exchangeName = new AMQShortString(longExchangeName.toString());
+ transactedSession.declareExchange(exchangeName, new AMQShortString("direct"), false);
+
+ Destination testQueue = new AMQQueue(exchangeName, getTestQueueName());
+ MessageProducer mandatoryProducer = transactedSession.createProducer(
+ testQueue,
+ true, // mandatory
+ false); // immediate
+
+ Message message = transactedSession.createMessage();
+ mandatoryProducer.send(message);
+ try
+ {
+ transactedSession.commit();
+ fail("Expected exception not thrown");
+ }
+ catch (IllegalStateException ise)
+ {
+ _logger.debug("Caught exception", ise);
+ //The session was marked closed even before we had a chance to call commit on it
+ assertTrue("ISE did not indicate closure", ise.getMessage().contains("closed"));
+ }
+ catch (JMSException e)
+ {
+ _logger.debug("Caught exception", e);
+ AMQException noRouteException = (AMQException) e.getLinkedException();
+ assertNotNull("AMQException should be linked to JMSException", noRouteException);
+
+ assertEquals(AMQConstant.NO_ROUTE, noRouteException.getErrorCode());
+ String expectedMessage = "Error: No route for message [Exchange: " + longExchangeName.substring(0, 220) + "...";
+ assertEquals("Unexpected exception message: " + noRouteException.getMessage(), expectedMessage,
+ noRouteException.getMessage());
+ }
+ finally
+ {
+ forgetConnection(_connection);
+ }
+ }
+
+ public void testNoRouteMessageReurnedWhenExceptionMessageLengthIsGreater255() throws Exception
+ {
+ createConnectionWithCloseWhenNoRoute(false);
+
+ AMQSession<?, ?> transactedSession = (AMQSession<?, ?>) _connection.createSession(true, Session.SESSION_TRANSACTED);
+
+ StringBuilder longExchangeName = getLongExchangeName();
+
+ AMQShortString exchangeName = new AMQShortString(longExchangeName.toString());
+ transactedSession.declareExchange(exchangeName, new AMQShortString("direct"), false);
+
+ AMQQueue testQueue = new AMQQueue(exchangeName, getTestQueueName());
+ MessageProducer mandatoryProducer = transactedSession.createProducer(
+ testQueue,
+ true, // mandatory
+ false); // immediate
+
+ Message message = transactedSession.createMessage();
+ mandatoryProducer.send(message);
+ transactedSession.commit();
+ _testExceptionListener.assertReceivedReturnedMessageWithLongExceptionMessage(message, testQueue);
+ }
+
+ private StringBuilder getLongExchangeName()
+ {
+ StringBuilder longExchangeName = new StringBuilder();
+ for (int i = 0; i < 50; i++)
+ {
+ longExchangeName.append("abcde");
+ }
+ return longExchangeName;
+ }
+
+ public void testNoRouteForNonMandatoryMessage_brokerKeepsConnectionOpenAndCallsExceptionListener() throws Exception
+ {
+ createConnectionWithCloseWhenNoRoute(true);
+
+ Session transactedSession = _connection.createSession(true, Session.SESSION_TRANSACTED);
+ String testQueueName = getTestQueueName();
+ MessageProducer nonMandatoryProducer = ((AMQSession<?, ?>) transactedSession).createProducer(
+ transactedSession.createQueue(testQueueName),
+ false, // mandatory
+ false); // immediate
+
+ Message message = transactedSession.createMessage();
+ nonMandatoryProducer.send(message);
+
+ // should succeed - the message is simply discarded
+ transactedSession.commit();
+
+ _testExceptionListener.assertNoException();
+ }
+
+
+ public void testNoRouteOnNonTransactionalSession_brokerKeepsConnectionOpenAndCallsExceptionListener() throws Exception
+ {
+ createConnectionWithCloseWhenNoRoute(true);
+
+ Session nonTransactedSession = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ String testQueueName = getTestQueueName();
+ MessageProducer mandatoryProducer = ((AMQSession<?, ?>) nonTransactedSession).createProducer(
+ nonTransactedSession.createQueue(testQueueName),
+ true, // mandatory
+ false); // immediate
+
+ Message message = nonTransactedSession.createMessage();
+ mandatoryProducer.send(message);
+
+ // should succeed - the message is asynchronously bounced back to the exception listener
+ message.acknowledge();
+
+ _testExceptionListener.assertReceivedNoRouteWithReturnedMessage(message, getTestQueueName());
+ }
+
+ public void testClientDisablesCloseOnNoRoute_brokerKeepsConnectionOpenAndCallsExceptionListener() throws Exception
+ {
+ createConnectionWithCloseWhenNoRoute(false);
+
+ Session transactedSession = _connection.createSession(true, Session.SESSION_TRANSACTED);
+ String testQueueName = getTestQueueName();
+ MessageProducer mandatoryProducer = ((AMQSession<?, ?>) transactedSession).createProducer(
+ transactedSession.createQueue(testQueueName),
+ true, // mandatory
+ false); // immediate
+
+ Message message = transactedSession.createMessage();
+ mandatoryProducer.send(message);
+ transactedSession.commit();
+ _testExceptionListener.assertReceivedNoRouteWithReturnedMessage(message, getTestQueueName());
+ }
+
+ private void createConnectionWithCloseWhenNoRoute(boolean closeWhenNoRoute) throws URLSyntaxException, NamingException, JMSException
+ {
+ Map<String, String> options = new HashMap<String, String>();
+ options.put(ConnectionURL.OPTIONS_CLOSE_WHEN_NO_ROUTE, Boolean.toString(closeWhenNoRoute));
+ _connection = getConnectionWithOptions(options);
+ _connection.setExceptionListener(_testExceptionListener);
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/client/DupsOkTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/client/DupsOkTest.java
new file mode 100644
index 0000000000..fa36d73283
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/client/DupsOkTest.java
@@ -0,0 +1,167 @@
+/*
+ *
+ * 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.qpid.test.client;
+
+import org.apache.qpid.client.AMQDestination;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+import javax.jms.Connection;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageListener;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+
+public class DupsOkTest extends QpidBrokerTestCase
+{
+
+ private Queue _queue;
+ private static final int MSG_COUNT = 100;
+ private CountDownLatch _awaitCompletion = new CountDownLatch(1);
+
+ public void setUp() throws Exception
+ {
+ super.setUp();
+
+ _queue = (Queue) getInitialContext().lookup("queue");
+
+
+ //Declare the queue
+ Connection consumerConnection = getConnection();
+ consumerConnection.createSession(false,Session.AUTO_ACKNOWLEDGE).createConsumer(_queue).close();
+
+ //Create Producer put some messages on the queue
+ Connection producerConnection = getConnection();
+
+ producerConnection.start();
+
+ Session producerSession = producerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ MessageProducer producer = producerSession.createProducer(_queue);
+
+ for (int count = 1; count <= MSG_COUNT; count++)
+ {
+ Message msg = producerSession.createTextMessage("Message " + count);
+ msg.setIntProperty("count", count);
+ producer.send(msg);
+ }
+
+ producerConnection.close();
+ }
+
+ /**
+ * This test sends x messages and receives them with an async consumer.
+ * Waits for all messages to be received or for 60 s
+ * and checks whether the queue is empty.
+ *
+ * @throws Exception
+ */
+ public void testDupsOK() throws Exception
+ {
+ //Create Client
+ Connection clientConnection = getConnection();
+
+ final Session clientSession = clientConnection.createSession(false, Session.DUPS_OK_ACKNOWLEDGE);
+
+ MessageConsumer consumer = clientSession.createConsumer(_queue);
+
+ assertEquals("The queue should have msgs at start", MSG_COUNT, ((AMQSession) clientSession).getQueueDepth((AMQDestination) _queue));
+
+ clientConnection.start();
+
+ consumer.setMessageListener(new MessageListener()
+ {
+ private int _msgCount = 0;
+
+ public void onMessage(Message message)
+ {
+ _msgCount++;
+ if (message == null)
+ {
+ fail("Should not get null messages");
+ }
+
+ if (message instanceof TextMessage)
+ {
+ try
+ {
+ if (message.getIntProperty("count") == MSG_COUNT)
+ {
+ try
+ {
+ if(_msgCount != MSG_COUNT)
+ {
+ assertEquals("Wrong number of messages seen.", MSG_COUNT, _msgCount);
+ }
+ }
+ finally
+ {
+ //This is the last message so release test.
+ _awaitCompletion.countDown();
+ }
+ }
+ }
+ catch (JMSException e)
+ {
+ fail("Unable to get int property 'count'");
+ }
+ }
+ else
+ {
+ fail("Got wrong message type");
+ }
+ }
+ });
+
+ try
+ {
+ if (!_awaitCompletion.await(120, TimeUnit.SECONDS))
+ {
+ fail("Test did not complete in 120 seconds");
+ }
+ }
+ catch (InterruptedException e)
+ {
+ fail("Unable to wait for test completion");
+ throw e;
+ }
+
+ //Close consumer to give broker time to process in bound Acks. As The main thread will be released while
+ // before the dispatcher has sent the ack back to the broker.
+ consumer.close();
+
+ clientSession.close();
+
+ final Session clientSession2 = clientConnection.createSession(false, Session.DUPS_OK_ACKNOWLEDGE);
+
+ assertEquals("The queue should have 0 msgs left", 0, ((AMQSession) clientSession2).getQueueDepth((AMQDestination) _queue));
+
+ clientConnection.close();
+ }
+
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/client/FlowControlTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/client/FlowControlTest.java
new file mode 100644
index 0000000000..f8bc051be7
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/client/FlowControlTest.java
@@ -0,0 +1,220 @@
+/*
+*
+* 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.qpid.test.client;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.client.AMQSession_0_8;
+import org.apache.qpid.client.message.AbstractJMSMessage;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+import javax.jms.BytesMessage;
+import javax.jms.Connection;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.Session;
+
+public class FlowControlTest extends QpidBrokerTestCase
+{
+ private static final Logger _logger = Logger.getLogger(FlowControlTest.class);
+
+ private Connection _clientConnection;
+ private Session _clientSession;
+ private Queue _queue;
+
+ /**
+ * Simply
+ *
+ * @throws Exception
+ */
+ public void testBasicBytesFlowControl() throws Exception
+ {
+ _queue = (Queue) getInitialContext().lookup("queue");
+
+ //Create Client
+ _clientConnection = getConnection();
+
+ _clientConnection.start();
+
+ _clientSession = _clientConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ //Ensure _queue is created
+ _clientSession.createConsumer(_queue).close();
+
+ Connection producerConnection = getConnection();
+
+ producerConnection.start();
+
+ Session producerSession = producerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ MessageProducer producer = producerSession.createProducer(_queue);
+
+ BytesMessage m1 = producerSession.createBytesMessage();
+ m1.writeBytes(new byte[128]);
+ m1.setIntProperty("msg", 1);
+ producer.send(m1);
+ BytesMessage m2 = producerSession.createBytesMessage();
+ m2.writeBytes(new byte[128]);
+ m2.setIntProperty("msg", 2);
+ producer.send(m2);
+ BytesMessage m3 = producerSession.createBytesMessage();
+ m3.writeBytes(new byte[256]);
+ m3.setIntProperty("msg", 3);
+ producer.send(m3);
+
+ producer.close();
+ producerSession.close();
+ producerConnection.close();
+
+ Connection consumerConnection = getConnection();
+ Session consumerSession = consumerConnection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ ((AMQSession_0_8) consumerSession).setPrefetchLimits(0, 256);
+ MessageConsumer recv = consumerSession.createConsumer(_queue);
+ consumerConnection.start();
+
+ Message r1 = recv.receive(RECEIVE_TIMEOUT);
+ assertNotNull("First message not received", r1);
+ assertEquals("Messages in wrong order", 1, r1.getIntProperty("msg"));
+
+ Message r2 = recv.receive(RECEIVE_TIMEOUT);
+ assertNotNull("Second message not received", r2);
+ assertEquals("Messages in wrong order", 2, r2.getIntProperty("msg"));
+
+ Message r3 = recv.receive(RECEIVE_TIMEOUT);
+ assertNull("Third message incorrectly delivered", r3);
+
+ ((AbstractJMSMessage)r1).acknowledgeThis();
+
+ r3 = recv.receive(RECEIVE_TIMEOUT);
+ assertNull("Third message incorrectly delivered", r3);
+
+ ((AbstractJMSMessage)r2).acknowledgeThis();
+
+ r3 = recv.receive(RECEIVE_TIMEOUT);
+ assertNotNull("Third message not received", r3);
+ assertEquals("Messages in wrong order", 3, r3.getIntProperty("msg"));
+
+ ((AbstractJMSMessage)r3).acknowledgeThis();
+ consumerConnection.close();
+ }
+
+ public void testTwoConsumersBytesFlowControl() throws Exception
+ {
+ _queue = (Queue) getInitialContext().lookup("queue");
+
+ //Create Client
+ _clientConnection = getConnection();
+
+ _clientConnection.start();
+
+ _clientSession = _clientConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ //Ensure _queue is created
+ _clientSession.createConsumer(_queue).close();
+
+ Connection producerConnection = getConnection();
+
+ producerConnection.start();
+
+ Session producerSession = producerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ MessageProducer producer = producerSession.createProducer(_queue);
+
+ BytesMessage m1 = producerSession.createBytesMessage();
+ m1.writeBytes(new byte[128]);
+ m1.setIntProperty("msg", 1);
+ producer.send(m1);
+ BytesMessage m2 = producerSession.createBytesMessage();
+ m2.writeBytes(new byte[256]);
+ m2.setIntProperty("msg", 2);
+ producer.send(m2);
+ BytesMessage m3 = producerSession.createBytesMessage();
+ m3.writeBytes(new byte[128]);
+ m3.setIntProperty("msg", 3);
+ producer.send(m3);
+
+ producer.close();
+ producerSession.close();
+ producerConnection.close();
+
+ Connection consumerConnection = getConnection();
+ Session consumerSession1 = consumerConnection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ ((AMQSession_0_8) consumerSession1).setPrefetchLimits(0, 256);
+ MessageConsumer recv1 = consumerSession1.createConsumer(_queue);
+
+ consumerConnection.start();
+
+ Message r1 = recv1.receive(RECEIVE_TIMEOUT);
+ assertNotNull("First message not received", r1);
+ assertEquals("Messages in wrong order", 1, r1.getIntProperty("msg"));
+
+ Message r2 = recv1.receive(RECEIVE_TIMEOUT);
+ assertNull("Second message incorrectly delivered", r2);
+
+ Session consumerSession2 = consumerConnection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ ((AMQSession_0_8) consumerSession2).setPrefetchLimits(0, 256);
+ MessageConsumer recv2 = consumerSession2.createConsumer(_queue);
+
+ r2 = recv2.receive(RECEIVE_TIMEOUT);
+ assertNotNull("Second message not received", r2);
+ assertEquals("Messages in wrong order", 2, r2.getIntProperty("msg"));
+
+ Message r3 = recv2.receive(RECEIVE_TIMEOUT);
+ assertNull("Third message incorrectly delivered", r3);
+
+ r3 = recv1.receive(RECEIVE_TIMEOUT);
+ assertNotNull("Third message not received", r3);
+ assertEquals("Messages in wrong order", 3, r3.getIntProperty("msg"));
+
+ r2.acknowledge();
+ r3.acknowledge();
+ recv1.close();
+ recv2.close();
+ consumerSession1.close();
+ consumerSession2.close();
+ consumerConnection.close();
+
+ }
+
+ public static void main(String args[]) throws Throwable
+ {
+ FlowControlTest test = new FlowControlTest();
+
+ int run = 0;
+ while (true)
+ {
+ System.err.println("Test Run:" + ++run);
+ Thread.sleep(1000);
+ try
+ {
+ test.startBroker();
+ test.testBasicBytesFlowControl();
+
+ Thread.sleep(1000);
+ }
+ finally
+ {
+ test.stopBroker();
+ }
+ }
+ }
+}
+
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/client/ImmediateAndMandatoryPublishingTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/client/ImmediateAndMandatoryPublishingTest.java
new file mode 100644
index 0000000000..d012b9abbb
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/client/ImmediateAndMandatoryPublishingTest.java
@@ -0,0 +1,237 @@
+/*
+ * 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.qpid.test.client;
+
+import javax.jms.Connection;
+import javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+import javax.jms.Topic;
+
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.server.model.Broker;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+/**
+ * @see CloseOnNoRouteForMandatoryMessageTest for related tests
+ */
+public class ImmediateAndMandatoryPublishingTest extends QpidBrokerTestCase
+{
+ private Connection _connection;
+ private UnroutableMessageTestExceptionListener _testExceptionListener = new UnroutableMessageTestExceptionListener();
+
+ @Override
+ public void setUp() throws Exception
+ {
+ getBrokerConfiguration().setBrokerAttribute(Broker.CONNECTION_CLOSE_WHEN_NO_ROUTE, false);
+ super.setUp();
+ _connection = getConnection();
+ _connection.setExceptionListener(_testExceptionListener);
+ }
+
+ public void testPublishP2PWithNoConsumerAndImmediateOnAndAutoAck() throws Exception
+ {
+ publishIntoExistingDestinationWithNoConsumerAndImmediateOn(Session.AUTO_ACKNOWLEDGE, false);
+ }
+
+ public void testPublishP2PWithNoConsumerAndImmediateOnAndTx() throws Exception
+ {
+ publishIntoExistingDestinationWithNoConsumerAndImmediateOn(Session.SESSION_TRANSACTED, false);
+ }
+
+ public void testPublishPubSubWithDisconnectedDurableSubscriberAndImmediateOnAndAutoAck() throws Exception
+ {
+ publishIntoExistingDestinationWithNoConsumerAndImmediateOn(Session.AUTO_ACKNOWLEDGE, true);
+ }
+
+ public void testPublishPubSubWithDisconnectedDurableSubscriberAndImmediateOnAndTx() throws Exception
+ {
+ publishIntoExistingDestinationWithNoConsumerAndImmediateOn(Session.SESSION_TRANSACTED, true);
+ }
+
+ public void testPublishP2PIntoNonExistingDesitinationWithMandatoryOnAutoAck() throws Exception
+ {
+ publishWithMandatoryOnImmediateOff(Session.AUTO_ACKNOWLEDGE, false);
+ }
+
+ public void testPublishP2PIntoNonExistingDesitinationWithMandatoryOnAndTx() throws Exception
+ {
+ publishWithMandatoryOnImmediateOff(Session.SESSION_TRANSACTED, false);
+ }
+
+ public void testPubSubMandatoryAutoAck() throws Exception
+ {
+ publishWithMandatoryOnImmediateOff(Session.AUTO_ACKNOWLEDGE, true);
+ }
+
+ public void testPubSubMandatoryTx() throws Exception
+ {
+ publishWithMandatoryOnImmediateOff(Session.SESSION_TRANSACTED, true);
+ }
+
+ public void testP2PNoMandatoryAutoAck() throws Exception
+ {
+ publishWithMandatoryOffImmediateOff(Session.AUTO_ACKNOWLEDGE, false);
+ }
+
+ public void testP2PNoMandatoryTx() throws Exception
+ {
+ publishWithMandatoryOffImmediateOff(Session.SESSION_TRANSACTED, false);
+ }
+
+ public void testPubSubWithImmediateOnAndAutoAck() throws Exception
+ {
+ consumerCreateAndClose(true, false);
+
+ Message message = produceMessage(Session.AUTO_ACKNOWLEDGE, true, false, true);
+ _testExceptionListener.assertReceivedNoRouteWithReturnedMessage(message, getTestQueueName());
+ }
+
+ private void publishIntoExistingDestinationWithNoConsumerAndImmediateOn(int acknowledgeMode, boolean pubSub)
+ throws JMSException, InterruptedException
+ {
+ consumerCreateAndClose(pubSub, true);
+
+ Message message = produceMessage(acknowledgeMode, pubSub, false, true);
+
+ _testExceptionListener.assertReceivedNoConsumersWithReturnedMessage(message);
+ }
+
+ private void publishWithMandatoryOnImmediateOff(int acknowledgeMode, boolean pubSub) throws JMSException,
+ InterruptedException
+ {
+ Message message = produceMessage(acknowledgeMode, pubSub, true, false);
+ _testExceptionListener.assertReceivedNoRouteWithReturnedMessage(message, getTestQueueName());
+ }
+
+ private void publishWithMandatoryOffImmediateOff(int acknowledgeMode, boolean pubSub) throws JMSException,
+ InterruptedException
+ {
+ produceMessage(acknowledgeMode, pubSub, false, false);
+
+ _testExceptionListener.assertNoException();
+ }
+
+ private void consumerCreateAndClose(boolean pubSub, boolean durable) throws JMSException
+ {
+ Session session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ Destination destination = null;
+ MessageConsumer consumer = null;
+ if (pubSub)
+ {
+ destination = session.createTopic(getTestQueueName());
+ if (durable)
+ {
+ consumer = session.createDurableSubscriber((Topic) destination, getTestName());
+ }
+ else
+ {
+ consumer = session.createConsumer(destination);
+ }
+ }
+ else
+ {
+ destination = session.createQueue(getTestQueueName());
+ consumer = session.createConsumer(destination);
+ }
+ consumer.close();
+ }
+
+ private Message produceMessage(int acknowledgeMode, boolean pubSub, boolean mandatory, boolean immediate)
+ throws JMSException
+ {
+ Session session = _connection.createSession(acknowledgeMode == Session.SESSION_TRANSACTED, acknowledgeMode);
+ Destination destination = null;
+ if (pubSub)
+ {
+ destination = session.createTopic(getTestQueueName());
+ }
+ else
+ {
+ destination = session.createQueue(getTestQueueName());
+ }
+
+ MessageProducer producer = ((AMQSession<?, ?>) session).createProducer(destination, mandatory, immediate);
+ Message message = session.createMessage();
+ producer.send(message);
+ if (session.getTransacted())
+ {
+ session.commit();
+ }
+ return message;
+ }
+
+ public void testMandatoryAndImmediateDefaults() throws JMSException, InterruptedException
+ {
+ Session session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ // publish to non-existent queue - should get mandatory failure
+ MessageProducer producer = session.createProducer(session.createQueue(getTestQueueName()));
+ Message message = session.createMessage();
+ producer.send(message);
+
+ _testExceptionListener.assertReceivedNoRouteWithReturnedMessage(message, getTestQueueName());
+
+ producer = session.createProducer(null);
+ message = session.createMessage();
+ producer.send(session.createQueue(getTestQueueName()), message);
+
+ _testExceptionListener.assertReceivedNoRouteWithReturnedMessage(message, getTestQueueName());
+
+ // publish to non-existent topic - should get no failure
+ producer = session.createProducer(session.createTopic(getTestQueueName()));
+ message = session.createMessage();
+ producer.send(message);
+
+ _testExceptionListener.assertNoException();
+
+ producer = session.createProducer(null);
+ message = session.createMessage();
+ producer.send(session.createTopic(getTestQueueName()), message);
+
+ _testExceptionListener.assertNoException();
+
+ session.close();
+ }
+
+ public void testMandatoryAndImmediateSystemProperties() throws JMSException, InterruptedException
+ {
+ setTestClientSystemProperty("qpid.default_mandatory","true");
+ Session session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ // publish to non-existent topic - should get mandatory failure
+
+ MessageProducer producer = session.createProducer(session.createTopic(getTestQueueName()));
+ Message message = session.createMessage();
+ producer.send(message);
+
+ _testExceptionListener.assertReceivedNoRouteWithReturnedMessage(message, getTestQueueName());
+
+ // now set topic specific system property to false - should no longer get mandatory failure on new producer
+ setTestClientSystemProperty("qpid.default_mandatory_topic","false");
+ producer = session.createProducer(null);
+ message = session.createMessage();
+ producer.send(session.createTopic(getTestQueueName()), message);
+
+ _testExceptionListener.assertNoException();
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/client/QueueBrowserAutoAckTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/client/QueueBrowserAutoAckTest.java
new file mode 100644
index 0000000000..6b6b4a7b3c
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/client/QueueBrowserAutoAckTest.java
@@ -0,0 +1,440 @@
+/*
+ * 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.qpid.test.client;
+
+import java.util.Enumeration;
+import javax.jms.Connection;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.QueueBrowser;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import javax.naming.NamingException;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.client.AMQDestination;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+public class QueueBrowserAutoAckTest extends QpidBrokerTestCase
+{
+ protected Connection _clientConnection;
+ protected Session _clientSession;
+ protected Queue _queue;
+ protected static final String MESSAGE_ID_PROPERTY = "MessageIDProperty";
+
+ public void setUp() throws Exception
+ {
+ super.setUp();
+
+ //Create Client
+ _clientConnection = getConnection();
+ _clientConnection.start();
+
+ setupSession();
+
+ _queue = _clientSession.createQueue(getTestQueueName());
+ _clientSession.createConsumer(_queue).close();
+
+ //Ensure there are no messages on the queue to start with.
+ checkQueueDepth(0);
+ }
+
+ protected void setupSession() throws Exception
+ {
+ _clientSession = _clientConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ }
+
+ public void tearDown() throws Exception
+ {
+ if (_clientConnection != null)
+ {
+ _clientConnection.close();
+ }
+
+ super.tearDown();
+ }
+
+ protected void sendMessages(int num) throws JMSException
+ {
+ Connection producerConnection = null;
+ try
+ {
+ producerConnection = getConnection();
+ }
+ catch (Exception e)
+ {
+ fail("Unable to lookup connection in JNDI.");
+ }
+
+ sendMessages(producerConnection, num);
+ }
+
+ protected void sendMessages(Connection producerConnection, int messageSendCount) throws JMSException
+ {
+ producerConnection.start();
+
+ Session producerSession = producerConnection.createSession(true, Session.AUTO_ACKNOWLEDGE);
+
+ //Ensure _queue is created
+ producerSession.createConsumer(_queue).close();
+
+ MessageProducer producer = producerSession.createProducer(_queue);
+
+ for (int messsageID = 0; messsageID < messageSendCount; messsageID++)
+ {
+ TextMessage textMsg = producerSession.createTextMessage("Message " + messsageID);
+ textMsg.setIntProperty(MESSAGE_ID_PROPERTY, messsageID);
+ producer.send(textMsg);
+ }
+ producerSession.commit();
+
+ producerConnection.close();
+ }
+
+ /**
+ * Using the Protocol getQueueDepth method ensure that the correct number of messages are on the queue.
+ *
+ * Also uses a QueueBrowser as a second method of validating the message count on the queue.
+ *
+ * @param expectedDepth The expected Queue depth
+ * @throws JMSException on error
+ */
+ protected void checkQueueDepth(int expectedDepth) throws JMSException
+ {
+
+ // create QueueBrowser
+ _logger.info("Creating Queue Browser");
+
+ QueueBrowser queueBrowser = _clientSession.createBrowser(_queue);
+
+ // check for messages
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Checking for " + expectedDepth + " messages with QueueBrowser");
+ }
+
+ //Check what the session believes the queue count to be.
+ long queueDepth = 0;
+
+ try
+ {
+ queueDepth = ((AMQSession) _clientSession).getQueueDepth((AMQDestination) _queue);
+ }
+ catch (AMQException e)
+ {
+ }
+
+ assertEquals("Session reports Queue expectedDepth not as expected", expectedDepth, queueDepth);
+
+
+
+ // Browse the queue to get a second opinion
+ int msgCount = 0;
+ Enumeration msgs = queueBrowser.getEnumeration();
+
+ while (msgs.hasMoreElements())
+ {
+ msgs.nextElement();
+ msgCount++;
+ }
+
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Found " + msgCount + " messages total in browser");
+ }
+
+ // check to see if all messages found
+ assertEquals("Browser did not find all messages", expectedDepth, msgCount);
+
+ //Close browser
+ queueBrowser.close();
+ }
+
+ protected void closeBrowserBeforeAfterGetNext(int messageCount) throws JMSException
+ {
+ QueueBrowser queueBrowser = _clientSession.createBrowser(_queue);
+
+ Enumeration msgs = queueBrowser.getEnumeration();
+
+ int msgCount = 0;
+
+ while (msgs.hasMoreElements() && msgCount < messageCount)
+ {
+ msgs.nextElement();
+ msgCount++;
+ }
+
+ try
+ {
+ queueBrowser.close();
+ }
+ catch (JMSException e)
+ {
+ fail("Close should happen without error:" + e.getMessage());
+ }
+ }
+
+ /**
+ * This method checks that multiple calls to getEnumeration() on a queueBrowser provide the same behaviour.
+ *
+ * @param sentMessages The number of messages sent
+ * @param browserEnumerationCount The number of times to call getEnumeration()
+ * @throws JMSException
+ */
+ protected void checkMultipleGetEnum(int sentMessages, int browserEnumerationCount) throws JMSException
+ {
+ QueueBrowser queueBrowser = _clientSession.createBrowser(_queue);
+
+ for (int count = 0; count < browserEnumerationCount; count++)
+ {
+ _logger.info("Checking getEnumeration:" + count);
+ Enumeration msgs = queueBrowser.getEnumeration();
+
+ int msgCount = 0;
+
+ while (msgs.hasMoreElements())
+ {
+ msgs.nextElement();
+ msgCount++;
+ }
+
+ // Verify that the browser can see all the messages sent.
+ assertEquals(sentMessages, msgCount);
+ }
+
+ try
+ {
+ queueBrowser.close();
+ }
+ catch (JMSException e)
+ {
+ fail("Close should happen without error:" + e.getMessage());
+ }
+ }
+
+ protected void checkOverlappingMultipleGetEnum(int expectedMessages, int browserEnumerationCount) throws JMSException
+ {
+ checkOverlappingMultipleGetEnum(expectedMessages, browserEnumerationCount, null);
+ }
+
+ protected void checkOverlappingMultipleGetEnum(int expectedMessages, int browserEnumerationCount, String selector) throws JMSException
+ {
+ QueueBrowser queueBrowser = selector == null ?
+ _clientSession.createBrowser(_queue) : _clientSession.createBrowser(_queue, selector);
+
+ Enumeration[] msgs = new Enumeration[browserEnumerationCount];
+ int[] msgCount = new int[browserEnumerationCount];
+
+ //create Enums
+ for (int count = 0; count < browserEnumerationCount; count++)
+ {
+ msgs[count] = queueBrowser.getEnumeration();
+ }
+
+ //interleave reads
+ for (int cnt = 0; cnt < expectedMessages; cnt++)
+ {
+ for (int count = 0; count < browserEnumerationCount; count++)
+ {
+ if (msgs[count].hasMoreElements())
+ {
+ msgs[count].nextElement();
+ msgCount[count]++;
+ }
+ }
+ }
+
+ //validate all browsers get right message count.
+ for (int count = 0; count < browserEnumerationCount; count++)
+ {
+ assertEquals(msgCount[count], expectedMessages);
+ }
+
+ try
+ {
+ queueBrowser.close();
+ }
+ catch (JMSException e)
+ {
+ fail("Close should happen without error:" + e.getMessage());
+ }
+ }
+
+ protected void validate(int messages) throws JMSException
+ {
+ //Create a new connection to validate message content
+ Connection connection = null;
+
+ try
+ {
+ connection = getConnection();
+ }
+ catch (Exception e)
+ {
+ fail("Unable to make validation connection");
+ }
+
+ Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ connection.start();
+
+ MessageConsumer consumer = session.createConsumer(_queue);
+
+ _logger.info("Verify messages are still on the queue");
+
+ Message tempMsg;
+
+ for (int msgCount = 0; msgCount < messages; msgCount++)
+ {
+ tempMsg = (TextMessage) consumer.receive(RECEIVE_TIMEOUT);
+ if (tempMsg == null)
+ {
+ fail("Message " + msgCount + " not retrieved from queue");
+ }
+ }
+
+ //Close this new connection
+ connection.close();
+
+ _logger.info("All messages recevied from queue");
+
+ //ensure no message left.
+ checkQueueDepth(0);
+ }
+
+ protected void checkQueueDepthWithSelectors(int totalMessages, int clients) throws JMSException
+ {
+
+ String selector = MESSAGE_ID_PROPERTY + " % " + clients + " = 0" ;
+
+ checkOverlappingMultipleGetEnum(totalMessages / clients, clients, selector);
+ }
+
+
+ /**
+ * This tests you can browse an empty queue, see QPID-785
+ *
+ * @throws Exception
+ */
+ public void testBrowsingEmptyQueue() throws Exception
+ {
+ checkQueueDepth(0);
+ }
+
+ /*
+ * Test Messages Remain on Queue
+ * Create a queu and send messages to it. Browse them and then receive them all to verify they were still there
+ *
+ */
+ public void testQueueBrowserMsgsRemainOnQueue() throws Exception
+ {
+ int messages = 10;
+
+ sendMessages(messages);
+
+ checkQueueDepth(messages);
+
+ validate(messages);
+ }
+
+
+ public void testClosingBrowserMidReceiving() throws NamingException, JMSException
+ {
+ int messages = 100;
+
+ sendMessages(messages);
+
+ checkQueueDepth(messages);
+
+ closeBrowserBeforeAfterGetNext(10);
+
+ validate(messages);
+ }
+
+ /**
+ * This tests that multiple getEnumerations on a QueueBrowser return the required number of messages.
+ * @throws NamingException
+ * @throws JMSException
+ */
+ public void testMultipleGetEnum() throws NamingException, JMSException
+ {
+ int messages = 10;
+
+ sendMessages(messages);
+
+ checkQueueDepth(messages);
+
+ checkMultipleGetEnum(messages, 5);
+
+ validate(messages);
+ }
+
+ public void testMultipleOverlappingGetEnum() throws NamingException, JMSException
+ {
+ int messages = 25;
+
+ sendMessages(messages);
+
+ checkQueueDepth(messages);
+
+ checkOverlappingMultipleGetEnum(messages, 5);
+
+ validate(messages);
+ }
+
+
+ public void testBrowsingWithSelector() throws JMSException
+ {
+ int messages = 40;
+
+ sendMessages(messages);
+
+ checkQueueDepth(messages);
+
+ for (int clients = 2; clients <= 10; clients++)
+ {
+ checkQueueDepthWithSelectors(messages, clients);
+ }
+
+ validate(messages);
+ }
+
+ public void testBrowsingWhileStopped() throws JMSException
+ {
+ _clientConnection.stop();
+
+ try
+ {
+ QueueBrowser browser = _clientSession.createBrowser(getTestQueue());
+ Enumeration messages = browser.getEnumeration();
+ fail("Expected exception when attempting to browse on a stopped connection did not occur");
+ }
+ catch(javax.jms.IllegalStateException e)
+ {
+ // pass
+ }
+
+ }
+
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/client/QueueBrowserClientAckTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/client/QueueBrowserClientAckTest.java
new file mode 100644
index 0000000000..f30b8043ad
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/client/QueueBrowserClientAckTest.java
@@ -0,0 +1,34 @@
+/*
+ *
+ * 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.qpid.test.client;
+
+import javax.jms.Session;
+
+public class QueueBrowserClientAckTest extends QueueBrowserAutoAckTest
+{
+
+
+ protected void setupSession() throws Exception
+ {
+ _clientSession = _clientConnection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ }
+
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/client/QueueBrowserDupsOkTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/client/QueueBrowserDupsOkTest.java
new file mode 100644
index 0000000000..b19809b8f2
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/client/QueueBrowserDupsOkTest.java
@@ -0,0 +1,31 @@
+/*
+ *
+ * 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.qpid.test.client;
+
+import javax.jms.Session;
+
+public class QueueBrowserDupsOkTest extends QueueBrowserAutoAckTest
+{
+ protected void setupSession() throws Exception
+ {
+ _clientSession = _clientConnection.createSession(false, Session.DUPS_OK_ACKNOWLEDGE);
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/client/QueueBrowserNoAckTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/client/QueueBrowserNoAckTest.java
new file mode 100644
index 0000000000..c97343464c
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/client/QueueBrowserNoAckTest.java
@@ -0,0 +1,33 @@
+/*
+ *
+ * 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.qpid.test.client;
+
+import org.apache.qpid.client.AMQSession;
+
+
+public class QueueBrowserNoAckTest extends QueueBrowserAutoAckTest
+{
+
+ protected void setupSession() throws Exception
+ {
+ _clientSession = _clientConnection.createSession(false, AMQSession.NO_ACKNOWLEDGE);
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/client/QueueBrowserPreAckTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/client/QueueBrowserPreAckTest.java
new file mode 100644
index 0000000000..bb1c0d3698
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/client/QueueBrowserPreAckTest.java
@@ -0,0 +1,32 @@
+/*
+ *
+ * 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.qpid.test.client;
+
+import org.apache.qpid.client.AMQSession;
+
+public class QueueBrowserPreAckTest extends QueueBrowserAutoAckTest
+{
+
+ protected void setupSession() throws Exception
+ {
+ _clientSession = _clientConnection.createSession(false, AMQSession.PRE_ACKNOWLEDGE);
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/client/QueueBrowserTransactedTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/client/QueueBrowserTransactedTest.java
new file mode 100644
index 0000000000..d79788f017
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/client/QueueBrowserTransactedTest.java
@@ -0,0 +1,31 @@
+/*
+ *
+ * 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.qpid.test.client;
+
+import javax.jms.Session;
+
+public class QueueBrowserTransactedTest extends QueueBrowserAutoAckTest
+{
+ protected void setupSession() throws Exception
+ {
+ _clientSession = _clientConnection.createSession(true, Session.SESSION_TRANSACTED);
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/client/RollbackOrderTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/client/RollbackOrderTest.java
new file mode 100644
index 0000000000..d0968aefc7
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/client/RollbackOrderTest.java
@@ -0,0 +1,189 @@
+/*
+ *
+ * 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.qpid.test.client;
+
+import junit.framework.AssertionFailedError;
+
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+import javax.jms.Connection;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageListener;
+import javax.jms.Queue;
+import javax.jms.Session;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * RollbackOrderTest, QPID-1864, QPID-1871
+ *
+ * Description:
+ *
+ * The problem that this test is exposing is that the dispatcher used to be capable
+ * of holding on to a message when stopped. This meant that when the rollback was
+ * called and the dispatcher stopped it may have hold of a message. So after all
+ * the local queues(preDeliveryQueue, SynchronousQueue, PostDeliveryTagQueue)
+ * have been cleared the client still had a single message, the one the
+ * dispatcher was holding on to.
+ *
+ * As a result the TxRollback operation would run and then release the dispatcher.
+ * Whilst the dispatcher would then proceed to reject the message it was holding
+ * the Broker would already have resent that message so the rejection would silently
+ * fail.
+ *
+ * And the client would receive that single message 'early', depending on the
+ * number of messages already received when rollback was called.
+ *
+ *
+ * Aims:
+ *
+ * The tests puts 50 messages on to the queue.
+ *
+ * The test then tries to cause the dispatcher to stop whilst it is in the process
+ * of moving a message from the preDeliveryQueue to a consumers sychronousQueue.
+ *
+ * To exercise this path we have 50 message flowing to the client to give the
+ * dispatcher a bit of work to do moving messages.
+ *
+ * Then we loop - 10 times
+ * - Validating that the first message received is always message 1.
+ * - Receive a few more so that there are a few messages to reject.
+ * - call rollback, to try and catch the dispatcher mid process.
+ *
+ * Outcome:
+ *
+ * The hope is that we catch the dispatcher mid process and cause a BasicReject
+ * to fail. Which will be indicated in the log but will also cause that failed
+ * rejected message to be the next to be delivered which will not be message 1
+ * as expected.
+ *
+ * We are testing a race condition here but we can check through the log file if
+ * the race condition occurred. However, performing that check will only validate
+ * the problem exists and will not be suitable as part of a system test.
+ *
+ * @see org.apache.qpid.test.unit.transacted.CommitRollbackTest
+ */
+public class RollbackOrderTest extends QpidBrokerTestCase
+{
+
+ private Connection _connection;
+ private Queue _queue;
+ private Session _session;
+ private MessageConsumer _consumer;
+
+ @Override public void setUp() throws Exception
+ {
+ super.setUp();
+ _connection = getConnection();
+
+ _session = _connection.createSession(true, Session.SESSION_TRANSACTED);
+ _queue = _session.createQueue(getTestQueueName());
+ _consumer = _session.createConsumer(_queue);
+
+ //Send more messages so it is more likely that the dispatcher is
+ // processing on rollback.
+ sendMessage(_session, _queue, 50);
+ _session.commit();
+
+ }
+
+ public void testOrderingAfterRollback() throws Exception
+ {
+ //Start the session now so we
+ _connection.start();
+
+ for (int i = 0; i < 20; i++)
+ {
+ Message msg = _consumer.receive();
+ assertEquals("Incorrect Message Received", 0, msg.getIntProperty(INDEX));
+
+ // Pull additional messages through so we have some reject work to do
+ for (int m=1; m <= 5 ; m++)
+ {
+ msg = _consumer.receive();
+ assertEquals("Incorrect Message Received (message " + m + ")", m, msg.getIntProperty(INDEX));
+ }
+
+ _session.rollback();
+ }
+ }
+
+ public void testOrderingAfterRollbackOnMessage() throws Exception
+ {
+ final CountDownLatch count= new CountDownLatch(20);
+ final Exception exceptions[] = new Exception[20];
+ final AtomicBoolean failed = new AtomicBoolean(false);
+
+ _consumer.setMessageListener(new MessageListener()
+ {
+
+ public void onMessage(Message message)
+ {
+
+ Message msg = message;
+ try
+ {
+ count.countDown();
+ assertEquals("Incorrect Message Received", 0, msg.getIntProperty(INDEX));
+
+ _session.rollback();
+ }
+ catch (JMSException e)
+ {
+ _logger.error("Error:" + e.getMessage(), e);
+ exceptions[(int)count.getCount()] = e;
+ }
+ catch (AssertionFailedError cf)
+ {
+ // End Test if Equality test fails
+ while (count.getCount() != 0)
+ {
+ count.countDown();
+ }
+
+ _logger.error("Error:" + cf.getMessage(), cf);
+ failed.set(true);
+ }
+ }
+ });
+ //Start the session now so we
+ _connection.start();
+
+ count.await(10l, TimeUnit.SECONDS);
+ assertEquals("Not all message received. Count should be 0.", 0, count.getCount());
+
+ for (Exception e : exceptions)
+ {
+ if (e != null)
+ {
+ _logger.error("Encountered exception", e);
+ failed.set(true);
+ }
+ }
+
+ _connection.close();
+
+ assertFalse("Exceptions thrown during test run, Check Std.err.", failed.get());
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/client/UnroutableMessageTestExceptionListener.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/client/UnroutableMessageTestExceptionListener.java
new file mode 100644
index 0000000000..99afe0015d
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/client/UnroutableMessageTestExceptionListener.java
@@ -0,0 +1,178 @@
+/*
+ * 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.qpid.test.client;
+
+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.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+import javax.jms.Connection;
+import javax.jms.ExceptionListener;
+import javax.jms.JMSException;
+import javax.jms.Message;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.client.AMQNoConsumersException;
+import org.apache.qpid.client.AMQNoRouteException;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.protocol.AMQConstant;
+
+/**
+ * Provides utility methods for checking exceptions that are thrown on the client side when a message is
+ * not routable.
+ *
+ * Exception objects are passed either explicitly as method parameters or implicitly
+ * by previously doing {@link Connection#setExceptionListener(ExceptionListener)}.
+ */
+public class UnroutableMessageTestExceptionListener implements ExceptionListener
+{
+ private static final Logger _logger = Logger.getLogger(UnroutableMessageTestExceptionListener.class);
+
+ /**
+ * Number of seconds to check for an event that should should NOT happen
+ */
+ private static final int NEGATIVE_TIMEOUT = 2;
+
+ /**
+ * Number of seconds to keep checking for an event that should should happen
+ */
+ private static final int POSITIVE_TIMEOUT = 30;
+
+ private BlockingQueue<JMSException> _exceptions = new ArrayBlockingQueue<JMSException>(1);
+
+ @Override
+ public void onException(JMSException e)
+ {
+ _logger.info("Received exception " + e);
+ _exceptions.add(e);
+ }
+
+ public void assertReceivedNoRouteWithReturnedMessage(Message message, String intendedQueueName)
+ {
+ JMSException exception = getReceivedException();
+ assertNoRouteExceptionWithReturnedMessage(exception, message, intendedQueueName);
+ }
+
+ public void assertReceivedNoRoute(String intendedQueueName)
+ {
+ JMSException exception = getReceivedException();
+ assertNoRoute(exception, intendedQueueName);
+ }
+
+ public void assertReceivedNoConsumersWithReturnedMessage(Message message)
+ {
+ JMSException exception = getReceivedException();
+ AMQNoConsumersException noConsumersException = (AMQNoConsumersException) exception.getLinkedException();
+ assertNotNull("AMQNoConsumersException should be linked to JMSException", noConsumersException);
+ Message bounceMessage = (Message) noConsumersException.getUndeliveredMessage();
+ assertNotNull("Bounced Message is expected", bounceMessage);
+
+ try
+ {
+ assertEquals("Unexpected message is bounced", message.getJMSMessageID(), bounceMessage.getJMSMessageID());
+ }
+ catch (JMSException e)
+ {
+ throw new RuntimeException("Couldn't check exception", e);
+ }
+ }
+
+ public void assertReceivedReturnedMessageWithLongExceptionMessage(Message message, AMQQueue queue)
+ {
+ JMSException exception = getReceivedException();
+ assertNoRouteException(exception, message);
+ AMQShortString exchangeName = queue.getExchangeName();
+ String expectedMessage = "Error: No Route for message [Exchange: " + exchangeName.asString().substring(0, 220) + "...";
+ assertTrue("Unexpected exception message: " + exception.getMessage(), exception.getMessage().contains(expectedMessage));
+ }
+
+ public void assertNoRouteExceptionWithReturnedMessage(
+ JMSException exception, Message message, String intendedQueueName)
+ {
+ assertNoRoute(exception, intendedQueueName);
+
+ assertNoRouteException(exception, message);
+ }
+
+ private void assertNoRouteException(JMSException exception, Message message)
+ {
+ AMQNoRouteException noRouteException = (AMQNoRouteException) exception.getLinkedException();
+ assertNotNull("AMQNoRouteException should be linked to JMSException", noRouteException);
+ Message bounceMessage = (Message) noRouteException.getUndeliveredMessage();
+ assertNotNull("Bounced Message is expected", bounceMessage);
+
+ try
+ {
+ assertEquals("Unexpected message is bounced", message.getJMSMessageID(), bounceMessage.getJMSMessageID());
+ }
+ catch (JMSException e)
+ {
+ throw new RuntimeException("Couldn't check exception", e);
+ }
+ }
+
+ public void assertNoRoute(JMSException exception, String intendedQueueName)
+ {
+ assertTrue(
+ exception + " message should contain intended queue name",
+ exception.getMessage().contains(intendedQueueName));
+
+ AMQException noRouteException = (AMQException) exception.getLinkedException();
+ assertNotNull("AMQException should be linked to JMSException", noRouteException);
+
+ assertEquals(AMQConstant.NO_ROUTE, noRouteException.getErrorCode());
+ assertTrue(
+ "Linked exception " + noRouteException + " message should contain intended queue name",
+ noRouteException.getMessage().contains(intendedQueueName));
+ }
+
+
+ public void assertNoException()
+ {
+ try
+ {
+ assertNull("Unexpected JMSException", _exceptions.poll(NEGATIVE_TIMEOUT, TimeUnit.SECONDS));
+ }
+ catch (InterruptedException e)
+ {
+ throw new RuntimeException("Couldn't check exception", e);
+ }
+ }
+
+ private JMSException getReceivedException()
+ {
+ try
+ {
+ JMSException exception = _exceptions.poll(POSITIVE_TIMEOUT, TimeUnit.SECONDS);
+ assertNotNull("JMSException is expected", exception);
+ return exception;
+ }
+ catch(InterruptedException e)
+ {
+ throw new RuntimeException("Couldn't check exception", e);
+ }
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/client/destination/AddressBasedDestinationTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/client/destination/AddressBasedDestinationTest.java
new file mode 100644
index 0000000000..14cadc2389
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/client/destination/AddressBasedDestinationTest.java
@@ -0,0 +1,1464 @@
+/*
+ *
+ * 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.qpid.test.client.destination;
+
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.jms.Connection;
+import javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.QueueBrowser;
+import javax.jms.QueueReceiver;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import javax.jms.Topic;
+import javax.jms.TopicSession;
+import javax.jms.TopicSubscriber;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.qpid.client.AMQAnyDestination;
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQDestination;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.AMQSession_0_10;
+import org.apache.qpid.client.message.QpidMessageProperties;
+import org.apache.qpid.jndi.PropertiesFileInitialContextFactory;
+import org.apache.qpid.messaging.Address;
+import org.apache.qpid.protocol.AMQConstant;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+import org.apache.qpid.transport.ExecutionErrorCode;
+
+public class AddressBasedDestinationTest extends QpidBrokerTestCase
+{
+ private static final Logger _logger = LoggerFactory.getLogger(AddressBasedDestinationTest.class);
+ private Connection _connection;
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+ _connection = getConnection() ;
+ _connection.start();
+ }
+
+ @Override
+ public void tearDown() throws Exception
+ {
+ _connection.close();
+ super.tearDown();
+ }
+
+ public void testCreateOptions() throws Exception
+ {
+ Session jmsSession = _connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
+ MessageProducer prod;
+ MessageConsumer cons;
+
+ // default (create never, assert never) -------------------
+ // create never --------------------------------------------
+ String addr1 = "ADDR:testQueue1";
+ AMQDestination dest = new AMQAnyDestination(addr1);
+ try
+ {
+ cons = jmsSession.createConsumer(dest);
+ }
+ catch(JMSException e)
+ {
+ assertTrue(e.getMessage().contains("The name 'testQueue1' supplied in the address " +
+ "doesn't resolve to an exchange or a queue"));
+ }
+
+ try
+ {
+ prod = jmsSession.createProducer(dest);
+ }
+ catch(JMSException e)
+ {
+ assertTrue(e.getCause().getCause().getMessage().contains("The name 'testQueue1' supplied in the address " +
+ "doesn't resolve to an exchange or a queue"));
+ }
+
+ assertFalse("Queue should not be created",(
+ (AMQSession_0_10)jmsSession).isQueueExist(dest,false));
+
+
+ // create always -------------------------------------------
+ addr1 = "ADDR:testQueue1; { create: always }";
+ dest = new AMQAnyDestination(addr1);
+ cons = jmsSession.createConsumer(dest);
+
+ assertTrue("Queue not created as expected",(
+ (AMQSession_0_10)jmsSession).isQueueExist(dest, true));
+ assertTrue("Queue not bound as expected",(
+ (AMQSession_0_10)jmsSession).isQueueBound("",
+ dest.getAddressName(),dest.getAddressName(), null));
+
+ // create receiver -----------------------------------------
+ addr1 = "ADDR:testQueue2; { create: receiver }";
+ dest = new AMQAnyDestination(addr1);
+ try
+ {
+ prod = jmsSession.createProducer(dest);
+ }
+ catch(JMSException e)
+ {
+ assertTrue(e.getCause().getCause().getMessage().contains("The name 'testQueue2' supplied in the address " +
+ "doesn't resolve to an exchange or a queue"));
+ }
+
+ assertFalse("Queue should not be created",(
+ (AMQSession_0_10)jmsSession).isQueueExist(dest, false));
+
+
+ cons = jmsSession.createConsumer(dest);
+
+ assertTrue("Queue not created as expected",(
+ (AMQSession_0_10)jmsSession).isQueueExist(dest, true));
+ assertTrue("Queue not bound as expected",(
+ (AMQSession_0_10)jmsSession).isQueueBound("",
+ dest.getAddressName(),dest.getAddressName(), null));
+
+ // create never --------------------------------------------
+ addr1 = "ADDR:testQueue3; { create: never }";
+ dest = new AMQAnyDestination(addr1);
+ try
+ {
+ cons = jmsSession.createConsumer(dest);
+ }
+ catch(JMSException e)
+ {
+ assertTrue(e.getMessage().contains("The name 'testQueue3' supplied in the address " +
+ "doesn't resolve to an exchange or a queue"));
+ }
+
+ try
+ {
+ prod = jmsSession.createProducer(dest);
+ }
+ catch(JMSException e)
+ {
+ assertTrue(e.getCause().getCause().getMessage().contains("The name 'testQueue3' supplied in the address " +
+ "doesn't resolve to an exchange or a queue"));
+ }
+
+ assertFalse("Queue should not be created",(
+ (AMQSession_0_10)jmsSession).isQueueExist(dest, false));
+
+ // create sender ------------------------------------------
+ addr1 = "ADDR:testQueue3; { create: sender }";
+ dest = new AMQAnyDestination(addr1);
+
+ try
+ {
+ cons = jmsSession.createConsumer(dest);
+ }
+ catch(JMSException e)
+ {
+ assertTrue(e.getMessage().contains("The name 'testQueue3' supplied in the address " +
+ "doesn't resolve to an exchange or a queue"));
+ }
+ assertFalse("Queue should not be created",(
+ (AMQSession_0_10)jmsSession).isQueueExist(dest, false));
+
+ prod = jmsSession.createProducer(dest);
+ assertTrue("Queue not created as expected",(
+ (AMQSession_0_10)jmsSession).isQueueExist(dest, true));
+ assertTrue("Queue not bound as expected",(
+ (AMQSession_0_10)jmsSession).isQueueBound("",
+ dest.getAddressName(),dest.getAddressName(), null));
+
+ }
+
+ public void testCreateQueue() throws Exception
+ {
+ Session jmsSession = _connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
+
+ String addr = "ADDR:my-queue/hello; " +
+ "{" +
+ "create: always, " +
+ "node: " +
+ "{" +
+ "durable: true ," +
+ "x-declare: " +
+ "{" +
+ "exclusive: true," +
+ "arguments: {" +
+ "'qpid.alert_size': 1000," +
+ "'qpid.alert_count': 100" +
+ "}" +
+ "}, " +
+ "x-bindings: [{exchange : 'amq.direct', key : test}, " +
+ "{exchange : 'amq.fanout'}," +
+ "{exchange: 'amq.match', arguments: {x-match: any, dep: sales, loc: CA}}," +
+ "{exchange : 'amq.topic', key : 'a.#'}" +
+ "]," +
+
+ "}" +
+ "}";
+ AMQDestination dest = new AMQAnyDestination(addr);
+ MessageConsumer cons = jmsSession.createConsumer(dest);
+ cons.close();
+
+ // Even if the consumer is closed the queue and the bindings should be intact.
+
+ assertTrue("Queue not created as expected",(
+ (AMQSession_0_10)jmsSession).isQueueExist(dest, true));
+
+ assertTrue("Queue not bound as expected",(
+ (AMQSession_0_10)jmsSession).isQueueBound("",
+ dest.getAddressName(),dest.getAddressName(), null));
+
+ assertTrue("Queue not bound as expected",(
+ (AMQSession_0_10)jmsSession).isQueueBound("amq.direct",
+ dest.getAddressName(),"test", null));
+
+ assertTrue("Queue not bound as expected",(
+ (AMQSession_0_10)jmsSession).isQueueBound("amq.fanout",
+ dest.getAddressName(),null, null));
+
+ assertTrue("Queue not bound as expected",(
+ (AMQSession_0_10)jmsSession).isQueueBound("amq.topic",
+ dest.getAddressName(),"a.#", null));
+
+ Map<String,Object> args = new HashMap<String,Object>();
+ args.put("x-match","any");
+ args.put("dep","sales");
+ args.put("loc","CA");
+ assertTrue("Queue not bound as expected",(
+ (AMQSession_0_10)jmsSession).isQueueBound("amq.match",
+ dest.getAddressName(),null, args));
+
+ MessageProducer prod = jmsSession.createProducer(dest);
+ prod.send(jmsSession.createTextMessage("test"));
+
+ MessageConsumer cons2 = jmsSession.createConsumer(jmsSession.createQueue("ADDR:my-queue"));
+ Message m = cons2.receive(1000);
+ assertNotNull("Should receive message sent to my-queue",m);
+ assertEquals("The subject set in the message is incorrect","hello",m.getStringProperty(QpidMessageProperties.QPID_SUBJECT));
+ }
+
+ public void testCreateExchange() throws Exception
+ {
+ createExchangeImpl(false, false, false);
+ }
+
+ /**
+ * Verify creating an exchange via an Address, with supported
+ * exchange-declare arguments.
+ */
+ public void testCreateExchangeWithArgs() throws Exception
+ {
+ createExchangeImpl(true, false, false);
+ }
+
+ /**
+ * Verify that when creating an exchange via an Address, if a
+ * nonsense argument is specified the broker throws an execution
+ * exception back on the session with NOT_IMPLEMENTED status.
+ */
+ public void testCreateExchangeWithNonsenseArgs() throws Exception
+ {
+ createExchangeImpl(true, true, false);
+ }
+
+ private void createExchangeImpl(final boolean withExchangeArgs,
+ final boolean useNonsenseArguments,
+ final boolean useNonsenseExchangeType) throws Exception
+ {
+ Session jmsSession = _connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
+
+ String addr = "ADDR:my-exchange/hello; " +
+ "{ " +
+ "create: always, " +
+ "node: " +
+ "{" +
+ "type: topic, " +
+ "x-declare: " +
+ "{ " +
+ "type:" +
+ (useNonsenseExchangeType ? "nonsense" : "direct") +
+ ", " +
+ "auto-delete: true" +
+ createExchangeArgsString(withExchangeArgs, useNonsenseArguments) +
+ "}" +
+ "}" +
+ "}";
+
+ AMQDestination dest = new AMQAnyDestination(addr);
+
+ MessageConsumer cons;
+ try
+ {
+ cons = jmsSession.createConsumer(dest);
+ if(useNonsenseArguments || useNonsenseExchangeType)
+ {
+ fail("Expected execution exception during exchange declare did not occur");
+ }
+ }
+ catch(JMSException e)
+ {
+ if(useNonsenseArguments && e.getCause().getMessage().contains(ExecutionErrorCode.NOT_IMPLEMENTED.toString()))
+ {
+ //expected because we used an argument which the broker doesn't have functionality
+ //for. We can't do the rest of the test as a result of the exception, just stop.
+ return;
+ }
+ else if(useNonsenseExchangeType && (e.getErrorCode().equals(String.valueOf(AMQConstant.NOT_FOUND.getCode()))))
+ {
+ return;
+ }
+ else
+ {
+ fail("Unexpected exception whilst creating consumer: " + e);
+ }
+ }
+
+ assertTrue("Exchange not created as expected",(
+ (AMQSession_0_10)jmsSession).isExchangeExist(dest,true));
+
+ // The existence of the queue is implicitly tested here
+ assertTrue("Queue not bound as expected",(
+ (AMQSession_0_10)jmsSession).isQueueBound("my-exchange",
+ dest.getQueueName(),"hello", null));
+
+ // The client should be able to query and verify the existence of my-exchange (QPID-2774)
+ dest = new AMQAnyDestination("ADDR:my-exchange; {create: never}");
+ cons = jmsSession.createConsumer(dest);
+ }
+
+ private String createExchangeArgsString(final boolean withExchangeArgs,
+ final boolean useNonsenseArguments)
+ {
+ String argsString;
+
+ if(withExchangeArgs && useNonsenseArguments)
+ {
+ argsString = ", arguments: {" +
+ "'abcd.1234.wxyz': 1, " +
+ "}";
+ }
+ else if(withExchangeArgs)
+ {
+ argsString = ", arguments: {" +
+ "'qpid.msg_sequence': 1, " +
+ "'qpid.ive': 1" +
+ "}";
+ }
+ else
+ {
+ argsString = "";
+ }
+
+ return argsString;
+ }
+
+ public void checkQueueForBindings(Session jmsSession, AMQDestination dest,String headersBinding) throws Exception
+ {
+ assertTrue("Queue not created as expected",(
+ (AMQSession_0_10)jmsSession).isQueueExist(dest, true));
+
+ assertTrue("Queue not bound as expected",(
+ (AMQSession_0_10)jmsSession).isQueueBound("",
+ dest.getAddressName(),dest.getAddressName(), null));
+
+ assertTrue("Queue not bound as expected",(
+ (AMQSession_0_10)jmsSession).isQueueBound("amq.direct",
+ dest.getAddressName(),"test", null));
+
+ assertTrue("Queue not bound as expected",(
+ (AMQSession_0_10)jmsSession).isQueueBound("amq.topic",
+ dest.getAddressName(),"a.#", null));
+
+ Address a = Address.parse(headersBinding);
+ assertTrue("Queue not bound as expected",(
+ (AMQSession_0_10)jmsSession).isQueueBound("amq.match",
+ dest.getAddressName(),null, a.getOptions()));
+ }
+
+ /**
+ * Test goal: Verifies that a producer and consumer creation triggers the correct
+ * behavior for x-bindings specified in node props.
+ */
+ public void testBindQueueWithArgs() throws Exception
+ {
+
+ Session jmsSession = _connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
+ String headersBinding = "{exchange: 'amq.match', arguments: {x-match: any, dep: sales, loc: CA}}";
+
+ String addr = "node: " +
+ "{" +
+ "durable: true ," +
+ "x-declare: " +
+ "{ " +
+ "auto-delete: true," +
+ "arguments: {'qpid.alert_count': 100}" +
+ "}, " +
+ "x-bindings: [{exchange : 'amq.direct', key : test}, " +
+ "{exchange : 'amq.topic', key : 'a.#'}," +
+ headersBinding +
+ "]" +
+ "}" +
+ "}";
+
+
+ AMQDestination dest1 = new AMQAnyDestination("ADDR:my-queue/hello; {create: receiver, " +addr);
+ MessageConsumer cons = jmsSession.createConsumer(dest1);
+ checkQueueForBindings(jmsSession,dest1,headersBinding);
+
+ AMQDestination dest2 = new AMQAnyDestination("ADDR:my-queue2/hello; {create: sender, " +addr);
+ MessageProducer prod = jmsSession.createProducer(dest2);
+ checkQueueForBindings(jmsSession,dest2,headersBinding);
+ }
+
+ /**
+ * Test goal: Verifies the capacity property in address string is handled properly.
+ * Test strategy:
+ * Creates a destination with capacity 10.
+ * Creates consumer with client ack.
+ * Sends 15 messages to the queue, tries to receive 10.
+ * Tries to receive the 11th message and checks if its null.
+ *
+ * Since capacity is 10 and we haven't acked any messages,
+ * we should not have received the 11th.
+ *
+ * Acks the 10th message and verifies we receive the rest of the msgs.
+ */
+ public void testCapacity() throws Exception
+ {
+ verifyCapacity("ADDR:my-queue; {create: always, link:{capacity: 10}}");
+ }
+
+ public void testSourceAndTargetCapacity() throws Exception
+ {
+ verifyCapacity("ADDR:my-queue; {create: always, link:{capacity: {source:10, target:15} }}");
+ }
+
+ private void verifyCapacity(String address) throws Exception
+ {
+ if (!isCppBroker())
+ {
+ _logger.info("Not C++ broker, exiting test");
+ return;
+ }
+
+ Session jmsSession = _connection.createSession(false,Session.CLIENT_ACKNOWLEDGE);
+
+ AMQDestination dest = new AMQAnyDestination(address);
+ MessageConsumer cons = jmsSession.createConsumer(dest);
+ MessageProducer prod = jmsSession.createProducer(dest);
+
+ for (int i=0; i< 15; i++)
+ {
+ prod.send(jmsSession.createTextMessage("msg" + i) );
+ }
+ Message msg = null;
+ for (int i=0; i< 10; i++)
+ {
+ msg = cons.receive(RECEIVE_TIMEOUT);
+ assertNotNull("Should have received " + i + " message", msg);
+ assertEquals("Unexpected message received", "msg" + i, ((TextMessage)msg).getText());
+ }
+ assertNull("Shouldn't have received the 11th message as capacity is 10",cons.receive(RECEIVE_TIMEOUT));
+ msg.acknowledge();
+ for (int i=11; i<16; i++)
+ {
+ assertNotNull("Should have received the " + i + "th message as we acked the last 10",cons.receive(RECEIVE_TIMEOUT));
+ }
+ }
+
+ /**
+ * Test goal: Verifies if the new address format based destinations
+ * can be specified and loaded correctly from the properties file.
+ *
+ */
+ public void testLoadingFromPropertiesFile() throws Exception
+ {
+ Hashtable<String,String> map = new Hashtable<String,String>();
+ map.put("destination.myQueue1", "ADDR:my-queue/hello; {create: always, node: " +
+ "{x-declare: {auto-delete: true, arguments : {'qpid.alert_size': 1000}}}}");
+
+ map.put("destination.myQueue2", "ADDR:my-queue2; { create: receiver }");
+
+ map.put("destination.myQueue3", "BURL:direct://amq.direct/my-queue3?routingkey='test'");
+
+ PropertiesFileInitialContextFactory props = new PropertiesFileInitialContextFactory();
+ Context ctx = props.getInitialContext(map);
+
+ AMQDestination dest1 = (AMQDestination)ctx.lookup("myQueue1");
+ AMQDestination dest2 = (AMQDestination)ctx.lookup("myQueue2");
+ AMQDestination dest3 = (AMQDestination)ctx.lookup("myQueue3");
+
+ Session jmsSession = _connection.createSession(false,Session.CLIENT_ACKNOWLEDGE);
+ MessageConsumer cons1 = jmsSession.createConsumer(dest1);
+ MessageConsumer cons2 = jmsSession.createConsumer(dest2);
+ MessageConsumer cons3 = jmsSession.createConsumer(dest3);
+
+ assertTrue("Destination1 was not created as expected",(
+ (AMQSession_0_10)jmsSession).isQueueExist(dest1, true));
+
+ assertTrue("Destination1 was not bound as expected",(
+ (AMQSession_0_10)jmsSession).isQueueBound("",
+ dest1.getAddressName(),dest1.getAddressName(), null));
+
+ assertTrue("Destination2 was not created as expected",(
+ (AMQSession_0_10)jmsSession).isQueueExist(dest2,true));
+
+ assertTrue("Destination2 was not bound as expected",(
+ (AMQSession_0_10)jmsSession).isQueueBound("",
+ dest2.getAddressName(),dest2.getAddressName(), null));
+
+ MessageProducer producer = jmsSession.createProducer(dest3);
+ producer.send(jmsSession.createTextMessage("Hello"));
+ TextMessage msg = (TextMessage)cons3.receive(1000);
+ assertEquals("Destination3 was not created as expected.",msg.getText(),"Hello");
+ }
+
+ /**
+ * Test goal: Verifies the subject can be overridden using "qpid.subject" message property.
+ * Test strategy: Creates and address with a default subject "topic1"
+ * Creates a message with "qpid.subject"="topic2" and sends it.
+ * Verifies that the message goes to "topic2" instead of "topic1".
+ */
+ public void testOverridingSubject() throws Exception
+ {
+ Session jmsSession = _connection.createSession(false,Session.CLIENT_ACKNOWLEDGE);
+
+ AMQDestination topic1 = new AMQAnyDestination("ADDR:amq.topic/topic1; {link:{name: queue1}}");
+
+ MessageProducer prod = jmsSession.createProducer(topic1);
+
+ Message m = jmsSession.createTextMessage("Hello");
+ m.setStringProperty("qpid.subject", "topic2");
+
+ MessageConsumer consForTopic1 = jmsSession.createConsumer(topic1);
+ MessageConsumer consForTopic2 = jmsSession.createConsumer(new AMQAnyDestination("ADDR:amq.topic/topic2; {link:{name: queue2}}"));
+
+ prod.send(m);
+ Message msg = consForTopic1.receive(1000);
+ assertNull("message shouldn't have been sent to topic1",msg);
+
+ msg = consForTopic2.receive(1000);
+ assertNotNull("message should have been sent to topic2",msg);
+
+ }
+
+ /**
+ * Test goal: Verifies that session.createQueue method
+ * works as expected both with the new and old addressing scheme.
+ */
+ public void testSessionCreateQueue() throws Exception
+ {
+ Session ssn = _connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
+
+ // Using the BURL method
+ Destination queue = ssn.createQueue("my-queue");
+ MessageProducer prod = ssn.createProducer(queue);
+ MessageConsumer cons = ssn.createConsumer(queue);
+ assertTrue("my-queue was not created as expected",(
+ (AMQSession_0_10)ssn).isQueueBound("amq.direct",
+ "my-queue","my-queue", null));
+
+ prod.send(ssn.createTextMessage("test"));
+ assertNotNull("consumer should receive a message",cons.receive(1000));
+ cons.close();
+
+ // Using the ADDR method
+ // default case
+ queue = ssn.createQueue("ADDR:my-queue2");
+ try
+ {
+ prod = ssn.createProducer(queue);
+ fail("The client should throw an exception, since there is no queue present in the broker");
+ }
+ catch(Exception e)
+ {
+ String s = "The name 'my-queue2' supplied in the address " +
+ "doesn't resolve to an exchange or a queue";
+ assertEquals(s,e.getCause().getCause().getMessage());
+ }
+
+ // explicit create case
+ queue = ssn.createQueue("ADDR:my-queue2; {create: sender}");
+ prod = ssn.createProducer(queue);
+ cons = ssn.createConsumer(queue);
+ assertTrue("my-queue2 was not created as expected",(
+ (AMQSession_0_10)ssn).isQueueBound("",
+ "my-queue2","my-queue2", null));
+
+ prod.send(ssn.createTextMessage("test"));
+ assertNotNull("consumer should receive a message",cons.receive(1000));
+ cons.close();
+
+ // Using the ADDR method to create a more complicated queue
+ String addr = "ADDR:amq.direct/x512; {" +
+ "link : {name : 'MY.RESP.QUEUE', " +
+ "x-declare : { auto-delete: true, exclusive: true, " +
+ "arguments : {'qpid.alert_size': 1000, 'qpid.policy_type': ring} } } }";
+ queue = ssn.createQueue(addr);
+
+ cons = ssn.createConsumer(queue);
+ prod = ssn.createProducer(queue);
+ assertTrue("MY.RESP.QUEUE was not created as expected",(
+ (AMQSession_0_10)ssn).isQueueBound("amq.direct",
+ "MY.RESP.QUEUE","x512", null));
+ cons.close();
+ }
+
+ /**
+ * Test goal: Verifies that session.creatTopic method works as expected
+ * both with the new and old addressing scheme.
+ */
+ public void testSessionCreateTopic() throws Exception
+ {
+ sessionCreateTopicImpl(false);
+ }
+
+ /**
+ * Test goal: Verifies that session.creatTopic method works as expected
+ * both with the new and old addressing scheme when adding exchange arguments.
+ */
+ public void testSessionCreateTopicWithExchangeArgs() throws Exception
+ {
+ sessionCreateTopicImpl(true);
+ }
+
+ private void sessionCreateTopicImpl(boolean withExchangeArgs) throws Exception
+ {
+ Session ssn = _connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
+
+ // Using the BURL method
+ Topic topic = ssn.createTopic("ACME");
+ MessageProducer prod = ssn.createProducer(topic);
+ MessageConsumer cons = ssn.createConsumer(topic);
+
+ prod.send(ssn.createTextMessage("test"));
+ assertNotNull("consumer should receive a message",cons.receive(1000));
+ cons.close();
+
+ // Using the ADDR method
+ topic = ssn.createTopic("ADDR:ACME");
+ prod = ssn.createProducer(topic);
+ cons = ssn.createConsumer(topic);
+
+ prod.send(ssn.createTextMessage("test"));
+ assertNotNull("consumer should receive a message",cons.receive(1000));
+ cons.close();
+
+ String addr = "ADDR:vehicles/bus; " +
+ "{ " +
+ "create: always, " +
+ "node: " +
+ "{" +
+ "type: topic, " +
+ "x-declare: " +
+ "{ " +
+ "type:direct, " +
+ "auto-delete: true" +
+ createExchangeArgsString(withExchangeArgs, false) +
+ "}" +
+ "}, " +
+ "link: {name : my-topic, " +
+ "x-bindings: [{exchange : 'vehicles', key : car}, " +
+ "{exchange : 'vehicles', key : van}]" +
+ "}" +
+ "}";
+
+ // Using the ADDR method to create a more complicated topic
+ topic = ssn.createTopic(addr);
+ cons = ssn.createConsumer(topic);
+ prod = ssn.createProducer(topic);
+
+ assertTrue("The queue was not bound to vehicle exchange using bus as the binding key",(
+ (AMQSession_0_10)ssn).isQueueBound("vehicles",
+ "my-topic","bus", null));
+
+ assertTrue("The queue was not bound to vehicle exchange using car as the binding key",(
+ (AMQSession_0_10)ssn).isQueueBound("vehicles",
+ "my-topic","car", null));
+
+ assertTrue("The queue was not bound to vehicle exchange using van as the binding key",(
+ (AMQSession_0_10)ssn).isQueueBound("vehicles",
+ "my-topic","van", null));
+
+ Message msg = ssn.createTextMessage("test");
+ msg.setStringProperty("qpid.subject", "van");
+ prod.send(msg);
+ assertNotNull("consumer should receive a message",cons.receive(1000));
+ cons.close();
+ }
+
+ /**
+ * Test Goal : Verify the default subjects used for each exchange type.
+ * The default for amq.topic is "#" and for the rest it's ""
+ */
+ public void testDefaultSubjects() throws Exception
+ {
+ Session ssn = _connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
+
+ MessageConsumer queueCons = ssn.createConsumer(new AMQAnyDestination("ADDR:amq.direct"));
+ MessageConsumer topicCons = ssn.createConsumer(new AMQAnyDestination("ADDR:amq.topic"));
+
+ MessageProducer queueProducer = ssn.createProducer(new AMQAnyDestination("ADDR:amq.direct"));
+ MessageProducer topicProducer1 = ssn.createProducer(new AMQAnyDestination("ADDR:amq.topic/usa.weather"));
+ MessageProducer topicProducer2 = ssn.createProducer(new AMQAnyDestination("ADDR:amq.topic/sales"));
+
+ queueProducer.send(ssn.createBytesMessage());
+ assertNotNull("The consumer subscribed to amq.direct " +
+ "with empty binding key should have received the message ",queueCons.receive(1000));
+
+ topicProducer1.send(ssn.createTextMessage("25c"));
+ assertEquals("The consumer subscribed to amq.topic " +
+ "with '#' binding key should have received the message ",
+ ((TextMessage)topicCons.receive(1000)).getText(),"25c");
+
+ topicProducer2.send(ssn.createTextMessage("1000"));
+ assertEquals("The consumer subscribed to amq.topic " +
+ "with '#' binding key should have received the message ",
+ ((TextMessage)topicCons.receive(1000)).getText(),"1000");
+ }
+
+ /**
+ * Test Goal : Verify that 'mode : browse' works as expected using a regular consumer.
+ * This indirectly tests ring queues as well.
+ */
+ public void testBrowseMode() throws Exception
+ {
+
+ Session ssn = _connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
+
+ String addr = "ADDR:my-ring-queue; {create: always, mode: browse, " +
+ "node: {x-bindings: [{exchange : 'amq.direct', key : test}], " +
+ "x-declare:{arguments : {'qpid.policy_type':ring, 'qpid.max_count':2}}}}";
+
+ Destination dest = ssn.createQueue(addr);
+ MessageConsumer browseCons = ssn.createConsumer(dest);
+ MessageProducer prod = ssn.createProducer(ssn.createQueue("ADDR:amq.direct/test"));
+
+ prod.send(ssn.createTextMessage("Test1"));
+ prod.send(ssn.createTextMessage("Test2"));
+
+ TextMessage msg = (TextMessage)browseCons.receive(1000);
+ assertEquals("Didn't receive the first message",msg.getText(),"Test1");
+
+ msg = (TextMessage)browseCons.receive(1000);
+ assertEquals("Didn't receive the first message",msg.getText(),"Test2");
+
+ browseCons.close();
+ prod.send(ssn.createTextMessage("Test3"));
+ browseCons = ssn.createConsumer(dest);
+
+ msg = (TextMessage)browseCons.receive(1000);
+ assertEquals("Should receive the second message again",msg.getText(),"Test2");
+
+ msg = (TextMessage)browseCons.receive(1000);
+ assertEquals("Should receive the third message since it's a ring queue",msg.getText(),"Test3");
+
+ assertNull("Should not receive anymore messages",browseCons.receive(500));
+ }
+
+ /**
+ * Test Goal : When the same destination is used when creating two consumers,
+ * If the type == topic, verify that unique subscription queues are created,
+ * unless subscription queue has a name.
+ *
+ * If the type == queue, same queue should be shared.
+ */
+ public void testSubscriptionForSameDestination() throws Exception
+ {
+ Session ssn = _connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
+ Destination dest = ssn.createTopic("ADDR:amq.topic/foo");
+ MessageConsumer consumer1 = ssn.createConsumer(dest);
+ MessageConsumer consumer2 = ssn.createConsumer(dest);
+ MessageProducer prod = ssn.createProducer(dest);
+
+ prod.send(ssn.createTextMessage("A"));
+ TextMessage m = (TextMessage)consumer1.receive(1000);
+ assertEquals("Consumer1 should recieve message A",m.getText(),"A");
+ m = (TextMessage)consumer2.receive(1000);
+ assertEquals("Consumer2 should recieve message A",m.getText(),"A");
+
+ consumer1.close();
+ consumer2.close();
+
+ dest = ssn.createTopic("ADDR:amq.topic/foo; { link: {name: my-queue}}");
+ consumer1 = ssn.createConsumer(dest);
+ try
+ {
+ consumer2 = ssn.createConsumer(dest);
+ fail("An exception should be thrown as 'my-queue' already have an exclusive subscriber");
+ }
+ catch(Exception e)
+ {
+ }
+ _connection.close();
+
+ _connection = getConnection() ;
+ _connection.start();
+ ssn = _connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
+ dest = ssn.createTopic("ADDR:my_queue; {create: always}");
+ consumer1 = ssn.createConsumer(dest);
+ consumer2 = ssn.createConsumer(dest);
+ prod = ssn.createProducer(dest);
+
+ prod.send(ssn.createTextMessage("A"));
+ Message m1 = consumer1.receive(1000);
+ Message m2 = consumer2.receive(1000);
+
+ if (m1 != null)
+ {
+ assertNull("Only one consumer should receive the message",m2);
+ }
+ else
+ {
+ assertNotNull("Only one consumer should receive the message",m2);
+ }
+ }
+
+ public void testXBindingsWithoutExchangeName() throws Exception
+ {
+ Session ssn = _connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
+ String addr = "ADDR:MRKT; " +
+ "{" +
+ "create: receiver," +
+ "node : {type: topic, x-declare: {type: topic} }," +
+ "link:{" +
+ "name: my-topic," +
+ "x-bindings:[{key:'NYSE.#'},{key:'NASDAQ.#'},{key:'CNTL.#'}]" +
+ "}" +
+ "}";
+
+ // Using the ADDR method to create a more complicated topic
+ Topic topic = ssn.createTopic(addr);
+ MessageConsumer cons = ssn.createConsumer(topic);
+
+ assertTrue("The queue was not bound to MRKT exchange using NYSE.# as the binding key",(
+ (AMQSession_0_10)ssn).isQueueBound("MRKT",
+ "my-topic","NYSE.#", null));
+
+ assertTrue("The queue was not bound to MRKT exchange using NASDAQ.# as the binding key",(
+ (AMQSession_0_10)ssn).isQueueBound("MRKT",
+ "my-topic","NASDAQ.#", null));
+
+ assertTrue("The queue was not bound to MRKT exchange using CNTL.# as the binding key",(
+ (AMQSession_0_10)ssn).isQueueBound("MRKT",
+ "my-topic","CNTL.#", null));
+
+ MessageProducer prod = ssn.createProducer(topic);
+ Message msg = ssn.createTextMessage("test");
+ msg.setStringProperty("qpid.subject", "NASDAQ.ABCD");
+ prod.send(msg);
+ assertNotNull("consumer should receive a message",cons.receive(1000));
+ cons.close();
+ }
+
+ public void testXSubscribeOverrides() throws Exception
+ {
+ Session ssn = _connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
+ String str = "ADDR:my_queue; {create:always,link: {x-subscribes:{exclusive: true, arguments: {a:b,x:y}}}}";
+ Destination dest = ssn.createTopic(str);
+ MessageConsumer consumer1 = ssn.createConsumer(dest);
+ try
+ {
+ MessageConsumer consumer2 = ssn.createConsumer(dest);
+ fail("An exception should be thrown as 'my-queue' already have an exclusive subscriber");
+ }
+ catch(Exception e)
+ {
+ }
+ }
+
+ public void testQueueReceiversAndTopicSubscriber() throws Exception
+ {
+ Queue queue = new AMQAnyDestination("ADDR:my-queue; {create: always}");
+ Topic topic = new AMQAnyDestination("ADDR:amq.topic/test");
+
+ QueueSession qSession = ((AMQConnection)_connection).createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
+ QueueReceiver receiver = qSession.createReceiver(queue);
+
+ TopicSession tSession = ((AMQConnection)_connection).createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
+ TopicSubscriber sub = tSession.createSubscriber(topic);
+
+ Session ssn = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ MessageProducer prod1 = ssn.createProducer(ssn.createQueue("ADDR:my-queue"));
+ prod1.send(ssn.createTextMessage("test1"));
+
+ MessageProducer prod2 = ssn.createProducer(ssn.createTopic("ADDR:amq.topic/test"));
+ prod2.send(ssn.createTextMessage("test2"));
+
+ Message msg1 = receiver.receive();
+ assertNotNull(msg1);
+ assertEquals("test1",((TextMessage)msg1).getText());
+
+ Message msg2 = sub.receive();
+ assertNotNull(msg2);
+ assertEquals("test2",((TextMessage)msg2).getText());
+ }
+
+ public void testDurableSubscriber() throws Exception
+ {
+ Session ssn = _connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
+
+ String bindingStr = "x-bindings:[{key:'NYSE.#'},{key:'NASDAQ.#'},{key:'CNTL.#'}]}}";
+
+ Properties props = new Properties();
+ props.setProperty("java.naming.factory.initial", "org.apache.qpid.jndi.PropertiesFileInitialContextFactory");
+ props.setProperty("destination.address1", "ADDR:amq.topic/test");
+ props.setProperty("destination.address2", "ADDR:amq.topic/test; {node:{" + bindingStr);
+ props.setProperty("destination.address3", "ADDR:amq.topic/test; {link:{" + bindingStr);
+ String addrStr = "ADDR:my_queue; {create:always,link: {x-subscribes:{exclusive: true, arguments: {a:b,x:y}}}}";
+ props.setProperty("destination.address5", addrStr);
+
+ Context ctx = new InitialContext(props);
+
+ for (int i=1; i < 4; i++)
+ {
+ Topic topic = (Topic) ctx.lookup("address"+i);
+ createDurableSubscriber(ctx,ssn,"address"+i,topic,"ADDR:amq.topic/test");
+ }
+
+ Topic topic = ssn.createTopic("ADDR:news.us");
+ createDurableSubscriber(ctx,ssn,"my-dest",topic,"ADDR:news.us");
+
+ Topic namedQueue = (Topic) ctx.lookup("address5");
+ try
+ {
+ createDurableSubscriber(ctx,ssn,"my-queue",namedQueue,"ADDR:amq.topic/test");
+ fail("Exception should be thrown. Durable subscribers cannot be created for Queues");
+ }
+ catch(JMSException e)
+ {
+ assertEquals("Durable subscribers can only be created for Topics",
+ e.getMessage());
+ }
+ }
+
+ public void testDurableSubscription() throws Exception
+ {
+ Session session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ Topic topic = session.createTopic("ADDR:amq.topic/" + getTestQueueName());
+ MessageProducer publisher = session.createProducer(topic);
+ MessageConsumer subscriber = session.createDurableSubscriber(topic, getTestQueueName());
+
+ TextMessage messageToSend = session.createTextMessage("Test0");
+ publisher.send(messageToSend);
+ ((AMQSession<?,?>)session).sync();
+
+ Message receivedMessage = subscriber.receive(1000);
+ assertNotNull("Message has not been received", receivedMessage);
+ assertEquals("Unexpected message", messageToSend.getText(), ((TextMessage)receivedMessage).getText());
+
+ subscriber.close();
+
+ messageToSend = session.createTextMessage("Test1");
+ publisher.send(messageToSend);
+ ((AMQSession<?,?>)session).sync();
+
+ subscriber = session.createDurableSubscriber(topic, getTestQueueName());
+ receivedMessage = subscriber.receive(1000);
+ assertNotNull("Message has not been received", receivedMessage);
+ assertEquals("Unexpected message", messageToSend.getText(), ((TextMessage)receivedMessage).getText());
+ }
+
+ public void testDurableSubscriptionnWithSelector() throws Exception
+ {
+ Session session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ Topic topic = session.createTopic("ADDR:amq.topic/" + getTestQueueName());
+ MessageProducer publisher = session.createProducer(topic);
+ MessageConsumer subscriber = session.createDurableSubscriber(topic, getTestQueueName(), "id=1", false);
+
+ TextMessage messageToSend = session.createTextMessage("Test0");
+ messageToSend.setIntProperty("id", 1);
+ publisher.send(messageToSend);
+ ((AMQSession<?,?>)session).sync();
+
+ Message receivedMessage = subscriber.receive(1000);
+ assertNotNull("Message has not been received", receivedMessage);
+ assertEquals("Unexpected message", messageToSend.getText(), ((TextMessage)receivedMessage).getText());
+ assertEquals("Unexpected id", 1, receivedMessage.getIntProperty("id"));
+
+ subscriber.close();
+
+ messageToSend = session.createTextMessage("Test1");
+ messageToSend.setIntProperty("id", 1);
+ publisher.send(messageToSend);
+ ((AMQSession<?,?>)session).sync();
+
+ subscriber = session.createDurableSubscriber(topic, getTestQueueName(), "id=1", false);
+ receivedMessage = subscriber.receive(1000);
+ assertNotNull("Message has not been received", receivedMessage);
+ assertEquals("Unexpected message", messageToSend.getText(), ((TextMessage)receivedMessage).getText());
+ assertEquals("Unexpected id", 1, receivedMessage.getIntProperty("id"));
+ }
+
+ private void createDurableSubscriber(Context ctx,Session ssn,String destName,Topic topic, String producerAddr) throws Exception
+ {
+ MessageConsumer cons = ssn.createDurableSubscriber(topic, destName);
+ MessageProducer prod = ssn.createProducer(ssn.createTopic(producerAddr));
+
+ Message m = ssn.createTextMessage(destName);
+ prod.send(m);
+ Message msg = cons.receive(1000);
+ assertNotNull("Message not received as expected when using Topic : " + topic,msg);
+ assertEquals(destName,((TextMessage)msg).getText());
+ ssn.unsubscribe(destName);
+ }
+
+ public void testDeleteOptions() throws Exception
+ {
+ Session jmsSession = _connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
+ MessageConsumer cons;
+
+ // default (create never, assert never) -------------------
+ // create never --------------------------------------------
+ String addr1 = "ADDR:testQueue1;{create: always, delete: always}";
+ AMQDestination dest = new AMQAnyDestination(addr1);
+ try
+ {
+ cons = jmsSession.createConsumer(dest);
+ cons.close();
+ }
+ catch(JMSException e)
+ {
+ fail("Exception should not be thrown. Exception thrown is : " + e);
+ }
+
+ assertFalse("Queue not deleted as expected",(
+ (AMQSession_0_10)jmsSession).isQueueExist(dest, false));
+
+
+ String addr2 = "ADDR:testQueue2;{create: always, delete: receiver}";
+ dest = new AMQAnyDestination(addr2);
+ try
+ {
+ cons = jmsSession.createConsumer(dest);
+ cons.close();
+ }
+ catch(JMSException e)
+ {
+ fail("Exception should not be thrown. Exception thrown is : " + e);
+ }
+
+ assertFalse("Queue not deleted as expected",(
+ (AMQSession_0_10)jmsSession).isQueueExist(dest, false));
+
+
+ String addr3 = "ADDR:testQueue3;{create: always, delete: sender}";
+ dest = new AMQAnyDestination(addr3);
+ try
+ {
+ cons = jmsSession.createConsumer(dest);
+ MessageProducer prod = jmsSession.createProducer(dest);
+ prod.close();
+ }
+ catch(JMSException e)
+ {
+ fail("Exception should not be thrown. Exception thrown is : " + e);
+ }
+
+ assertFalse("Queue not deleted as expected",(
+ (AMQSession_0_10)jmsSession).isQueueExist(dest, false));
+ }
+
+ /**
+ * Test Goals : 1. Test if the client sets the correct accept mode for unreliable
+ * and at-least-once.
+ * 2. Test default reliability modes for Queues and Topics.
+ * 3. Test if an exception is thrown if exactly-once is used.
+ * 4. Test if an exception is thrown if at-least-once is used with topics.
+ *
+ * Test Strategy: For goal #1 & #2
+ * For unreliable and at-least-once the test tries to receives messages
+ * in client_ack mode but does not ack the messages.
+ * It will then close the session, recreate a new session
+ * and will then try to verify the queue depth.
+ * For unreliable the messages should have been taken off the queue.
+ * For at-least-once the messages should be put back onto the queue.
+ *
+ */
+
+ public void testReliabilityOptions() throws Exception
+ {
+ String addr1 = "ADDR:testQueue1;{create: always, delete : receiver, link : {reliability : unreliable}}";
+ acceptModeTest(addr1,0);
+
+ String addr2 = "ADDR:testQueue2;{create: always, delete : receiver, link : {reliability : at-least-once}}";
+ acceptModeTest(addr2,2);
+
+ // Default accept-mode for topics
+ acceptModeTest("ADDR:amq.topic/test",0);
+
+ // Default accept-mode for queues
+ acceptModeTest("ADDR:testQueue1;{create: always}",2);
+
+ String addr3 = "ADDR:testQueue2;{create: always, delete : receiver, link : {reliability : exactly-once}}";
+ try
+ {
+ AMQAnyDestination dest = new AMQAnyDestination(addr3);
+ fail("An exception should be thrown indicating it's an unsupported type");
+ }
+ catch(Exception e)
+ {
+ assertTrue(e.getCause().getMessage().contains("The reliability mode 'exactly-once' is not yet supported"));
+ }
+ }
+
+ private void acceptModeTest(String address, int expectedQueueDepth) throws Exception
+ {
+ Session ssn = _connection.createSession(false,Session.CLIENT_ACKNOWLEDGE);
+ MessageConsumer cons;
+ MessageProducer prod;
+
+ AMQDestination dest = new AMQAnyDestination(address);
+ cons = ssn.createConsumer(dest);
+ prod = ssn.createProducer(dest);
+
+ for (int i=0; i < expectedQueueDepth; i++)
+ {
+ prod.send(ssn.createTextMessage("Msg" + i));
+ }
+
+ for (int i=0; i < expectedQueueDepth; i++)
+ {
+ Message msg = cons.receive(1000);
+ assertNotNull(msg);
+ assertEquals("Msg" + i,((TextMessage)msg).getText());
+ }
+
+ ssn.close();
+ ssn = _connection.createSession(false,Session.CLIENT_ACKNOWLEDGE);
+ long queueDepth = ((AMQSession) ssn).getQueueDepth(dest);
+ assertEquals(expectedQueueDepth,queueDepth);
+ cons.close();
+ prod.close();
+ }
+
+ public void testDestinationOnSend() throws Exception
+ {
+ Session ssn = _connection.createSession(false,Session.CLIENT_ACKNOWLEDGE);
+ MessageConsumer cons = ssn.createConsumer(ssn.createTopic("ADDR:amq.topic/test"));
+ MessageProducer prod = ssn.createProducer(null);
+
+ Topic queue = ssn.createTopic("ADDR:amq.topic/test");
+ prod.send(queue,ssn.createTextMessage("A"));
+
+ Message msg = cons.receive(1000);
+ assertNotNull(msg);
+ assertEquals("A",((TextMessage)msg).getText());
+ prod.close();
+ cons.close();
+ }
+
+ public void testReplyToWithNamelessExchange() throws Exception
+ {
+ System.setProperty("qpid.declare_exchanges","false");
+ replyToTest("ADDR:my-queue;{create: always}");
+ System.setProperty("qpid.declare_exchanges","true");
+ }
+
+ public void testReplyToWithCustomExchange() throws Exception
+ {
+ replyToTest("ADDR:hello;{create:always,node:{type:topic}}");
+ }
+
+ private void replyToTest(String replyTo) throws Exception
+ {
+ Session session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ Destination replyToDest = AMQDestination.createDestination(replyTo);
+ MessageConsumer replyToCons = session.createConsumer(replyToDest);
+
+ Destination dest = session.createQueue("ADDR:amq.direct/test");
+
+ MessageConsumer cons = session.createConsumer(dest);
+ MessageProducer prod = session.createProducer(dest);
+ Message m = session.createTextMessage("test");
+ m.setJMSReplyTo(replyToDest);
+ prod.send(m);
+
+ Message msg = cons.receive();
+ MessageProducer prodR = session.createProducer(msg.getJMSReplyTo());
+ prodR.send(session.createTextMessage("x"));
+
+ Message m1 = replyToCons.receive();
+ assertNotNull("The reply to consumer should have received the messsage",m1);
+ }
+
+ public void testAltExchangeInAddressString() throws Exception
+ {
+ String addr1 = "ADDR:my-exchange/test; {create: always, node:{type: topic,x-declare:{alternate-exchange:'amq.fanout'}}}";
+ Session session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ String altQueueAddr = "ADDR:my-alt-queue;{create: always, delete: receiver,node:{x-bindings:[{exchange:'amq.fanout'}] }}";
+ MessageConsumer cons = session.createConsumer(session.createQueue(altQueueAddr));
+
+ MessageProducer prod = session.createProducer(session.createTopic(addr1));
+ prod.send(session.createMessage());
+ prod.close();
+ assertNotNull("The consumer on the queue bound to the alt-exchange should receive the message",cons.receive(1000));
+
+ String addr2 = "ADDR:test-queue;{create:sender, delete: sender,node:{type:queue,x-declare:{alternate-exchange:'amq.fanout'}}}";
+ prod = session.createProducer(session.createTopic(addr2));
+ prod.send(session.createMessage());
+ prod.close();
+ assertNotNull("The consumer on the queue bound to the alt-exchange should receive the message",cons.receive(1000));
+ cons.close();
+ }
+
+ public void testUnknownAltExchange() throws Exception
+ {
+ Session session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ String altQueueAddr = "ADDR:my-alt-queue;{create: always, delete: receiver,node:{x-bindings:[{exchange:'doesnotexist'}] }}";
+ try
+ {
+ session.createConsumer(session.createQueue(altQueueAddr));
+ fail("Attempt to create a queue with an unknown alternate exchange should fail");
+ }
+ catch(JMSException e)
+ {
+ assertEquals("Failure code is not as expected", "404", e.getErrorCode());
+ }
+ }
+
+ public void testUnknownExchangeType() throws Exception
+ {
+ createExchangeImpl(false, false, true);
+ }
+
+ public void testQueueBrowserWithSelectorAutoAcknowledgement() throws Exception
+ {
+ assertQueueBrowserWithSelector(Session.AUTO_ACKNOWLEDGE);
+ }
+
+ public void testQueueBrowserWithSelectorClientAcknowldgement() throws Exception
+ {
+ assertQueueBrowserWithSelector(Session.CLIENT_ACKNOWLEDGE);
+ }
+
+ public void testQueueBrowserWithSelectorTransactedSession() throws Exception
+ {
+ assertQueueBrowserWithSelector(Session.SESSION_TRANSACTED);
+ }
+
+ public void testConsumerWithSelectorAutoAcknowledgement() throws Exception
+ {
+ assertConsumerWithSelector(Session.AUTO_ACKNOWLEDGE);
+ }
+
+ public void testConsumerWithSelectorClientAcknowldgement() throws Exception
+ {
+ assertConsumerWithSelector(Session.CLIENT_ACKNOWLEDGE);
+ }
+
+ public void testConsumerWithSelectorTransactedSession() throws Exception
+ {
+ assertConsumerWithSelector(Session.SESSION_TRANSACTED);
+ }
+
+ private void assertQueueBrowserWithSelector(int acknowledgement) throws Exception
+ {
+ String queueAddress = "ADDR:" + getTestQueueName() + ";{create: always}";
+
+ boolean transacted = acknowledgement == Session.SESSION_TRANSACTED;
+ Session session = _connection.createSession(transacted, acknowledgement);
+
+ Queue queue = session.createQueue(queueAddress);
+
+ final int numberOfMessages = 10;
+ List<Message> sentMessages = sendMessage(session, queue, numberOfMessages);
+ assertNotNull("Messages were not sent", sentMessages);
+ assertEquals("Unexpected number of messages were sent", numberOfMessages, sentMessages.size());
+
+ QueueBrowser browser = session.createBrowser(queue, INDEX + "%2=0");
+ _connection.start();
+
+ Enumeration<Message> enumaration = browser.getEnumeration();
+
+ int counter = 0;
+ int expectedIndex = 0;
+ while (enumaration.hasMoreElements())
+ {
+ Message m = enumaration.nextElement();
+ assertNotNull("Expected not null message at step " + counter, m);
+ int messageIndex = m.getIntProperty(INDEX);
+ assertEquals("Unexpected index", expectedIndex, messageIndex);
+ expectedIndex += 2;
+ counter++;
+ }
+ assertEquals("Unexpected number of messsages received", 5, counter);
+ }
+
+ private void assertConsumerWithSelector(int acknowledgement) throws Exception
+ {
+ String queueAddress = "ADDR:" + getTestQueueName() + ";{create: always}";
+
+ boolean transacted = acknowledgement == Session.SESSION_TRANSACTED;
+ Session session = _connection.createSession(transacted, acknowledgement);
+
+ Queue queue = session.createQueue(queueAddress);
+
+ final int numberOfMessages = 10;
+ List<Message> sentMessages = sendMessage(session, queue, numberOfMessages);
+ assertNotNull("Messages were not sent", sentMessages);
+ assertEquals("Unexpected number of messages were sent", numberOfMessages, sentMessages.size());
+
+ MessageConsumer consumer = session.createConsumer(queue, INDEX + "%2=0");
+
+ int expectedIndex = 0;
+ for (int i = 0; i < 5; i++)
+ {
+ Message m = consumer.receive(RECEIVE_TIMEOUT);
+ assertNotNull("Expected not null message at step " + i, m);
+ int messageIndex = m.getIntProperty(INDEX);
+ assertEquals("Unexpected index", expectedIndex, messageIndex);
+ expectedIndex += 2;
+
+ if (transacted)
+ {
+ session.commit();
+ }
+ else if (acknowledgement == Session.CLIENT_ACKNOWLEDGE)
+ {
+ m.acknowledge();
+ }
+ }
+
+ Message m = consumer.receive(RECEIVE_TIMEOUT);
+ assertNull("Unexpected message received", m);
+ }
+
+ /**
+ * Tests that a client using a session in {@link Session#CLIENT_ACKNOWLEDGE} can correctly
+ * recover a session and re-receive the same message.
+ */
+ public void testTopicRereceiveAfterRecover() throws Exception
+ {
+ final Session jmsSession = _connection.createSession(false,Session.CLIENT_ACKNOWLEDGE);
+ final Destination topic = jmsSession.createTopic("ADDR:amq.topic/topic1; {link:{name: queue1}}");
+
+ final MessageProducer prod = jmsSession.createProducer(topic);
+ final MessageConsumer consForTopic1 = jmsSession.createConsumer(topic);
+ final Message sentMessage = jmsSession.createTextMessage("Hello");
+
+ prod.send(sentMessage);
+ Message receivedMessage = consForTopic1.receive(1000);
+ assertNotNull("message should be received by consumer", receivedMessage);
+
+ jmsSession.recover();
+ receivedMessage = consForTopic1.receive(1000);
+ assertNotNull("message should be re-received by consumer after recover", receivedMessage);
+ receivedMessage.acknowledge();
+ }
+
+ /**
+ * Tests that a client using a session in {@link Session#SESSION_TRANSACTED} can correctly
+ * rollback a session and re-receive the same message.
+ */
+ public void testTopicRereceiveAfterRollback() throws Exception
+ {
+ final Session jmsSession = _connection.createSession(true,Session.SESSION_TRANSACTED);
+ final Destination topic = jmsSession.createTopic("ADDR:amq.topic/topic1; {link:{name: queue1}}");
+
+ final MessageProducer prod = jmsSession.createProducer(topic);
+ final MessageConsumer consForTopic1 = jmsSession.createConsumer(topic);
+ final Message sentMessage = jmsSession.createTextMessage("Hello");
+
+ prod.send(sentMessage);
+ jmsSession.commit();
+
+ Message receivedMessage = consForTopic1.receive(1000);
+ assertNotNull("message should be received by consumer", receivedMessage);
+
+ jmsSession.rollback();
+ receivedMessage = consForTopic1.receive(1000);
+ assertNotNull("message should be re-received by consumer after rollback", receivedMessage);
+ jmsSession.commit();
+ }
+
+ /**
+ * Test Goals :
+ *
+ * 1. Verify that link bindings are created and destroyed after creating and closing a subscriber.
+ * 2. Verify that link bindings are created and destroyed after creating and closing a subscriber.
+ */
+ public void testLinkBindingBehavior() throws Exception
+ {
+ Session jmsSession = _connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
+ String addr = "ADDR:my-queue; {create: always, " +
+ "link: " +
+ "{" +
+ "x-bindings: [{exchange : 'amq.direct', key : test}]," +
+ "}" +
+ "}";
+
+ AMQDestination dest = (AMQDestination)jmsSession.createQueue(addr);
+ MessageConsumer cons = jmsSession.createConsumer(dest);
+ AMQSession_0_10 ssn = (AMQSession_0_10)jmsSession;
+
+ assertTrue("Queue not created as expected",ssn.isQueueExist(dest, true));
+ assertTrue("Queue not bound as expected",ssn.isQueueBound("amq.direct","my-queue","test", null));
+
+ cons.close(); // closing consumer, link binding should be removed now.
+ assertTrue("Queue should still be there",ssn.isQueueExist(dest, true));
+ assertFalse("Binding should not exist anymore",ssn.isQueueBound("amq.direct","my-queue","test", null));
+
+ MessageProducer prod = jmsSession.createProducer(dest);
+ assertTrue("Queue not bound as expected",ssn.isQueueBound("amq.direct","my-queue","test", null));
+ prod.close();
+ assertFalse("Binding should not exist anymore",ssn.isQueueBound("amq.direct","my-queue","test", null));
+ }
+
+ /**
+ * Test Goals : Verifies that the subscription queue created is as specified under link properties.
+ */
+ public void testCustomizingSubscriptionQueue() throws Exception
+ {
+ Session ssn = _connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
+ String xDeclareArgs = "x-declare: { exclusive: false, auto-delete: false," +
+ "alternate-exchange: 'amq.fanout'," +
+ "arguments: {'qpid.alert_size': 1000,'qpid.alert_count': 100}" +
+ "}";
+
+ String addr = "ADDR:amq.topic/test; {link: {name:my-queue, durable:true," + xDeclareArgs + "}}";
+ Destination dest = ssn.createTopic(addr);
+ MessageConsumer cons = ssn.createConsumer(dest);
+
+ String verifyAddr = "ADDR:my-queue;{ node: {durable:true, " + xDeclareArgs + "}}";
+ AMQDestination verifyDest = (AMQDestination)ssn.createQueue(verifyAddr);
+ ((AMQSession_0_10)ssn).isQueueExist(verifyDest, true);
+
+ // Verify that the producer does not delete the subscription queue.
+ MessageProducer prod = ssn.createProducer(dest);
+ prod.close();
+ ((AMQSession_0_10)ssn).isQueueExist(verifyDest, true);
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/client/failover/FailoverTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/client/failover/FailoverTest.java
new file mode 100644
index 0000000000..2875e2c6b1
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/client/failover/FailoverTest.java
@@ -0,0 +1,349 @@
+/*
+ *
+ * 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.qpid.test.client.failover;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.jms.ConnectionListener;
+import org.apache.qpid.test.utils.FailoverBaseCase;
+
+import javax.jms.Connection;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import java.util.Random;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class FailoverTest extends FailoverBaseCase implements ConnectionListener
+{
+ private static final Logger _logger = Logger.getLogger(FailoverTest.class);
+
+ private static final int DEFAULT_NUM_MESSAGES = 10;
+ private static final int DEFAULT_SEED = 20080921;
+ protected int numMessages = 0;
+ protected Connection connection;
+ private Session producerSession;
+ private Queue queue;
+ private MessageProducer producer;
+ private Session consumerSession;
+ private MessageConsumer consumer;
+
+ private CountDownLatch failoverComplete;
+ private boolean CLUSTERED = Boolean.getBoolean("profile.clustered");
+ private int seed;
+ private Random rand;
+ private int _currentPort = getFailingPort();
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+
+ numMessages = Integer.getInteger("profile.failoverMsgCount",DEFAULT_NUM_MESSAGES);
+ seed = Integer.getInteger("profile.failoverRandomSeed",DEFAULT_SEED);
+ rand = new Random(seed);
+
+ connection = getConnection();
+ ((AMQConnection) connection).setConnectionListener(this);
+ connection.start();
+ failoverComplete = new CountDownLatch(1);
+ }
+
+ private void init(boolean transacted, int mode) throws Exception
+ {
+ consumerSession = connection.createSession(transacted, mode);
+ queue = consumerSession.createQueue(getName()+System.currentTimeMillis());
+ consumer = consumerSession.createConsumer(queue);
+
+ producerSession = connection.createSession(transacted, mode);
+ producer = producerSession.createProducer(queue);
+ }
+
+ @Override
+ public void tearDown() throws Exception
+ {
+ try
+ {
+ connection.close();
+ }
+ catch (Exception e)
+ {
+
+ }
+
+ super.tearDown();
+ }
+
+ private void consumeMessages(int startIndex,int endIndex, boolean transacted) throws JMSException
+ {
+ Message msg;
+ _logger.debug("**************** Receive (Start: " + startIndex + ", End:" + endIndex + ")***********************");
+
+ for (int i = startIndex; i < endIndex; i++)
+ {
+ msg = consumer.receive(1000);
+ assertNotNull("Message " + i + " was null!", msg);
+
+ _logger.debug("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
+ _logger.debug("Received : " + ((TextMessage) msg).getText());
+ _logger.debug("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
+
+ assertEquals("Invalid message order","message " + i, ((TextMessage) msg).getText());
+
+ }
+ _logger.debug("***********************************************************");
+
+ if (transacted)
+ {
+ consumerSession.commit();
+ }
+ }
+
+ private void sendMessages(int startIndex,int endIndex, boolean transacted) throws Exception
+ {
+ _logger.debug("**************** Send (Start: " + startIndex + ", End:" + endIndex + ")***********************");
+
+ for (int i = startIndex; i < endIndex; i++)
+ {
+ producer.send(producerSession.createTextMessage("message " + i));
+
+ _logger.debug("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
+ _logger.debug("Sending message"+i);
+ _logger.debug("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
+ }
+
+ _logger.debug("***********************************************************");
+
+ if (transacted)
+ {
+ producerSession.commit();
+ }
+ else
+ {
+ ((AMQSession<?, ?>)producerSession).sync();
+ }
+ }
+
+ public void testP2PFailover() throws Exception
+ {
+ testP2PFailover(numMessages, true,true, false);
+ }
+
+ public void testP2PFailoverWithMessagesLeftToConsumeAndProduce() throws Exception
+ {
+ if (CLUSTERED)
+ {
+ testP2PFailover(numMessages, false,false, false);
+ }
+ }
+
+ public void testP2PFailoverWithMessagesLeftToConsume() throws Exception
+ {
+ if (CLUSTERED)
+ {
+ testP2PFailover(numMessages, false, true, false);
+ }
+ }
+
+ public void testP2PFailoverTransacted() throws Exception
+ {
+ testP2PFailover(numMessages, true,true, true);
+ }
+
+ public void testP2PFailoverTransactedWithMessagesLeftToConsumeAndProduce() throws Exception
+ {
+ // Currently the cluster does not support transactions that span a failover
+ if (CLUSTERED)
+ {
+ testP2PFailover(numMessages, false, false, false);
+ }
+ }
+ private void testP2PFailover(int totalMessages, boolean consumeAll, boolean produceAll , boolean transacted) throws Exception
+ {
+ init(transacted, Session.AUTO_ACKNOWLEDGE);
+ runP2PFailover(totalMessages,consumeAll, produceAll , transacted);
+ }
+
+ private void runP2PFailover(int totalMessages, boolean consumeAll, boolean produceAll , boolean transacted) throws Exception
+ {
+ int toProduce = totalMessages;
+
+ _logger.debug("===================================================================");
+ _logger.debug("Total messages used for the test " + totalMessages + " messages");
+ _logger.debug("===================================================================");
+
+ if (!produceAll)
+ {
+ toProduce = totalMessages - rand.nextInt(totalMessages);
+ }
+
+ _logger.debug("==================");
+ _logger.debug("Sending " + toProduce + " messages");
+ _logger.debug("==================");
+
+ sendMessages(0,toProduce, transacted);
+
+ // Consume some messages
+ int toConsume = toProduce;
+ if (!consumeAll)
+ {
+ toConsume = toProduce - rand.nextInt(toProduce);
+ }
+
+ consumeMessages(0,toConsume, transacted);
+
+ _logger.debug("==================");
+ _logger.debug("Consuming " + toConsume + " messages");
+ _logger.debug("==================");
+
+ _logger.info("Failing over");
+
+ causeFailure(_currentPort, DEFAULT_FAILOVER_TIME);
+
+ // Check that you produce and consume the rest of messages.
+ _logger.debug("==================");
+ _logger.debug("Sending " + (totalMessages-toProduce) + " messages");
+ _logger.debug("==================");
+
+ sendMessages(toProduce,totalMessages, transacted);
+ consumeMessages(toConsume,totalMessages, transacted);
+
+ _logger.debug("==================");
+ _logger.debug("Consuming " + (totalMessages-toConsume) + " messages");
+ _logger.debug("==================");
+ }
+
+ private void causeFailure(int port, long delay)
+ {
+
+ failBroker(port);
+
+ _logger.info("Awaiting Failover completion");
+ try
+ {
+ if (!failoverComplete.await(delay, TimeUnit.MILLISECONDS))
+ {
+ fail("failover did not complete");
+ }
+ }
+ catch (InterruptedException e)
+ {
+ //evil ignore IE.
+ }
+ }
+
+ public void testClientAckFailover() throws Exception
+ {
+ init(false, Session.CLIENT_ACKNOWLEDGE);
+ sendMessages(0,1, false);
+ Message msg = consumer.receive();
+ assertNotNull("Expected msgs not received", msg);
+
+ causeFailure(getFailingPort(), DEFAULT_FAILOVER_TIME);
+
+ Exception failure = null;
+ try
+ {
+ msg.acknowledge();
+ }
+ catch (Exception e)
+ {
+ failure = e;
+ }
+ assertNotNull("Exception should be thrown", failure);
+ }
+
+ /**
+ * The idea is to run a failover test in a loop by failing over
+ * to the other broker each time.
+ */
+ public void testFailoverInALoop() throws Exception
+ {
+ if (!CLUSTERED)
+ {
+ return;
+ }
+
+ int iterations = Integer.getInteger("profile.failoverIterations",0);
+ boolean useAltPort = false;
+ int altPort = FAILING_PORT;
+ int stdPort = DEFAULT_PORT;
+ init(false, Session.AUTO_ACKNOWLEDGE);
+ for (int i=0; i < iterations; i++)
+ {
+ _logger.debug("===================================================================");
+ _logger.debug("Failover In a loop : iteration number " + i);
+ _logger.debug("===================================================================");
+
+ runP2PFailover(numMessages, false,false, false);
+ startBroker(_currentPort);
+ if (useAltPort)
+ {
+ _currentPort = altPort;
+ useAltPort = false;
+ }
+ else
+ {
+ _currentPort = stdPort;
+ useAltPort = true;
+ }
+
+ }
+ //To prevent any failover logic being initiated when we shutdown the brokers.
+ connection.close();
+
+ // Shutdown the brokers
+ stopBroker(altPort);
+ stopBroker(stdPort);
+
+ }
+
+ public void bytesSent(long count)
+ {
+ }
+
+ public void bytesReceived(long count)
+ {
+ }
+
+ public boolean preFailover(boolean redirect)
+ {
+ return true;
+ }
+
+ public boolean preResubscribe()
+ {
+ return true;
+ }
+
+ public void failoverComplete()
+ {
+ failoverComplete.countDown();
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/client/message/JMSDestinationTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/client/message/JMSDestinationTest.java
new file mode 100644
index 0000000000..760884e654
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/client/message/JMSDestinationTest.java
@@ -0,0 +1,359 @@
+/*
+ *
+ * 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.qpid.test.client.message;
+
+import org.apache.qpid.client.AMQDestination;
+import org.apache.qpid.client.AMQTopic;
+import org.apache.qpid.client.CustomJMSXProperty;
+import org.apache.qpid.configuration.ClientProperties;
+import org.apache.qpid.management.common.mbeans.ManagedQueue;
+import org.apache.qpid.test.utils.JMXTestUtils;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+import javax.jms.Connection;
+import javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageListener;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.Session;
+import javax.jms.Topic;
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.TabularData;
+
+import java.util.Iterator;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * From the API Docs getJMSDestination:
+ *
+ * When a message is received, its JMSDestination value must be equivalent to
+ * the value assigned when it was sent.
+ */
+public class JMSDestinationTest extends QpidBrokerTestCase
+{
+
+ private Connection _connection;
+ private Session _session;
+
+ private CountDownLatch _receiveMessage;
+ private Message _message;
+
+ public void setUp() throws Exception
+ {
+ getBrokerConfiguration().addJmxManagementConfiguration();
+
+ super.setUp();
+
+ _connection = getConnection();
+
+ _session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ }
+
+ /**
+ * Test a message sent to a queue comes back with JMSDestination queue
+ *
+ * @throws Exception
+ */
+ public void testQueue() throws Exception
+ {
+
+ Queue queue = _session.createQueue(getTestQueueName());
+
+ MessageConsumer consumer = _session.createConsumer(queue);
+
+ sendMessage(_session, queue, 1);
+
+ _connection.start();
+
+ Message receivedMessage = consumer.receive(10000);
+
+ assertNotNull("Message should not be null", receivedMessage);
+
+ Destination receivedDestination = receivedMessage.getJMSDestination();
+
+ assertNotNull("JMSDestination should not be null", receivedDestination);
+
+ assertEquals("Incorrect Destination type", queue.getClass(), receivedDestination.getClass());
+ }
+
+ /**
+ * Test a message sent to a topic comes back with JMSDestination topic
+ *
+ * @throws Exception
+ */
+ public void testTopic() throws Exception
+ {
+
+ Topic topic = _session.createTopic(getTestQueueName() + "Topic");
+
+ MessageConsumer consumer = _session.createConsumer(topic);
+
+ sendMessage(_session, topic, 1);
+
+ _connection.start();
+
+ Message receivedMessage = consumer.receive(10000);
+
+ assertNotNull("Message should not be null", receivedMessage);
+
+ Destination receivedDestination = receivedMessage.getJMSDestination();
+
+ assertNotNull("JMSDestination should not be null", receivedDestination);
+ assertEquals("Incorrect Destination type", topic.getClass(), receivedDestination.getClass());
+ }
+
+ /**
+ * Test a message sent to a topic then moved on the broker
+ * comes back with JMSDestination queue.
+ *
+ * i.e. The client is not just setting the value to be the same as the
+ * current consumer destination.
+ *
+ * This test can only be run against the Java broker as it uses JMX to move
+ * messages between queues.
+ *
+ * @throws Exception
+ */
+ public void testMovedToQueue() throws Exception
+ {
+ // Setup JMXUtils
+ JMXTestUtils jmxUtils = new JMXTestUtils(this);
+
+ // Open the JMX Connection
+ jmxUtils.open();
+ try
+ {
+
+ Queue queue = _session.createQueue(getTestQueueName());
+
+ _session.createConsumer(queue).close();
+
+ sendMessage(_session, queue, 1);
+
+ Topic topic = _session.createTopic(getTestQueueName() + "Topic");
+
+ MessageConsumer consumer = _session.createConsumer(topic);
+
+ // Use Management to move message.
+
+ ManagedQueue managedQueue = jmxUtils.
+ getManagedObject(ManagedQueue.class,
+ jmxUtils.getQueueObjectName(getConnectionFactory().getVirtualPath().substring(1),
+ getTestQueueName()));
+
+ // Find the first message on the queue
+ TabularData data = managedQueue.viewMessages(1L, 2L);
+
+ Iterator values = data.values().iterator();
+ assertTrue("No Messages found via JMX", values.hasNext());
+
+ // Get its message ID
+ Long msgID = (Long) ((CompositeDataSupport) values.next()).get("AMQ MessageId");
+
+ // Start the connection and consume message that has been moved to the
+ // queue
+ _connection.start();
+
+ Message message = consumer.receive(1000);
+
+ //Validate we don't have a message on the queue before we start
+ assertNull("Message should be null", message);
+
+ // Move it to from the topic to the queue
+ managedQueue.moveMessages(msgID, msgID, ((AMQTopic) topic).getQueueName());
+
+ // Retrieve the newly moved message
+ message = consumer.receive(1000);
+
+ assertNotNull("Message should not be null", message);
+
+ Destination receivedDestination = message.getJMSDestination();
+
+ assertNotNull("JMSDestination should not be null", receivedDestination);
+
+ assertEquals("Incorrect Destination type", queue.getClass(), receivedDestination.getClass());
+
+ }
+ finally
+ {
+ jmxUtils.close();
+ }
+
+ }
+
+ /**
+ * Test a message sent to a queue comes back with JMSDestination queue
+ * when received via a message listener
+ *
+ * @throws Exception
+ */
+ public void testQueueAsync() throws Exception
+ {
+
+ Queue queue = _session.createQueue(getTestQueueName());
+
+ MessageConsumer consumer = _session.createConsumer(queue);
+
+ sendMessage(_session, queue, 1);
+
+ _connection.start();
+
+ _message = null;
+ _receiveMessage = new CountDownLatch(1);
+
+ consumer.setMessageListener(new MessageListener()
+ {
+ public void onMessage(Message message)
+ {
+ _message = message;
+ _receiveMessage.countDown();
+ }
+ });
+
+ assertTrue("Timed out waiting for message to be received ", _receiveMessage.await(1, TimeUnit.SECONDS));
+
+ assertNotNull("Message should not be null", _message);
+
+ Destination receivedDestination = _message.getJMSDestination();
+
+ assertNotNull("JMSDestination should not be null", receivedDestination);
+
+ assertEquals("Incorrect Destination type", queue.getClass(), receivedDestination.getClass());
+ }
+
+ /**
+ * Test a message received without the JMS_QPID_DESTTYPE can be resent
+ * and correctly have the property set.
+ *
+ * To do this we need to create a 0-10 connection and send a message
+ * which is then received by a 0-8/9 client.
+ *
+ * @throws Exception
+ */
+ public void testReceiveResend() throws Exception
+ {
+ // Create a 0-10 Connection and send message
+ setSystemProperty(ClientProperties.AMQP_VERSION, "0-10");
+
+ Connection connection010 = getConnection();
+
+ Session session010 = connection010.createSession(true, Session.SESSION_TRANSACTED);
+
+ // Create queue for testing
+ Queue queue = session010.createQueue(getTestQueueName());
+
+ // Ensure queue exists
+ session010.createConsumer(queue).close();
+
+ sendMessage(session010, queue, 1);
+
+ // Close the 010 connection
+ connection010.close();
+
+ // Create a 0-8 Connection and receive message
+ setSystemProperty(ClientProperties.AMQP_VERSION, "0-8");
+
+ Connection connection08 = getConnection();
+
+ Session session08 = connection08.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ MessageConsumer consumer = session08.createConsumer(queue);
+
+ connection08.start();
+
+ Message message = consumer.receive(1000);
+
+ assertNotNull("Didn't receive 0-10 message.", message);
+
+ // Validate that JMS_QPID_DESTTYPE is not set
+ try
+ {
+ message.getIntProperty(CustomJMSXProperty.JMS_QPID_DESTTYPE.toString());
+ fail("JMS_QPID_DESTTYPE should not be set, so should throw NumberFormatException");
+ }
+ catch (NumberFormatException nfe)
+ {
+
+ }
+
+ // Resend message back to queue and validate that
+ // a) getJMSDestination works without the JMS_QPID_DESTTYPE
+ // b) we can actually send without a BufferOverFlow.
+
+ MessageProducer producer = session08.createProducer(queue);
+ producer.send(message);
+
+ message = consumer.receive(1000);
+
+ assertNotNull("Didn't receive recent 0-8 message.", message);
+
+ // Validate that JMS_QPID_DESTTYPE is not set
+ assertEquals("JMS_QPID_DESTTYPE should be set to a Queue", AMQDestination.QUEUE_TYPE,
+ message.getIntProperty(CustomJMSXProperty.JMS_QPID_DESTTYPE.toString()));
+
+ }
+
+ public void testQueueWithBindingUrlUsingCustomExchange() throws Exception
+ {
+ String exchangeName = "exch_" + getTestQueueName();
+ String queueName = "queue_" + getTestQueueName();
+
+ String address = String.format("direct://%s/%s/%s?routingkey='%s'", exchangeName, queueName, queueName, queueName);
+ sendReceive(address);
+ }
+
+ public void testQueueWithBindingUrlUsingAmqDirectExchange() throws Exception
+ {
+ String queueName = getTestQueueName();
+ String address = String.format("direct://amq.direct/%s/%s?routingkey='%s'", queueName, queueName, queueName);
+ sendReceive(address);
+ }
+
+ public void testQueueWithBindingUrlUsingDefaultExchange() throws Exception
+ {
+ String queueName = getTestQueueName();
+ String address = String.format("direct:///%s/%s?routingkey='%s'", queueName, queueName, queueName);
+ sendReceive(address);
+ }
+
+ private void sendReceive(String address) throws JMSException, Exception
+ {
+ Destination dest = _session.createQueue(address);
+ MessageConsumer consumer = _session.createConsumer(dest);
+
+ _connection.start();
+
+ sendMessage(_session, dest, 1);
+
+ Message receivedMessage = consumer.receive(10000);
+
+ assertNotNull("Message should not be null", receivedMessage);
+
+ Destination receivedDestination = receivedMessage.getJMSDestination();
+
+ assertNotNull("JMSDestination should not be null", receivedDestination);
+ assertEquals("JMSDestination should match that sent", address, receivedDestination.toString());
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/client/message/JMSReplyToTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/client/message/JMSReplyToTest.java
new file mode 100644
index 0000000000..fe8180d6c6
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/client/message/JMSReplyToTest.java
@@ -0,0 +1,169 @@
+/*
+ *
+ * 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.qpid.test.client.message;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.jms.Connection;
+import javax.jms.Destination;
+import javax.jms.IllegalStateException;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageListener;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.Session;
+import javax.jms.TemporaryQueue;
+
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+/**
+ * Tests that {@link Message#setJMSReplyTo(Destination)} can be used to pass a {@link Destination} between
+ * messaging clients as is commonly used in request/response messaging pattern implementations.
+ */
+public class JMSReplyToTest extends QpidBrokerTestCase
+{
+ private AtomicReference<Throwable> _caughtException = new AtomicReference<Throwable>();
+ private Queue _requestQueue;
+ private Connection _connection;
+ private Session _session;
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+
+ _requestQueue = startAsyncRespondingJmsConsumerOnSeparateConnection();
+
+ _connection = getConnection();
+ _connection.start();
+ _session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ }
+
+ public void testRequestResponseUsingJmsReplyTo() throws Exception
+ {
+ final String responseQueueName = getTestQueueName() + ".response";
+ Queue replyToQueue = _session.createQueue(responseQueueName);
+ sendRequestAndValidateResponse(replyToQueue);
+ }
+
+ public void testRequestResponseUsingTemporaryJmsReplyTo() throws Exception
+ {
+ TemporaryQueue replyToQueue = _session.createTemporaryQueue();
+
+ sendRequestAndValidateResponse(replyToQueue);
+ }
+
+ private void sendRequestAndValidateResponse(Queue replyToQueue) throws JMSException, Exception
+ {
+ MessageConsumer replyConsumer = _session.createConsumer(replyToQueue);
+
+ Message requestMessage = createRequestMessageWithJmsReplyTo(_session, replyToQueue);
+ sendRequest(_requestQueue, _session, requestMessage);
+
+ receiveAndValidateResponse(replyConsumer, requestMessage);
+
+ assertNull("Async responder caught unexpected exception", _caughtException.get());
+ }
+
+ private Message createRequestMessageWithJmsReplyTo(Session session, Queue replyToQueue)
+ throws JMSException
+ {
+ Message requestMessage = session.createTextMessage("My request");
+ requestMessage.setJMSReplyTo(replyToQueue);
+ return requestMessage;
+ }
+
+ private void sendRequest(final Queue requestQueue, Session session, Message requestMessage) throws Exception
+ {
+ MessageProducer producer = session.createProducer(requestQueue);
+ producer.send(requestMessage);
+ }
+
+ private void receiveAndValidateResponse(MessageConsumer replyConsumer, Message requestMessage) throws JMSException
+ {
+ Message responseMessage = replyConsumer.receive(RECEIVE_TIMEOUT);
+ assertNotNull("Response message not received", responseMessage);
+ assertEquals("Correlation id of the response should match message id of the request",
+ responseMessage.getJMSCorrelationID(), requestMessage.getJMSMessageID());
+ }
+
+ private Queue startAsyncRespondingJmsConsumerOnSeparateConnection() throws Exception
+ {
+ final String requestQueueName = getTestQueueName() + ".request";
+ final Connection responderConnection = getConnection();
+ responderConnection.start();
+ final Session responderSession = responderConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ final Queue requestQueue = responderSession.createQueue(requestQueueName);
+
+ final MessageConsumer requestConsumer = responderSession.createConsumer(requestQueue);
+ requestConsumer.setMessageListener(new AsyncResponder(responderSession));
+
+ return requestQueue;
+ }
+
+ private final class AsyncResponder implements MessageListener
+ {
+ private final Session _responderSession;
+
+ private AsyncResponder(Session responderSession)
+ {
+ _responderSession = responderSession;
+ }
+
+ @Override
+ public void onMessage(Message requestMessage)
+ {
+ try
+ {
+ Destination replyTo = getReplyToQueue(requestMessage);
+
+ Message responseMessage = _responderSession.createMessage();
+ responseMessage.setJMSCorrelationID(requestMessage.getJMSMessageID());
+
+ sendResponseToQueue(replyTo, responseMessage);
+ }
+ catch (Throwable t)
+ {
+ _caughtException.set(t);
+ }
+ }
+
+ private Destination getReplyToQueue(Message requestMessage) throws JMSException, IllegalStateException
+ {
+ Destination replyTo = requestMessage.getJMSReplyTo();
+ if (replyTo == null)
+ {
+ throw new IllegalStateException("JMSReplyTo was null on message " + requestMessage);
+ }
+ return replyTo;
+ }
+
+ private void sendResponseToQueue(Destination replyTo, Message responseMessage)
+ throws JMSException
+ {
+ MessageProducer responseProducer = _responderSession.createProducer(replyTo);
+ responseProducer.send(responseMessage);
+ }
+ }
+
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/client/message/MessageToStringTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/client/message/MessageToStringTest.java
new file mode 100644
index 0000000000..dc1f690b1e
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/client/message/MessageToStringTest.java
@@ -0,0 +1,255 @@
+/*
+ *
+ * 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.qpid.test.client.message;
+
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+import javax.jms.BytesMessage;
+import javax.jms.Connection;
+import javax.jms.JMSException;
+import javax.jms.MapMessage;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.ObjectMessage;
+import javax.jms.Queue;
+import javax.jms.Session;
+import javax.jms.StreamMessage;
+import javax.jms.TextMessage;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectOutput;
+import java.io.ObjectOutputStream;
+import java.util.UUID;
+
+public class MessageToStringTest extends QpidBrokerTestCase
+{
+ private Connection _connection;
+ private Session _session;
+ private Queue _queue;
+ private MessageConsumer _consumer;
+ private static final String BYTE_TEST = "MapByteTest";
+
+ public void setUp() throws Exception
+ {
+ super.setUp();
+
+ //Create Producer put some messages on the queue
+ _connection = getConnection();
+
+ //Create Consumer
+ _session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ String queueName = getTestQueueName();
+
+ //Create Queue
+ ((AMQSession) _session).createQueue(new AMQShortString(queueName), true, false, false);
+ _queue = _session.createQueue("direct://amq.direct/"+queueName+"/"+queueName+"?durable='false'&autodelete='true'");
+
+
+ _consumer = _session.createConsumer(_queue);
+
+ _connection.start();
+ }
+
+ public void tearDown() throws Exception
+ {
+ //clean up
+ _connection.close();
+
+ super.tearDown();
+ }
+
+ public void testBytesMessage() throws JMSException
+ {
+ //Create Sample Message using UUIDs
+ UUID test = UUID.randomUUID();
+
+ BytesMessage testMessage = _session.createBytesMessage();
+
+ //Convert UUID into bytes for transit
+ byte[] testBytes = test.toString().getBytes();
+
+ testMessage.writeBytes(testBytes);
+
+ sendAndTest(testMessage, testBytes);
+ }
+
+ public void testMapMessage() throws JMSException, IOException
+ {
+ //Create Sample Message using UUIDs
+ UUID test = UUID.randomUUID();
+
+ MapMessage testMessage = _session.createMapMessage();
+
+ byte[] testBytes = convertToBytes(test);
+
+ testMessage.setBytes(BYTE_TEST, testBytes);
+
+ sendAndTest(testMessage, testBytes);
+ }
+
+ public void testObjectMessage() throws JMSException
+ {
+ MessageProducer producer = _session.createProducer(_queue);
+
+ //Create Sample Message using UUIDs
+ UUID test = UUID.randomUUID();
+
+ Message testMessage = _session.createObjectMessage(test);
+
+ sendAndTest(testMessage, test);
+ }
+
+ public void testStreamMessage() throws JMSException, IOException
+ {
+ //Create Sample Message using UUIDs
+ UUID test = UUID.randomUUID();
+
+ StreamMessage testMessage = _session.createStreamMessage();
+
+ byte[] testBytes = convertToBytes(test);
+
+ testMessage.writeBytes(testBytes);
+
+ sendAndTest(testMessage, testBytes);
+ }
+
+ public void testTextMessage() throws JMSException, IOException
+ {
+ //Create Sample Message using UUIDs
+ UUID test = UUID.randomUUID();
+
+ TextMessage testMessage = _session.createTextMessage();
+
+ String stringValue = String.valueOf(test);
+ byte[] testBytes = stringValue.getBytes();
+
+ testMessage.setText(stringValue);
+
+ sendAndTest(testMessage, testBytes);
+ }
+
+ //***************** Helpers
+
+ private void sendAndTest(Message message, Object testBytes) throws JMSException
+ {
+ MessageProducer producer = _session.createProducer(_queue);
+
+ producer.send(message);
+
+ Message receivedMessage = _consumer.receive(1000);
+
+ assertNotNull("Message was not received.", receivedMessage);
+
+ //Ensure that to calling toString doesn't error and that doing this doesn't break next tests.
+ assertNotNull("Message returned null from toString", receivedMessage.toString());
+
+ byte[] byteResults;
+ UUID result;
+
+ try
+ {
+ if (receivedMessage instanceof ObjectMessage)
+ {
+ result = (UUID) ((ObjectMessage) receivedMessage).getObject();
+ assertEquals("UUIDs were not equal", testBytes, result);
+ }
+ else
+ {
+ byteResults = getBytes(receivedMessage, ((byte[]) testBytes).length);
+ assertBytesEquals("UUIDs were not equal", (byte[]) testBytes, byteResults);
+ }
+ }
+ catch (Exception e)
+ {
+ fail(e.getMessage());
+ }
+
+ }
+
+ private void assertBytesEquals(String message, byte[] expected, byte[] actual)
+ {
+ if (expected.length == actual.length)
+ {
+ int index = 0;
+ boolean failed = false;
+ for (byte b : expected)
+ {
+ if (actual[index++] != b)
+ {
+ failed = true;
+ break;
+ }
+ }
+
+ if (!failed)
+ {
+ return;
+ }
+
+ }
+
+ fail(message);
+ }
+
+ private byte[] getBytes(Message receivedMessage, int testBytesLength) throws JMSException
+ {
+ byte[] byteResults = new byte[testBytesLength];
+
+ if (receivedMessage instanceof BytesMessage)
+ {
+ assertEquals(testBytesLength, ((BytesMessage) receivedMessage).readBytes(byteResults));
+ }
+ else if (receivedMessage instanceof StreamMessage)
+ {
+ assertEquals(testBytesLength, ((StreamMessage) receivedMessage).readBytes(byteResults));
+ }
+ else if (receivedMessage instanceof MapMessage)
+ {
+ byteResults = ((MapMessage) receivedMessage).getBytes(BYTE_TEST);
+ assertEquals(testBytesLength, byteResults.length);
+ }
+ else if (receivedMessage instanceof TextMessage)
+ {
+ byteResults = ((TextMessage) receivedMessage).getText().getBytes();
+ assertEquals(testBytesLength, byteResults.length);
+ }
+
+
+ return byteResults;
+ }
+
+ private byte[] convertToBytes(UUID test) throws IOException
+ {
+ //Convert UUID into bytes for transit
+ ObjectOutput out;
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ out = new ObjectOutputStream(bos);
+ out.writeObject(test);
+ out.close();
+
+ return bos.toByteArray();
+ }
+
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/client/message/ObjectMessageTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/client/message/ObjectMessageTest.java
new file mode 100644
index 0000000000..3bd2c4a44e
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/client/message/ObjectMessageTest.java
@@ -0,0 +1,157 @@
+/*
+ *
+ * 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.qpid.test.client.message;
+
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+import javax.jms.Connection;
+import javax.jms.JMSException;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.ObjectMessage;
+import javax.jms.Queue;
+import javax.jms.Session;
+import java.util.UUID;
+
+public class ObjectMessageTest extends QpidBrokerTestCase
+{
+ private Connection _connection;
+ private Session _session;
+ private MessageConsumer _consumer;
+ private MessageProducer _producer;
+
+ public void setUp() throws Exception
+ {
+ super.setUp();
+
+ //Create Connection
+ _connection = getConnection();
+
+
+ //Create Session
+ _session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ //Create Queue
+ String queueName = getTestQueueName();
+ ((AMQSession) _session).createQueue(new AMQShortString(queueName), true, false, false);
+ Queue queue = _session.createQueue("direct://amq.direct/"+queueName+"/"+queueName+"?durable='false'&autodelete='true'");
+
+ //Create Consumer
+ _consumer = _session.createConsumer(queue);
+
+ //Create Producer
+ _producer = _session.createProducer(queue);
+
+ _connection.start();
+ }
+
+ public void tearDown() throws Exception
+ {
+ //clean up
+ _connection.close();
+
+ super.tearDown();
+ }
+
+ public void testGetAndSend() throws JMSException
+ {
+ //Create Sample Message using UUIDs
+ UUID test = UUID.randomUUID();
+
+ ObjectMessage testMessage = _session.createObjectMessage(test);
+
+ Object o = testMessage.getObject();
+
+ assertNotNull("Object was null", o);
+
+ sendAndTest(testMessage, test);
+ }
+
+ public void testSend() throws JMSException
+ {
+ //Create Sample Message using UUIDs
+ UUID test = UUID.randomUUID();
+
+ ObjectMessage testMessage = _session.createObjectMessage(test);
+
+ sendAndTest(testMessage, test);
+ }
+
+ public void testTostringAndSend() throws JMSException
+ {
+ //Create Sample Message using UUIDs
+ UUID test = UUID.randomUUID();
+
+ ObjectMessage testMessage = _session.createObjectMessage(test);
+
+ assertNotNull("Object was null", testMessage.toString());
+
+ sendAndTest(testMessage, test);
+ }
+
+ public void testSendNull() throws JMSException
+ {
+
+ ObjectMessage testMessage = _session.createObjectMessage(null);
+
+ assertNotNull("Object was null", testMessage.toString());
+
+ sendAndTest(testMessage, null);
+ }
+
+ //***************** Helpers
+
+ private void sendAndTest(ObjectMessage message, Object sent) throws JMSException
+ {
+ _producer.send(message);
+
+ ObjectMessage receivedMessage = (ObjectMessage) _consumer.receive(1000);
+
+ assertNotNull("Message was not received.", receivedMessage);
+
+ UUID result = (UUID) receivedMessage.getObject();
+
+ assertEquals("First read: UUIDs were not equal", sent, result);
+
+ result = (UUID) receivedMessage.getObject();
+
+ assertEquals("Second read: UUIDs were not equal", sent, result);
+ }
+
+
+ public void testSendEmptyObjectMessage() throws JMSException
+ {
+ ObjectMessage testMessage = _session.createObjectMessage();
+ testMessage.setStringProperty("test-property", "test-value");
+ assertNotNull("Object was null", testMessage.toString());
+
+ _producer.send(testMessage);
+
+ ObjectMessage receivedMessage = (ObjectMessage) _consumer.receive(1000);
+
+ assertNotNull("Message was not received.", receivedMessage);
+ assertNull("No object was sent", receivedMessage.getObject());
+ assertEquals("Unexpected property received", "test-value", receivedMessage.getStringProperty("test-property"));
+ }
+
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/client/message/SelectorTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/client/message/SelectorTest.java
new file mode 100644
index 0000000000..d945301bbe
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/client/message/SelectorTest.java
@@ -0,0 +1,314 @@
+/*
+ *
+ * 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.qpid.test.client.message;
+
+import org.junit.Assert;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQDestination;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.BasicMessageProducer;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+import javax.jms.DeliveryMode;
+import javax.jms.Destination;
+import javax.jms.InvalidSelectorException;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageListener;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import java.util.concurrent.CountDownLatch;
+
+public class SelectorTest extends QpidBrokerTestCase implements MessageListener
+{
+ private static final Logger _logger = LoggerFactory.getLogger(SelectorTest.class);
+
+ private AMQConnection _connection;
+ private AMQDestination _destination;
+ private int count;
+ private static final String INVALID_SELECTOR = "Cost LIKE 5";
+ CountDownLatch _responseLatch = new CountDownLatch(1);
+
+ private static final String BAD_MATHS_SELECTOR = " 1 % 5";
+
+ private static final long RECIEVE_TIMEOUT = 1000;
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ init((AMQConnection) getConnection("guest", "guest"));
+ }
+
+ private void init(AMQConnection connection) throws JMSException
+ {
+ init(connection, new AMQQueue(connection, getTestQueueName(), true));
+ }
+
+ private void init(AMQConnection connection, AMQDestination destination) throws JMSException
+ {
+ _connection = connection;
+ _destination = destination;
+ connection.start();
+ }
+
+ public void onMessage(Message message)
+ {
+ count++;
+ _logger.info("Got Message:" + message);
+ _responseLatch.countDown();
+ }
+
+ public void testUsingOnMessage() throws Exception
+ {
+ String selector = "Cost = 2 AND \"property-with-hyphen\" = 'wibble'";
+ // selector = "JMSType = Special AND Cost = 2 AND AMQMessageID > 0 AND JMSDeliveryMode=" + DeliveryMode.NON_PERSISTENT;
+
+ Session session = (AMQSession) _connection.createSession(false, AMQSession.NO_ACKNOWLEDGE);
+ // _session.createConsumer(destination).setMessageListener(this);
+ session.createConsumer(_destination, selector).setMessageListener(this);
+
+ try
+ {
+ Message msg = session.createTextMessage("Message");
+ msg.setJMSPriority(1);
+ msg.setIntProperty("Cost", 2);
+ msg.setStringProperty("property-with-hyphen", "wibble");
+ msg.setJMSType("Special");
+
+ _logger.info("Sending Message:" + msg);
+
+ ((BasicMessageProducer) session.createProducer(_destination)).send(msg, DeliveryMode.NON_PERSISTENT);
+ _logger.info("Message sent, waiting for response...");
+
+ _responseLatch.await();
+
+ if (count > 0)
+ {
+ _logger.info("Got message");
+ }
+
+ if (count == 0)
+ {
+ fail("Did not get message!");
+ // throw new RuntimeException("Did not get message!");
+ }
+ }
+ catch (JMSException e)
+ {
+ _logger.debug("JMS:" + e.getClass().getSimpleName() + ":" + e.getMessage());
+ if (!(e instanceof InvalidSelectorException))
+ {
+ fail("Wrong exception:" + e.getMessage());
+ }
+ else
+ {
+ _logger.debug("SUCCESS!!");
+ }
+ }
+ catch (InterruptedException e)
+ {
+ _logger.debug("IE :" + e.getClass().getSimpleName() + ":" + e.getMessage());
+ }
+
+ }
+
+ public void testUnparsableSelectors() throws Exception
+ {
+ AMQSession session = (AMQSession) _connection.createSession(false, AMQSession.NO_ACKNOWLEDGE);
+ boolean caught = false;
+
+ //Try Creating a Browser
+ try
+ {
+ session.createBrowser(session.createQueue("Ping"), INVALID_SELECTOR);
+ }
+ catch (JMSException e)
+ {
+ _logger.debug("JMS:" + e.getClass().getSimpleName() + ":" + e.getMessage());
+ if (!(e instanceof InvalidSelectorException))
+ {
+ fail("Wrong exception:" + e.getMessage());
+ }
+ caught = true;
+ }
+ assertTrue("No exception thrown!", caught);
+ caught = false;
+
+ //Try Creating a Consumer
+ try
+ {
+ session.createConsumer(session.createQueue("Ping"), INVALID_SELECTOR);
+ }
+ catch (JMSException e)
+ {
+ _logger.debug("JMS:" + e.getClass().getSimpleName() + ":" + e.getMessage());
+ if (!(e instanceof InvalidSelectorException))
+ {
+ fail("Wrong exception:" + e.getMessage());
+ }
+ caught = true;
+ }
+ assertTrue("No exception thrown!", caught);
+ caught = false;
+
+ //Try Creating a Receiever
+ try
+ {
+ session.createReceiver(session.createQueue("Ping"), INVALID_SELECTOR);
+ }
+ catch (JMSException e)
+ {
+ _logger.debug("JMS:" + e.getClass().getSimpleName() + ":" + e.getMessage());
+ if (!(e instanceof InvalidSelectorException))
+ {
+ fail("Wrong exception:" + e.getMessage());
+ }
+ caught = true;
+ }
+ assertTrue("No exception thrown!", caught);
+ caught = false;
+
+ try
+ {
+ session.createReceiver(session.createQueue("Ping"), BAD_MATHS_SELECTOR);
+ }
+ catch (JMSException e)
+ {
+ _logger.debug("JMS:" + e.getClass().getSimpleName() + ":" + e.getMessage());
+ if (!(e instanceof InvalidSelectorException))
+ {
+ fail("Wrong exception:" + e.getMessage());
+ }
+ caught = true;
+ }
+ assertTrue("No exception thrown!", caught);
+ caught = false;
+
+ }
+
+ public void testRuntimeSelectorError() throws JMSException
+ {
+ Session session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ MessageConsumer consumer = session.createConsumer(_destination , "testproperty % 5 = 1");
+ MessageProducer producer = session.createProducer(_destination);
+ Message sentMsg = session.createTextMessage();
+
+ sentMsg.setIntProperty("testproperty", 1); // 1 % 5
+ producer.send(sentMsg);
+ Message recvd = consumer.receive(RECIEVE_TIMEOUT);
+ assertNotNull(recvd);
+
+ sentMsg.setStringProperty("testproperty", "hello"); // "hello" % 5 makes no sense
+ producer.send(sentMsg);
+ try
+ {
+ recvd = consumer.receive(RECIEVE_TIMEOUT);
+ assertNull(recvd);
+ }
+ catch (Exception e)
+ {
+
+ }
+ assertFalse("Connection should not be closed", _connection.isClosed());
+ }
+
+ public void testSelectorWithJMSMessageID() throws Exception
+ {
+ Session session = _connection.createSession(true, Session.SESSION_TRANSACTED);
+
+ MessageProducer prod = session.createProducer(_destination);
+ MessageConsumer consumer = session.createConsumer(_destination,"JMSMessageID IS NOT NULL");
+
+ for (int i=0; i<2; i++)
+ {
+ Message msg = session.createTextMessage("Msg" + String.valueOf(i));
+ prod.send(msg);
+ }
+ session.commit();
+
+ Message msg1 = consumer.receive(1000);
+ Message msg2 = consumer.receive(1000);
+
+ Assert.assertNotNull("Msg1 should not be null", msg1);
+ Assert.assertNotNull("Msg2 should not be null", msg2);
+
+ session.commit();
+
+ prod.setDisableMessageID(true);
+
+ for (int i=2; i<4; i++)
+ {
+ Message msg = session.createTextMessage("Msg" + String.valueOf(i));
+ prod.send(msg);
+ }
+
+ session.commit();
+ Message msg3 = consumer.receive(1000);
+ Assert.assertNull("Msg3 should be null", msg3);
+ session.commit();
+ consumer = session.createConsumer(_destination,"JMSMessageID IS NULL");
+
+ Message msg4 = consumer.receive(1000);
+ Message msg5 = consumer.receive(1000);
+ session.commit();
+ Assert.assertNotNull("Msg4 should not be null", msg4);
+ Assert.assertNotNull("Msg5 should not be null", msg5);
+ }
+
+ public void testSelectorWithJMSDeliveryMode() throws Exception
+ {
+ Session session = _connection.createSession(false, Session.SESSION_TRANSACTED);
+
+ Destination dest1 = session.createTopic("test1");
+ Destination dest2 = session.createTopic("test2");
+
+ MessageProducer prod1 = session.createProducer(dest1);
+ MessageProducer prod2 = session.createProducer(dest2);
+ MessageConsumer consumer1 = session.createConsumer(dest1,"JMSDeliveryMode = 'PERSISTENT'");
+ MessageConsumer consumer2 = session.createConsumer(dest2,"JMSDeliveryMode = 'NON_PERSISTENT'");
+
+ Message msg1 = session.createTextMessage("Persistent");
+ prod1.send(msg1);
+ prod2.send(msg1);
+
+ prod1.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
+ prod2.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
+
+ Message msg2 = session.createTextMessage("Non_Persistent");
+ prod1.send(msg2);
+ prod2.send(msg2);
+
+ TextMessage m1 = (TextMessage)consumer1.receive(1000);
+ assertEquals("Consumer1 should receive the persistent message","Persistent",m1.getText());
+ assertNull("Consumer1 should not receiver another message",consumer1.receive(1000));
+
+ TextMessage m2 = (TextMessage)consumer2.receive(1000);
+ assertEquals("Consumer2 should receive the non persistent message","Non_Persistent",m2.getText());
+ assertNull("Consumer2 should not receiver another message",consumer2.receive(1000));
+ }
+
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/client/queue/LVQTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/client/queue/LVQTest.java
new file mode 100644
index 0000000000..51566403b3
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/client/queue/LVQTest.java
@@ -0,0 +1,84 @@
+/*
+ *
+ * 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.qpid.test.client.queue;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+import javax.jms.Connection;
+import javax.jms.Destination;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+
+public class LVQTest extends QpidBrokerTestCase
+{
+ private static final Logger _logger = LoggerFactory.getLogger(LVQTest.class);
+ private Connection _connection;
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+ _connection = getConnection() ;
+ _connection.start();
+ }
+
+ @Override
+ public void tearDown() throws Exception
+ {
+ _connection.close();
+ super.tearDown();
+ }
+
+ public void testLVQQueue() throws Exception
+ {
+ String addr = "ADDR:my-lvq-queue; {create: always, " +
+ "node: {x-bindings: [{exchange : 'amq.direct', key : test}], " +
+ "x-declare:{arguments : {'qpid.last_value_queue':1}}}}";
+
+ Session ssn = _connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
+
+ Destination dest = ssn.createQueue(addr);
+ MessageConsumer consumer = ssn.createConsumer(dest);
+ MessageProducer prod = ssn.createProducer(ssn.createQueue("ADDR:amq.direct/test"));
+
+ for (int i=0; i<40; i++)
+ {
+ Message msg = ssn.createTextMessage(String.valueOf(i));
+ msg.setStringProperty("qpid.LVQ_key", String.valueOf(i%10));
+ prod.send(msg);
+ }
+
+ for (int i=0; i<10; i++)
+ {
+ TextMessage msg = (TextMessage)consumer.receive(500);
+ assertEquals("The last value is not reflected","3" + i,msg.getText());
+ }
+
+ assertNull("There should not be anymore messages",consumer.receive(500));
+ }
+
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/client/queue/QueuePolicyTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/client/queue/QueuePolicyTest.java
new file mode 100644
index 0000000000..b785326ef2
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/client/queue/QueuePolicyTest.java
@@ -0,0 +1,120 @@
+/*
+*
+* 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.qpid.test.client.queue;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+import javax.jms.Connection;
+import javax.jms.Destination;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+
+public class QueuePolicyTest extends QpidBrokerTestCase
+{
+ private static final Logger _logger = LoggerFactory.getLogger(QueuePolicyTest.class);
+ private Connection _connection;
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+ _connection = getConnection() ;
+ _connection.start();
+ }
+
+ @Override
+ public void tearDown() throws Exception
+ {
+ _connection.close();
+ super.tearDown();
+ }
+
+ /**
+ * Test Goal : To create a ring queue programitcally with max queue count using the
+ * address string and observe that it works as expected.
+ */
+ public void testRejectPolicy() throws Exception
+ {
+ String addr = "ADDR:queue; {create: always, " +
+ "node: {x-bindings: [{exchange : 'amq.direct', key : test}], " +
+ "x-declare:{ arguments : {'qpid.max_count':5} }}}";
+
+ Session ssn = _connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
+
+ Destination dest = ssn.createQueue(addr);
+ MessageConsumer consumer = ssn.createConsumer(dest);
+ MessageProducer prod = ssn.createProducer(ssn.createQueue("ADDR:amq.direct/test"));
+
+ for (int i=0; i<6; i++)
+ {
+ prod.send(ssn.createMessage());
+ }
+
+ try
+ {
+ prod.send(ssn.createMessage());
+ ((AMQSession)ssn).sync();
+ fail("The client did not receive an exception after exceeding the queue limit");
+ }
+ catch (AMQException e)
+ {
+ assertTrue("The correct error code is not set",e.getErrorCode().toString().contains("506"));
+ }
+ }
+
+ /**
+ * Test Goal : To create a ring queue programitcally using the address string and observe
+ * that it works as expected.
+ */
+ public void testRingPolicy() throws Exception
+ {
+ Session ssn = _connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
+
+ String addr = "ADDR:my-ring-queue; {create: always, " +
+ "node: {x-bindings: [{exchange : 'amq.direct', key : test}], " +
+ "x-declare:{arguments : {'qpid.policy_type':ring, 'qpid.max_count':2} }}}";
+
+ Destination dest = ssn.createQueue(addr);
+ MessageConsumer consumer = ssn.createConsumer(dest);
+ MessageProducer prod = ssn.createProducer(ssn.createQueue("ADDR:amq.direct/test"));
+
+ _connection.stop();
+
+ prod.send(ssn.createTextMessage("Test1"));
+ prod.send(ssn.createTextMessage("Test2"));
+ prod.send(ssn.createTextMessage("Test3"));
+
+ _connection.start();
+
+ TextMessage msg = (TextMessage)consumer.receive(1000);
+ assertEquals("The consumer should receive the msg with body='Test2'","Test2",msg.getText());
+
+ msg = (TextMessage)consumer.receive(1000);
+ assertEquals("The consumer should receive the msg with body='Test3'","Test3",msg.getText());
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/ack/Acknowledge2ConsumersTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/ack/Acknowledge2ConsumersTest.java
new file mode 100644
index 0000000000..23efb656d2
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/ack/Acknowledge2ConsumersTest.java
@@ -0,0 +1,193 @@
+/*
+ *
+ * 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.qpid.test.unit.ack;
+
+import org.apache.qpid.client.AMQDestination;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+import javax.jms.Connection;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.Queue;
+import javax.jms.Session;
+
+public class Acknowledge2ConsumersTest extends QpidBrokerTestCase
+{
+ protected static int NUM_MESSAGES = 100;
+ protected Connection _con;
+ protected Queue _queue;
+ private Session _producerSession;
+ private Session _consumerSession;
+ private MessageConsumer _consumerA;
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+
+ _queue = (Queue) getInitialContext().lookup("queue");
+
+ //Create Producer put some messages on the queue
+ _con = getConnection();
+ }
+
+ private void init(boolean transacted, int mode) throws JMSException
+ {
+ _producerSession = _con.createSession(true, Session.SESSION_TRANSACTED);
+ _consumerSession = _con.createSession(transacted, mode);
+ _consumerA = _consumerSession.createConsumer(_queue);
+ _con.start();
+ }
+
+ /**
+ * Produces Messages that
+ *
+ * @param transacted
+ * @param mode
+ *
+ * @throws Exception
+ */
+ private void test2ConsumersAcking(boolean transacted, int mode) throws Exception
+ {
+ init(transacted, mode);
+
+ // These should all end up being prefetched by sessionA
+ sendMessage(_producerSession, _queue, NUM_MESSAGES / 2);
+
+ //Create a second consumer (consumerB) to consume some of the messages
+ MessageConsumer consumerB = _consumerSession.createConsumer(_queue);
+
+ // These messages should be roundrobined between A and B
+ sendMessage(_producerSession, _queue, NUM_MESSAGES / 2);
+
+ int count = 0;
+ //Use consumerB to receive messages it has
+ Message msg = consumerB.receive(1500);
+ while (msg != null)
+ {
+ if (mode == Session.CLIENT_ACKNOWLEDGE)
+ {
+ msg.acknowledge();
+ }
+ count++;
+ msg = consumerB.receive(1500);
+ }
+ if (transacted)
+ {
+ _consumerSession.commit();
+ }
+
+ // Close the consumers
+ _consumerA.close();
+ consumerB.close();
+
+ // and close the session to release any prefetched messages.
+ _consumerSession.close();
+ assertEquals("Wrong number of messages on queue", NUM_MESSAGES - count,
+ ((AMQSession) _producerSession).getQueueDepth((AMQDestination) _queue));
+
+ // Clean up messages that may be left on the queue
+ _consumerSession = _con.createSession(transacted, mode);
+ _consumerA = _consumerSession.createConsumer(_queue);
+ msg = _consumerA.receive(1500);
+ while (msg != null)
+ {
+ if (mode == Session.CLIENT_ACKNOWLEDGE)
+ {
+ msg.acknowledge();
+ }
+ msg = _consumerA.receive(1500);
+ }
+ _consumerA.close();
+ if (transacted)
+ {
+ _consumerSession.commit();
+ }
+ _consumerSession.close();
+ }
+
+ public void test2ConsumersAutoAck() throws Exception
+ {
+ test2ConsumersAcking(false, Session.AUTO_ACKNOWLEDGE);
+ }
+
+ public void test2ConsumersClientAck() throws Exception
+ {
+ test2ConsumersAcking(false, Session.CLIENT_ACKNOWLEDGE);
+ }
+
+ public void test2ConsumersTx() throws Exception
+ {
+ test2ConsumersAcking(true, Session.SESSION_TRANSACTED);
+ }
+
+
+
+//
+// /**
+// * Check that session level acknowledge does correctly ack all previous
+// * values. Send 3 messages(0,1,2) then ack 1 and 2. If session ack is
+// * working correctly then acking 1 will also ack 0. Acking 2 will not
+// * attempt to re-ack 0 and 1.
+// *
+// * @throws Exception
+// */
+// public void testSessionAck() throws Exception
+// {
+// init(false, Session.CLIENT_ACKNOWLEDGE);
+//
+// sendMessage(_producerSession, _queue, 3);
+// Message msg;
+//
+// // Drop msg 0
+// _consumerA.receive(RECEIVE_TIMEOUT);
+//
+// // Take msg 1
+// msg = _consumerA.receive(RECEIVE_TIMEOUT);
+//
+// assertNotNull("Message 1 not correctly received.", msg);
+// assertEquals("Incorrect message received", 1, msg.getIntProperty(INDEX));
+//
+// // This should also ack msg 0
+// msg.acknowledge();
+//
+// // Take msg 2
+// msg = _consumerA.receive(RECEIVE_TIMEOUT);
+//
+// assertNotNull("Message 2 not correctly received.", msg);
+// assertEquals("Incorrect message received", 2, msg.getIntProperty(INDEX));
+//
+// // This should just ack msg 2
+// msg.acknowledge();
+//
+// _consumerA.close();
+// _consumerSession.close();
+//
+// assertEquals("Queue not empty.", 0,
+// ((AMQSession) _producerSession).getQueueDepth((AMQDestination) _queue));
+// _con.close();
+//
+//
+// }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/ack/AcknowledgeOnMessageTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/ack/AcknowledgeOnMessageTest.java
new file mode 100644
index 0000000000..602eb5137a
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/ack/AcknowledgeOnMessageTest.java
@@ -0,0 +1,226 @@
+/*
+ *
+ * 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.qpid.test.unit.ack;
+
+import org.apache.qpid.client.AMQDestination;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.JMSAMQException;
+import org.apache.qpid.client.failover.FailoverException;
+
+import javax.jms.Message;
+import javax.jms.MessageListener;
+import javax.jms.Session;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * This test extends the synchronous AcknowledgeTest to use a MessageListener
+ * and receive messages asynchronously.
+ */
+public class AcknowledgeOnMessageTest extends AcknowledgeTest implements MessageListener
+{
+ protected CountDownLatch _receivedAll;
+ protected AtomicReference<Exception> _causeOfFailure = new AtomicReference<Exception>(null);
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+ }
+
+ /**
+ * Override the synchronous AcknowledgeTest init to provide the _receivedAll
+ * CountDownLatch init and ensure that we set the MessageListener.
+ * @param transacted
+ * @param mode
+ * @throws Exception
+ */
+ @Override
+ public void init(boolean transacted, int mode) throws Exception
+ {
+ _receivedAll = new CountDownLatch(NUM_MESSAGES);
+
+ super.init(transacted, mode);
+ _consumer.setMessageListener(this);
+ }
+
+ /**
+ * This test overrides the testAcking from the simple recieve() model to all
+ * for asynchronous receiving of messages.
+ *
+ * Again the transaction/ack mode is provided to this main test run
+ *
+ * The init method is called which will setup the listener so that we can
+ * then sit and await using the _receivedAll CountDownLatch. We wait for up
+ * to 10s if no messages have been received in the last 10s then test will
+ * fail.
+ *
+ * If the test fails then it will attempt to retrieve any exception that the
+ * asynchronous delivery thread may have recorded.
+ *
+ * @param transacted
+ * @param mode
+ *
+ * @throws Exception
+ */
+ @Override
+ protected void testAcking(boolean transacted, int mode) throws Exception
+ {
+ init(transacted, mode);
+
+ _connection.start();
+
+ // Set the lastCount to NUM_MESSAGES, this ensures that the compare
+ // against the receviedAll count is accurate.
+ int lastCount = NUM_MESSAGES;
+
+ // Wait for messages to arrive
+ boolean complete = _receivedAll.await(10000L, TimeUnit.MILLISECONDS);
+
+ // If the messasges haven't arrived
+ while (!complete)
+ {
+ // Check how many we have received
+ int currentCount = (int) _receivedAll.getCount();
+
+ // make sure we have received a message in the last cycle.
+ if (lastCount == currentCount)
+ {
+ // If we didn't receive any messages then stop.
+ // Something must have gone wrong.
+ System.err.println("Giving up waiting as we didn't receive anything.");
+ break;
+ }
+ // Remember the currentCount as the lastCount for the next cycle.
+ // so we can exit if things get locked up.
+ lastCount = currentCount;
+
+ // Wait again for messages to arrive.
+ complete = _receivedAll.await(10000L, TimeUnit.MILLISECONDS);
+ }
+
+ // If we failed to receive all the messages then fail the test.
+ if (!complete)
+ {
+ // Check to see if we ended due to an exception in the onMessage handler
+ Exception cause = _causeOfFailure.get();
+ if (cause != null)
+ {
+ _logger.error("Cause of failure is: ", cause);
+ fail(cause.getMessage());
+ }
+ else
+ {
+ _logger.info("AOMT: Check QueueDepth:" + _queue);
+ long onQueue=((AMQSession) getConnection().createSession(false, Session.AUTO_ACKNOWLEDGE)).getQueueDepth((AMQDestination) _queue);
+ fail("All messages not received missing:" + _receivedAll.getCount() + "/" + NUM_MESSAGES+" On Queue:"+onQueue);
+
+ }
+ }
+
+ // Even if we received all the messages.
+ // Check to see if we ended due to an exception in the onMessage handler
+ Exception cause = _causeOfFailure.get();
+ if (cause != null)
+ {
+ _logger.error("Failed due to following exception", cause);
+ fail(cause.getMessage());
+ }
+
+ try
+ {
+ _consumer.close();
+ }
+ catch (JMSAMQException amqe)
+ {
+ if (amqe.getLinkedException() instanceof FailoverException)
+ {
+ fail("QPID-143 : Auto Ack can acknowledge message from previous session after failver. If failover occurs between deliver and ack.");
+ }
+ // else Rethrow for TestCase to catch.
+ throw amqe;
+ }
+
+ _consumerSession.close();
+
+ _logger.info("AOMT: check number of message at end of test.");
+ assertEquals("Wrong number of messages on queue", 0,
+ ((AMQSession) getConnection().createSession(false, Session.AUTO_ACKNOWLEDGE)).getQueueDepth((AMQDestination) _queue));
+ }
+
+ /**
+ * The MessageListener interface that recieves the message and counts down
+ * the _receivedAll CountDownLatch.
+ *
+ * Again like AcknowledgeTest acknowledgement is actually handled in
+ * doAcknowlegement.
+ *
+ * The message INDEX is validated to ensure the correct message order is
+ * preserved.
+ *
+ * @param message
+ */
+ public void onMessage(Message message)
+ {
+ // Log received Message for debugging
+ _logger.info("RECEIVED MESSAGE:" + message);
+
+ try
+ {
+ int count = NUM_MESSAGES - (int) _receivedAll.getCount();
+
+ assertEquals("Incorrect message received", count, message.getIntProperty(INDEX));
+
+ count++;
+ if (count < NUM_MESSAGES)
+ {
+ //Send the next message
+ _producer.send(createNextMessage(_consumerSession, count));
+ }
+
+ doAcknowlegement(message);
+
+ _receivedAll.countDown();
+ }
+ catch (Exception e)
+ {
+ // This will end the test run by counting down _receivedAll
+ fail(e);
+ }
+ }
+
+ /**
+ * Pass the given exception back to the waiting thread to fail the test run.
+ *
+ * @param e The exception that is causing the test to fail.
+ */
+ protected void fail(Exception e)
+ {
+ //record the failure
+ _causeOfFailure.set(e);
+ // End the test.
+ while (_receivedAll.getCount() != 0)
+ {
+ _receivedAll.countDown();
+ }
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/ack/AcknowledgeTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/ack/AcknowledgeTest.java
new file mode 100644
index 0000000000..841d0ea4ba
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/ack/AcknowledgeTest.java
@@ -0,0 +1,188 @@
+/*
+ *
+ * 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.qpid.test.unit.ack;
+
+import org.apache.qpid.client.AMQDestination;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+import javax.jms.Connection;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.Session;
+
+/**
+ * Test the various JMS Acknowledge Modes the single testAcking method does all
+ * the work of receiving and validation of acking.
+ *
+ * The ack mode is provided from the various test methods.
+ */
+public class AcknowledgeTest extends QpidBrokerTestCase
+{
+ protected int NUM_MESSAGES;
+ protected Connection _connection;
+ protected Queue _queue;
+ protected Session _consumerSession;
+ protected MessageConsumer _consumer;
+ protected MessageProducer _producer;
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ NUM_MESSAGES = 5;
+
+ _queue = getTestQueue();
+
+ //Create Producer put some messages on the queue
+ _connection = getConnection();
+ }
+
+ protected void init(boolean transacted, int mode) throws Exception
+ {
+ _consumerSession = _connection.createSession(transacted, mode);
+ _consumer = _consumerSession.createConsumer(_queue);
+ _producer = _consumerSession.createProducer(_queue);
+
+ // These should all end up being prefetched by session
+ sendMessage(_consumerSession, _queue, 1);
+
+ syncIfNotTransacted(transacted);
+
+ assertEquals("Wrong number of messages on queue", 1,
+ ((AMQSession<?,?>) _consumerSession).getQueueDepth((AMQDestination) _queue));
+ }
+
+ /**
+ * The main test method.
+ *
+ * Receive the initial message and then proceed to send and ack messages
+ * until we have processed NUM_MESSAGES worth of messages.
+ *
+ * Each message is tagged with an INDEX value and these are used to check
+ * that the messages are received in the correct order.
+ *
+ * The test concludes by validating that the queue depth is 0 as expected.
+ *
+ * @param transacted
+ * @param mode
+ *
+ * @throws Exception
+ */
+ protected void testAcking(boolean transacted, int mode) throws Exception
+ {
+ init(transacted, mode);
+
+ _connection.start();
+
+ Message msg = _consumer.receive(1500);
+
+ int count = 0;
+ while (count < NUM_MESSAGES)
+ {
+ assertNotNull("Message " + count + " not correctly received.", msg);
+ assertEquals("Incorrect message received", count, msg.getIntProperty(INDEX));
+ count++;
+
+ if (count < NUM_MESSAGES)
+ {
+ //Send the next message
+ _producer.send(createNextMessage(_consumerSession, count));
+ syncIfNotTransacted(transacted);
+ }
+
+ doAcknowlegement(msg);
+
+ msg = _consumer.receive(1500);
+ }
+
+ if (_consumerSession.getTransacted())
+ {
+ //Acknowledge the last msg if we are testing transacted otherwise queueDepth will be 1
+ doAcknowlegement(msg);
+ }
+
+ assertEquals("Wrong number of messages on queue", 0,
+ ((AMQSession<?,?>) _consumerSession).getQueueDepth((AMQDestination) _queue));
+ }
+
+ /**
+ * Perform the acknowledgement of messages if additionally required.
+ *
+ * @param msg
+ *
+ * @throws JMSException
+ */
+ protected void doAcknowlegement(Message msg) throws JMSException
+ {
+ if (_consumerSession.getTransacted())
+ {
+ _consumerSession.commit();
+ }
+
+ if (_consumerSession.getAcknowledgeMode() == Session.CLIENT_ACKNOWLEDGE)
+ {
+ msg.acknowledge();
+ }
+ }
+
+ public void testClientAck() throws Exception
+ {
+ testAcking(false, Session.CLIENT_ACKNOWLEDGE);
+ }
+
+ public void testAutoAck() throws Exception
+ {
+ testAcking(false, Session.AUTO_ACKNOWLEDGE);
+ }
+
+ public void testTransacted() throws Exception
+ {
+ testAcking(true, Session.SESSION_TRANSACTED);
+ }
+
+ public void testDupsOk() throws Exception
+ {
+ testAcking(false, Session.DUPS_OK_ACKNOWLEDGE);
+ }
+
+ public void testNoAck() throws Exception
+ {
+ testAcking(false, AMQSession.NO_ACKNOWLEDGE);
+ }
+
+ public void testPreAck() throws Exception
+ {
+ testAcking(false, AMQSession.PRE_ACKNOWLEDGE);
+ }
+
+ private void syncIfNotTransacted(boolean transacted) throws Exception
+ {
+ if(!transacted)
+ {
+ ((AMQSession<?,?>)_consumerSession).sync();
+ }
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/ack/ClientAcknowledgeTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/ack/ClientAcknowledgeTest.java
new file mode 100644
index 0000000000..291e1697ca
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/ack/ClientAcknowledgeTest.java
@@ -0,0 +1,82 @@
+/*
+ * 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.qpid.test.unit.ack;
+
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+import javax.jms.Connection;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.Session;
+
+public class ClientAcknowledgeTest extends QpidBrokerTestCase
+{
+ private static final long ONE_DAY_MS = 1000l * 60 * 60 * 24;
+ private Connection _connection;
+ private Queue _queue;
+ private Session _consumerSession;
+ private MessageConsumer _consumer;
+ private MessageProducer _producer;
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ _queue = getTestQueue();
+ _connection = getConnection();
+ }
+
+ /**
+ * Test that message.acknowledge actually acknowledges, regardless of
+ * the flusher thread period, by restarting the broker after calling
+ * acknowledge, and then verifying after restart that the message acked
+ * is no longer present. This test requires a persistent store.
+ */
+ public void testClientAckWithLargeFlusherPeriod() throws Exception
+ {
+ setTestClientSystemProperty("qpid.session.max_ack_delay", Long.toString(ONE_DAY_MS));
+ _consumerSession = _connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ _consumer = _consumerSession.createConsumer(_queue);
+ _connection.start();
+
+ _producer = _consumerSession.createProducer(_queue);
+ _producer.send(createNextMessage(_consumerSession, 1));
+ _producer.send(createNextMessage(_consumerSession, 2));
+
+ Message message = _consumer.receive(1000l);
+ assertNotNull("Message has not been received", message);
+ assertEquals("Unexpected message is received", 1, message.getIntProperty(INDEX));
+ message.acknowledge();
+
+ //restart broker to allow verification of the acks
+ //without explicitly closing connection (which acks)
+ restartBroker();
+
+ // try to receive the message again, which should fail (as it was ackd)
+ _connection = getConnection();
+ _connection.start();
+ _consumerSession = _connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ _consumer = _consumerSession.createConsumer(_queue);
+ message = _consumer.receive(1000l);
+ assertNotNull("Message has not been received", message);
+ assertEquals("Unexpected message is received", 2, message.getIntProperty(INDEX));
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/ack/RecoverTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/ack/RecoverTest.java
new file mode 100644
index 0000000000..23ea4ac258
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/ack/RecoverTest.java
@@ -0,0 +1,483 @@
+/*
+ * 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.qpid.test.unit.ack;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.configuration.ClientProperties;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.jms.Session;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+import javax.jms.Connection;
+import javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageListener;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.TextMessage;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class RecoverTest extends QpidBrokerTestCase
+{
+ static final Logger _logger = LoggerFactory.getLogger(RecoverTest.class);
+
+ private static final int POSIITIVE_TIMEOUT = 2000;
+
+ private volatile Exception _error;
+ private AtomicInteger count;
+
+ protected AMQConnection _connection;
+ protected Session _consumerSession;
+ protected MessageConsumer _consumer;
+ static final int SENT_COUNT = 4;
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ _error = null;
+ count = new AtomicInteger();
+ }
+
+ protected void initTest() throws Exception
+ {
+ _connection = (AMQConnection) getConnection();
+
+ _consumerSession = _connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ Queue queue = _consumerSession.createQueue(getTestQueueName());
+
+ _consumer = _consumerSession.createConsumer(queue);
+
+ _logger.info("Sending four messages");
+ sendMessage(_connection.createSession(false, Session.AUTO_ACKNOWLEDGE), queue, SENT_COUNT);
+ _logger.info("Starting connection");
+ _connection.start();
+ }
+
+ protected Message validateNextMessages(int nextCount, int startIndex) throws JMSException
+ {
+ Message message = null;
+ for (int index = 0; index < nextCount; index++)
+ {
+ message = _consumer.receive(3000);
+ assertEquals(startIndex + index, message.getIntProperty(INDEX));
+ }
+ return message;
+ }
+
+ protected void validateRemainingMessages(int remaining) throws JMSException
+ {
+ int index = SENT_COUNT - remaining;
+
+ Message message = null;
+ while (index != SENT_COUNT)
+ {
+ message = _consumer.receive(3000);
+ assertNotNull(message);
+ assertEquals(index++, message.getIntProperty(INDEX));
+ }
+
+ if (message != null)
+ {
+ _logger.info("Received redelivery of three messages. Acknowledging last message");
+ message.acknowledge();
+ }
+
+ _logger.info("Calling acknowledge with no outstanding messages");
+ // all acked so no messages to be delivered
+ _consumerSession.recover();
+
+ message = _consumer.receiveNoWait();
+ assertNull(message);
+ _logger.info("No messages redelivered as is expected");
+ }
+
+ public void testRecoverResendsMsgs() throws Exception
+ {
+ initTest();
+
+ Message message = validateNextMessages(1, 0);
+ message.acknowledge();
+ _logger.info("Received and acknowledged first message");
+
+ _consumer.receive();
+ _consumer.receive();
+ _consumer.receive();
+ _logger.info("Received all four messages. Calling recover with three outstanding messages");
+ // no ack for last three messages so when I call recover I expect to get three messages back
+
+ _consumerSession.recover();
+
+ validateRemainingMessages(3);
+ }
+
+ public void testRecoverResendsMsgsAckOnEarlier() throws Exception
+ {
+ initTest();
+
+ Message message = validateNextMessages(2, 0);
+ message.acknowledge();
+ _logger.info("Received 2 messages, acknowledge() first message, should acknowledge both");
+
+ _consumer.receive();
+ _consumer.receive();
+ _logger.info("Received all four messages. Calling recover with two outstanding messages");
+ // no ack for last three messages so when I call recover I expect to get three messages back
+ _consumerSession.recover();
+
+ Message message2 = _consumer.receive(3000);
+ assertNotNull(message2);
+ assertEquals(2, message2.getIntProperty(INDEX));
+
+ Message message3 = _consumer.receive(3000);
+ assertNotNull(message3);
+ assertEquals(3, message3.getIntProperty(INDEX));
+
+ _logger.info("Received redelivery of two messages. calling acknolwedgeThis() first of those message");
+ ((org.apache.qpid.jms.Message) message2).acknowledgeThis();
+
+ _logger.info("Calling recover");
+ // all acked so no messages to be delivered
+ _consumerSession.recover();
+
+ message3 = _consumer.receive(3000);
+ assertNotNull(message3);
+ assertEquals(3, message3.getIntProperty(INDEX));
+ ((org.apache.qpid.jms.Message) message3).acknowledgeThis();
+
+ // all acked so no messages to be delivered
+ validateRemainingMessages(0);
+ }
+
+ public void testAcknowledgePerConsumer() throws Exception
+ {
+ AMQConnection con = (AMQConnection) getConnection();
+
+ Session consumerSession = con.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ Queue queue =
+ new AMQQueue(consumerSession.getDefaultQueueExchangeName(), new AMQShortString("Q1"), new AMQShortString("Q1"),
+ false, true);
+ Queue queue2 =
+ new AMQQueue(consumerSession.getDefaultQueueExchangeName(), new AMQShortString("Q2"), new AMQShortString("Q2"),
+ false, true);
+ MessageConsumer consumer = consumerSession.createConsumer(queue);
+ MessageConsumer consumer2 = consumerSession.createConsumer(queue2);
+
+ AMQConnection con2 = (AMQConnection) getConnection();
+ Session producerSession = con2.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ MessageProducer producer = producerSession.createProducer(queue);
+ MessageProducer producer2 = producerSession.createProducer(queue2);
+
+ producer.send(producerSession.createTextMessage("msg1"));
+ producer2.send(producerSession.createTextMessage("msg2"));
+
+ con2.close();
+
+ _logger.info("Starting connection");
+ con.start();
+
+ TextMessage tm2 = (TextMessage) consumer2.receive(2000);
+ assertNotNull(tm2);
+ assertEquals("msg2", tm2.getText());
+
+ tm2.acknowledge();
+ consumerSession.recover();
+
+ TextMessage tm1 = (TextMessage) consumer.receive(2000);
+ assertNotNull(tm1);
+ assertEquals("msg1", tm1.getText());
+
+ con.close();
+
+ }
+
+ public void testRecoverInAutoAckListener() throws Exception
+ {
+ AMQConnection con = (AMQConnection) getConnection();
+
+ final Session consumerSession = con.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ Queue queue =
+ new AMQQueue(consumerSession.getDefaultQueueExchangeName(), new AMQShortString("Q3"), new AMQShortString("Q3"),
+ false, true);
+ MessageConsumer consumer = consumerSession.createConsumer(queue);
+ MessageProducer producer = consumerSession.createProducer(queue);
+ producer.send(consumerSession.createTextMessage("hello"));
+
+ final Object lock = new Object();
+
+ consumer.setMessageListener(new MessageListener()
+ {
+
+ public void onMessage(Message message)
+ {
+ try
+ {
+ count.incrementAndGet();
+ if (count.get() == 1)
+ {
+ if (message.getJMSRedelivered())
+ {
+ setError(new Exception("Message marked as redelivered on what should be first delivery attempt"));
+ }
+
+ consumerSession.recover();
+ }
+ else if (count.get() == 2)
+ {
+ if (!message.getJMSRedelivered())
+ {
+ setError(new Exception("Message not marked as redelivered on what should be second delivery attempt"));
+ }
+ }
+ else
+ {
+ _logger.error(message.toString());
+ setError(new Exception("Message delivered too many times!: " + count));
+ }
+ }
+ catch (JMSException e)
+ {
+ _logger.error("Error recovering session: " + e, e);
+ setError(e);
+ }
+
+ synchronized (lock)
+ {
+ lock.notify();
+ }
+ }
+ });
+
+ con.start();
+
+ long waitTime = 30000L;
+ long waitUntilTime = System.currentTimeMillis() + waitTime;
+
+ synchronized (lock)
+ {
+ while ((count.get() <= 1) && (waitTime > 0))
+ {
+ lock.wait(waitTime);
+ if (count.get() <= 1)
+ {
+ waitTime = waitUntilTime - System.currentTimeMillis();
+ }
+ }
+ }
+
+ Thread.sleep(1000);
+
+ if (_error != null)
+ {
+ throw _error;
+ }
+
+ assertEquals("Message not received the correct number of times.",
+ 2, count.get());
+ }
+
+ private void setError(Exception e)
+ {
+ _error = e;
+ }
+
+ /**
+ * Goal : Check if ordering is preserved when doing recovery under reasonable circumstances.
+ * Refer QPID-2471 for more details.
+ * Test strategy :
+ * Send 8 messages to a topic.
+ * The consumer will call recover until it sees a message 5 times,
+ * at which point it will ack that message.
+ * It will continue the above until it acks all the messages.
+ * While doing so it will verify that the messages are not
+ * delivered out of order.
+ */
+ public void testOrderingWithSyncConsumer() throws Exception
+ {
+ Connection con = (Connection) getConnection();
+ javax.jms.Session session = con.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ Destination topic = session.createTopic("myTopic");
+ MessageConsumer cons = session.createConsumer(topic);
+
+ sendMessage(session,topic,8);
+ con.start();
+
+ int messageSeen = 0;
+ int expectedIndex = 0;
+ long startTime = System.currentTimeMillis();
+
+ while(expectedIndex < 8)
+ {
+ // Based on historical data, on average the test takes about 6 secs to complete.
+ if (System.currentTimeMillis() - startTime > 8000)
+ {
+ fail("Test did not complete on time. Received " +
+ expectedIndex + " msgs so far. Please check the logs");
+ }
+
+ Message message = cons.receive(POSIITIVE_TIMEOUT);
+ int actualIndex = message.getIntProperty(INDEX);
+
+ assertEquals("Received Message Out Of Order",expectedIndex, actualIndex);
+
+ //don't ack the message until we receive it 5 times
+ if( messageSeen < 5 )
+ {
+ _logger.debug("Ignoring message " + actualIndex + " and calling recover");
+ session.recover();
+ messageSeen++;
+ }
+ else
+ {
+ messageSeen = 0;
+ expectedIndex++;
+ message.acknowledge();
+ _logger.debug("Acknowledging message " + actualIndex);
+ }
+ }
+ }
+
+ /**
+ * Goal : Same as testOderingWithSyncConsumer
+ * Test strategy :
+ * Same as testOderingWithSyncConsumer but using a
+ * Message Listener instead of a sync receive().
+ */
+ public void testOrderingWithAsyncConsumer() throws Exception
+ {
+ Connection con = (Connection) getConnection();
+ final javax.jms.Session session = con.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ Destination topic = session.createTopic("myTopic");
+ MessageConsumer cons = session.createConsumer(topic);
+
+ sendMessage(session,topic,8);
+ con.start();
+
+ final Object lock = new Object();
+ final AtomicBoolean pass = new AtomicBoolean(false); //used as work around for 'final'
+
+ cons.setMessageListener(new MessageListener()
+ {
+ private int messageSeen = 0;
+ private int expectedIndex = 0;
+
+ public void onMessage(Message message)
+ {
+ try
+ {
+ int actualIndex = message.getIntProperty(INDEX);
+ assertEquals("Received Message Out Of Order", expectedIndex, actualIndex);
+
+ //don't ack the message until we receive it 5 times
+ if( messageSeen < 5 )
+ {
+ _logger.debug("Ignoring message " + actualIndex + " and calling recover");
+ session.recover();
+ messageSeen++;
+ }
+ else
+ {
+ messageSeen = 0;
+ expectedIndex++;
+ message.acknowledge();
+ _logger.debug("Acknowledging message " + actualIndex);
+ if (expectedIndex == 8)
+ {
+ pass.set(true);
+ synchronized (lock)
+ {
+ lock.notifyAll();
+ }
+ }
+ }
+ }
+ catch (JMSException e)
+ {
+ _error = e;
+ synchronized (lock)
+ {
+ lock.notifyAll();
+ }
+ }
+ }
+ });
+
+ synchronized(lock)
+ {
+ // Based on historical data, on average the test takes about 6 secs to complete.
+ lock.wait(8000);
+ }
+
+ assertNull("Unexpected exception thrown by async listener", _error);
+
+ if (!pass.get())
+ {
+ fail("Test did not complete on time. Please check the logs");
+ }
+ }
+
+ /**
+ * This test ensures that after exhausting credit (prefetch), a {@link Session#recover()} successfully
+ * restores credit and allows the same messages to be re-received.
+ */
+ public void testRecoverSessionAfterCreditExhausted() throws Exception
+ {
+ final int maxPrefetch = 5;
+
+ // We send more messages than prefetch size. This ensure that if the 0-10 client were to
+ // complete the message commands before the rollback command is sent, the broker would
+ // send additional messages utilising the release credit. This problem would manifest itself
+ // as an incorrect message (or no message at all) being received at the end of the test.
+
+ final int numMessages = maxPrefetch * 2;
+
+ setTestClientSystemProperty(ClientProperties.MAX_PREFETCH_PROP_NAME, String.valueOf(maxPrefetch));
+
+ Connection con = (Connection) getConnection();
+ final javax.jms.Session session = con.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ Destination dest = session.createQueue(getTestQueueName());
+ MessageConsumer cons = session.createConsumer(dest);
+
+ sendMessage(session, dest, numMessages);
+ con.start();
+
+ for (int i=0; i< maxPrefetch; i++)
+ {
+ final Message message = cons.receive(POSIITIVE_TIMEOUT);
+ assertNotNull("Received:" + i, message);
+ assertEquals("Unexpected message received", i, message.getIntProperty(INDEX));
+ }
+
+ _logger.info("Recovering");
+ session.recover();
+
+ Message result = cons.receive(POSIITIVE_TIMEOUT);
+ // Expect the first message
+ assertEquals("Unexpected message received", 0, result.getIntProperty(INDEX));
+ }
+
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/BytesMessageTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/BytesMessageTest.java
new file mode 100644
index 0000000000..b545f610d1
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/BytesMessageTest.java
@@ -0,0 +1,324 @@
+/*
+ * 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.qpid.test.unit.basic;
+
+import org.junit.Assert;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQDestination;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.message.JMSBytesMessage;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+import org.apache.qpid.transport.util.Waiter;
+
+import javax.jms.BytesMessage;
+import javax.jms.Connection;
+import javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageListener;
+import javax.jms.MessageNotReadableException;
+import javax.jms.MessageNotWriteableException;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public class BytesMessageTest extends QpidBrokerTestCase implements MessageListener
+{
+ private static final Logger _logger = LoggerFactory.getLogger(BytesMessageTest.class);
+
+ private Connection _connection;
+ private Destination _destination;
+ private Session _session;
+ private final List<JMSBytesMessage> received = new ArrayList<JMSBytesMessage>();
+ private final List<byte[]> messages = new ArrayList<byte[]>();
+ private int _count = 100;
+ public String _connectionString = "vm://:1";
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ init((AMQConnection) getConnection("guest", "guest"));
+ }
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ }
+
+ void init(AMQConnection connection) throws Exception
+ {
+ init(connection, new AMQQueue(connection, randomize("BytesMessageTest"), true));
+ }
+
+ void init(AMQConnection connection, AMQDestination destination) throws Exception
+ {
+ _connection = connection;
+ _destination = destination;
+ _session = connection.createSession(false, AMQSession.NO_ACKNOWLEDGE);
+
+ // Set up a slow consumer.
+ _session.createConsumer(destination).setMessageListener(this);
+ connection.start();
+ }
+
+ public void test() throws Exception
+ {
+ try
+ {
+ send(_count);
+ waitFor(_count);
+ check();
+ _logger.info("Completed without failure");
+ }
+ finally
+ {
+ _connection.close();
+ }
+ }
+
+ void send(int count) throws JMSException
+ {
+ // create a publisher
+ MessageProducer producer = _session.createProducer(_destination);
+ for (int i = 0; i < count; i++)
+ {
+ BytesMessage msg = _session.createBytesMessage();
+
+ try
+ {
+ msg.readFloat();
+ Assert.fail("Message should not be readable");
+ }
+ catch (MessageNotReadableException mnwe)
+ {
+ // normal execution
+ }
+
+ byte[] data = ("Message " + i).getBytes();
+ msg.writeBytes(data);
+ messages.add(data);
+ producer.send(msg);
+ }
+ }
+
+ void waitFor(int count) throws InterruptedException
+ {
+ synchronized (received)
+ {
+ Waiter w = new Waiter(received, 30000);
+ while (received.size() < count && w.hasTime())
+ {
+ w.await();
+ }
+ }
+ }
+
+ void check() throws JMSException
+ {
+ List<byte[]> actual = new ArrayList<byte[]>();
+ for (JMSBytesMessage m : received)
+ {
+ ByteBuffer buffer = m.getData();
+ byte[] data = new byte[buffer.remaining()];
+ buffer.get(data);
+ actual.add(data);
+
+ // Check Body Write Status
+ try
+ {
+ m.writeBoolean(true);
+ Assert.fail("Message should not be writeable");
+ }
+ catch (MessageNotWriteableException mnwe)
+ {
+ // normal execution
+ }
+
+ m.clearBody();
+
+ try
+ {
+ m.writeBoolean(true);
+ }
+ catch (MessageNotWriteableException mnwe)
+ {
+ Assert.fail("Message should be writeable");
+ }
+
+ // Check property write status
+ try
+ {
+ m.setStringProperty("test", "test");
+ Assert.fail("Message should not be writeable");
+ }
+ catch (MessageNotWriteableException mnwe)
+ {
+ // normal execution
+ }
+
+ m.clearProperties();
+
+ try
+ {
+ m.setStringProperty("test", "test");
+ }
+ catch (MessageNotWriteableException mnwe)
+ {
+ Assert.fail("Message should be writeable");
+ }
+
+ }
+
+ assertEqual(messages.iterator(), actual.iterator());
+ }
+
+ private static void assertEqual(Iterator expected, Iterator actual)
+ {
+ List<String> errors = new ArrayList<String>();
+ while (expected.hasNext() && actual.hasNext())
+ {
+ try
+ {
+ assertEquivalent((byte[]) expected.next(), (byte[]) actual.next());
+ }
+ catch (Exception e)
+ {
+ errors.add(e.getMessage());
+ }
+ }
+ while (expected.hasNext())
+ {
+ errors.add("Expected " + expected.next() + " but no more actual values.");
+ }
+ while (actual.hasNext())
+ {
+ errors.add("Found " + actual.next() + " but no more expected values.");
+ }
+
+ if (!errors.isEmpty())
+ {
+ throw new RuntimeException(errors.toString());
+ }
+ }
+
+ private static void assertEquivalent(byte[] expected, byte[] actual)
+ {
+ if (expected.length != actual.length)
+ {
+ throw new RuntimeException("Expected length " + expected.length + " got " + actual.length);
+ }
+
+ for (int i = 0; i < expected.length; i++)
+ {
+ if (expected[i] != actual[i])
+ {
+ throw new RuntimeException("Failed on byte " + i + " of " + expected.length);
+ }
+ }
+ }
+
+ public void onMessage(Message message)
+ {
+ synchronized (received)
+ {
+ received.add((JMSBytesMessage) message);
+ received.notify();
+ }
+ }
+
+ private static String randomize(String in)
+ {
+ return in + System.currentTimeMillis();
+ }
+
+ public static void main(String[] argv) throws Exception
+ {
+ final String connectionString;
+ final int count;
+ if (argv.length == 0)
+ {
+ connectionString = "vm://:1";
+ count = 100;
+ }
+ else
+ {
+ connectionString = argv[0];
+ count = Integer.parseInt(argv[1]);
+ }
+
+ _logger.info("connectionString = " + connectionString);
+ _logger.info("count = " + count);
+
+ BytesMessageTest test = new BytesMessageTest();
+ test._connectionString = connectionString;
+ test._count = count;
+ test.test();
+ }
+
+ public void testModificationAfterSend() throws Exception
+ {
+ Connection connection = getConnection();
+ Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ BytesMessage jmsMsg = session.createBytesMessage();
+ Destination destination = getTestQueue();
+
+ /* Set the constant message contents. */
+
+ jmsMsg.setStringProperty("foo", "test");
+
+ /* Pre-populate the message body buffer to the target size. */
+ byte[] jmsMsgBodyBuffer = new byte[1024];
+
+ connection.start();
+
+ /* Send messages. */
+ MessageProducer producer = session.createProducer(destination);
+
+ MessageConsumer consumer = session.createConsumer(destination);
+
+ for(int writtenMsgCount = 0; writtenMsgCount < 10; writtenMsgCount++)
+ {
+ /* Set the per send message contents. */
+ jmsMsgBodyBuffer[0] = (byte) writtenMsgCount;
+ jmsMsg.writeBytes(jmsMsgBodyBuffer, 0, jmsMsgBodyBuffer.length);
+ /** Try to write a message. */
+ producer.send(jmsMsg);
+ }
+
+
+ for(int writtenMsgCount = 0; writtenMsgCount < 10; writtenMsgCount++)
+ {
+ BytesMessage recvdMsg = (BytesMessage) consumer.receive(1000L);
+ assertNotNull("Expected to receive message " + writtenMsgCount + " but did not", recvdMsg);
+ assertEquals("Message "+writtenMsgCount+" not of expected size", (long) ((writtenMsgCount + 1)*1024),
+ recvdMsg.getBodyLength());
+
+ }
+ }
+
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/FieldTableMessageTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/FieldTableMessageTest.java
new file mode 100644
index 0000000000..599c8061a7
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/FieldTableMessageTest.java
@@ -0,0 +1,165 @@
+/*
+ *
+ * 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.qpid.test.unit.basic;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQDestination;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.message.JMSBytesMessage;
+import org.apache.qpid.framing.AMQFrameDecodingException;
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.framing.FieldTableFactory;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+import javax.jms.BytesMessage;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageListener;
+import javax.jms.MessageProducer;
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class FieldTableMessageTest extends QpidBrokerTestCase implements MessageListener
+{
+ private static final Logger _logger = LoggerFactory.getLogger(FieldTableMessageTest.class);
+
+ private AMQConnection _connection;
+ private AMQDestination _destination;
+ private AMQSession _session;
+ private final ArrayList<JMSBytesMessage> received = new ArrayList<JMSBytesMessage>();
+ private FieldTable _expected;
+ private int _count = 10;
+ private String _connectionString = "vm://:1";
+ private CountDownLatch _waitForCompletion;
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ init( (AMQConnection) getConnection("guest", "guest"));
+ }
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ }
+
+ private void init(AMQConnection connection) throws Exception
+ {
+ init(connection, new AMQQueue(connection, randomize("FieldTableMessageTest"), true));
+ }
+
+ private void init(AMQConnection connection, AMQDestination destination) throws Exception
+ {
+ _connection = connection;
+ _destination = destination;
+ _session = (AMQSession) connection.createSession(false, AMQSession.NO_ACKNOWLEDGE);
+
+ // set up a slow consumer
+ _session.createConsumer(destination).setMessageListener(this);
+ connection.start();
+
+ // _expected = new FieldTableTest().load("FieldTableTest2.properties");
+ _expected = load();
+ }
+
+ private FieldTable load() throws IOException
+ {
+ FieldTable result = FieldTableFactory.newFieldTable();
+ result.setLong("one", 1L);
+ result.setLong("two", 2L);
+ result.setLong("three", 3L);
+ result.setLong("four", 4L);
+ result.setLong("five", 5L);
+
+ return result;
+ }
+
+ public void test() throws Exception
+ {
+ int count = _count;
+ _waitForCompletion = new CountDownLatch(_count);
+ send(count);
+ _waitForCompletion.await(20, TimeUnit.SECONDS);
+ check();
+ _logger.info("Completed without failure");
+ _connection.close();
+ }
+
+ void send(int count) throws JMSException, IOException
+ {
+ // create a publisher
+ MessageProducer producer = _session.createProducer(_destination);
+ for (int i = 0; i < count; i++)
+ {
+ BytesMessage msg = _session.createBytesMessage();
+ msg.writeBytes(_expected.getDataAsBytes());
+ producer.send(msg);
+ }
+ }
+
+
+ void check() throws JMSException, AMQFrameDecodingException, IOException
+ {
+ for (Object m : received)
+ {
+ final BytesMessage bytesMessage = (BytesMessage) m;
+ final long bodyLength = bytesMessage.getBodyLength();
+ byte[] data = new byte[(int) bodyLength];
+ bytesMessage.readBytes(data);
+ FieldTable actual = FieldTableFactory.newFieldTable(new DataInputStream(new ByteArrayInputStream(data)), bodyLength);
+ for (String key : _expected.keys())
+ {
+ assertEquals("Values for " + key + " did not match", _expected.getObject(key), actual.getObject(key));
+ }
+ }
+ }
+
+ public void onMessage(Message message)
+ {
+ synchronized (received)
+ {
+ received.add((JMSBytesMessage) message);
+ _waitForCompletion.countDown();
+ }
+ }
+
+ private static String randomize(String in)
+ {
+ return in + System.currentTimeMillis();
+ }
+
+ public static void main(String[] argv) throws Exception
+ {
+ FieldTableMessageTest test = new FieldTableMessageTest();
+ test._connectionString = (argv.length == 0) ? "vm://:1" : argv[0];
+ test.setUp();
+ test._count = (argv.length > 1) ? Integer.parseInt(argv[1]) : 5;
+ test.test();
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/InvalidDestinationTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/InvalidDestinationTest.java
new file mode 100644
index 0000000000..8961574d1e
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/InvalidDestinationTest.java
@@ -0,0 +1,171 @@
+/*
+ *
+ * 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.qpid.test.unit.basic;
+
+import java.util.Collections;
+import java.util.Map;
+import javax.jms.Connection;
+import javax.jms.InvalidDestinationException;
+import javax.jms.JMSException;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.QueueSender;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import javax.jms.Topic;
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.configuration.ClientProperties;
+import org.apache.qpid.jms.ConnectionURL;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+public class InvalidDestinationTest extends QpidBrokerTestCase
+{
+ private AMQConnection _connection;
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ _connection = (AMQConnection) getConnection("guest", "guest");
+ }
+
+ protected void tearDown() throws Exception
+ {
+ _connection.close();
+ super.tearDown();
+ }
+
+ public void testInvalidDestination() throws Exception
+ {
+ QueueSession queueSession = _connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ Queue invalidDestination = queueSession.createQueue("unknownQ");
+
+ Queue validDestination = queueSession.createQueue(getTestQueueName());
+
+ // This is the only easy way to create and bind a queue from the API :-(
+ queueSession.createConsumer(validDestination);
+ QueueSender sender;
+ TextMessage msg= queueSession.createTextMessage("Hello");
+
+ try
+ {
+ sender = queueSession.createSender(invalidDestination);
+
+ sender.send(msg);
+ fail("Expected InvalidDestinationException");
+ }
+ catch (InvalidDestinationException ex)
+ {
+ // pass
+ }
+
+ sender = queueSession.createSender(null);
+
+ try
+ {
+ sender.send(invalidDestination,msg);
+ fail("Expected InvalidDestinationException");
+ }
+ catch (InvalidDestinationException ex)
+ {
+ // pass
+ }
+ sender.send(validDestination,msg);
+ sender.close();
+ sender = queueSession.createSender(validDestination);
+ sender.send(msg);
+ }
+
+ /**
+ * Tests that specifying the {@value ClientProperties#VERIFY_QUEUE_ON_SEND} system property
+ * results in an exception when sending to an invalid queue destination.
+ */
+ public void testInvalidDestinationOnMessageProducer() throws Exception
+ {
+ setTestSystemProperty(ClientProperties.VERIFY_QUEUE_ON_SEND, "true");
+ final AMQConnection connection = (AMQConnection) getConnection();
+ doInvalidDestinationOnMessageProducer(connection);
+ }
+
+ /**
+ * Tests that specifying the {@value ConnectionURL.OPTIONS_VERIFY_QUEUE_ON_SEND}
+ * connection URL option property results in an exception when sending to an
+ * invalid queue destination.
+ */
+ public void testInvalidDestinationOnMessageProducerURL() throws Exception
+ {
+ Map<String, String> options = Collections.singletonMap(ConnectionURL.OPTIONS_VERIFY_QUEUE_ON_SEND, "true");
+ doInvalidDestinationOnMessageProducer(getConnectionWithOptions(options));
+ }
+
+ private void doInvalidDestinationOnMessageProducer(Connection connection) throws JMSException
+ {
+ Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ String invalidQueueName = getTestQueueName() + "UnknownQ";
+ Queue invalidDestination = session.createQueue(invalidQueueName);
+
+ String validQueueName = getTestQueueName() + "KnownQ";
+ Queue validDestination = session.createQueue(validQueueName);
+
+ // This is the only easy way to create and bind a queue from the API :-(
+ session.createConsumer(validDestination);
+
+ MessageProducer sender;
+ TextMessage msg = session.createTextMessage("Hello");
+ try
+ {
+ sender = session.createProducer(invalidDestination);
+ sender.send(msg);
+ fail("Expected InvalidDestinationException");
+ }
+ catch (InvalidDestinationException ex)
+ {
+ // pass
+ }
+
+ sender = session.createProducer(null);
+ invalidDestination = new AMQQueue("amq.direct",invalidQueueName);
+
+ try
+ {
+ sender.send(invalidDestination,msg);
+ fail("Expected InvalidDestinationException");
+ }
+ catch (InvalidDestinationException ex)
+ {
+ // pass
+ }
+ sender.send(validDestination, msg);
+ sender.close();
+ sender = session.createProducer(validDestination);
+ sender.send(msg);
+
+ //Verify sending to an 'invalid' Topic doesn't throw an exception
+ String invalidTopic = getTestQueueName() + "UnknownT";
+ Topic topic = session.createTopic(invalidTopic);
+ sender = session.createProducer(topic);
+ sender.send(msg);
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/LargeMessageTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/LargeMessageTest.java
new file mode 100644
index 0000000000..ace8324dab
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/LargeMessageTest.java
@@ -0,0 +1,190 @@
+/*
+ * 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.qpid.test.unit.basic;
+
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+import javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+
+public class LargeMessageTest extends QpidBrokerTestCase
+{
+ private static final Logger _logger = LoggerFactory.getLogger(LargeMessageTest.class);
+
+ private Destination _destination;
+ private AMQSession _session;
+ private AMQConnection _connection;
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ try
+ {
+ _connection = (AMQConnection) getConnection("guest", "guest");
+ init( _connection );
+ }
+ catch (Exception e)
+ {
+ fail("Unable to initialilse connection: " + e);
+ }
+ }
+
+ protected void tearDown() throws Exception
+ {
+ _connection.close();
+ super.tearDown();
+ }
+
+ private void init(AMQConnection connection) throws Exception
+ {
+ Destination destination = new AMQQueue(connection, "LargeMessageTest", true);
+ init(connection, destination);
+ }
+
+ private void init(AMQConnection connection, Destination destination) throws Exception
+ {
+ _destination = destination;
+ _session = (AMQSession) connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ connection.start();
+ }
+
+ // Test boundary of 1 packet to 2 packets
+ public void test64kminus9()
+ {
+ checkLargeMessage((64 * 1024) - 9);
+ }
+
+ public void test64kminus8()
+ {
+ checkLargeMessage((64 * 1024)-8);
+ }
+
+ public void test64kminus7()
+ {
+ checkLargeMessage((64 * 1024)-7);
+ }
+
+
+ public void test64kplus1()
+ {
+ checkLargeMessage((64 * 1024) + 1);
+ }
+
+ // Test packet boundary of 3 packtes
+ public void test128kminus1()
+ {
+ checkLargeMessage((128 * 1024) - 1);
+ }
+
+ public void test128k()
+ {
+ checkLargeMessage(128 * 1024);
+ }
+
+ public void test128kplus1()
+ {
+ checkLargeMessage((128 * 1024) + 1);
+ }
+
+ // Testing larger messages
+
+ public void test256k()
+ {
+ checkLargeMessage(256 * 1024);
+ }
+
+ public void test512k()
+ {
+ checkLargeMessage(512 * 1024);
+ }
+
+ public void test1024k()
+ {
+ checkLargeMessage(1024 * 1024);
+ }
+
+ protected void checkLargeMessage(int messageSize)
+ {
+ try
+ {
+ MessageConsumer consumer = _session.createConsumer(_destination);
+ MessageProducer producer = _session.createProducer(_destination);
+ _logger.info("Testing message of size:" + messageSize);
+
+ String _messageText = buildLargeMessage(messageSize);
+
+ _logger.debug("Message built");
+
+ producer.send(_session.createTextMessage(_messageText));
+
+ TextMessage result = (TextMessage) consumer.receive(10000);
+
+ assertNotNull("Null message recevied", result);
+ assertEquals("Message Size", _messageText.length(), result.getText().length());
+ assertEquals("Message content differes", _messageText, result.getText());
+ }
+ catch (JMSException e)
+ {
+ _logger.error("Exception occured", e);
+ fail("Exception occured:" + e.getCause());
+ }
+ }
+
+ private String buildLargeMessage(int size)
+ {
+ StringBuilder builder = new StringBuilder(size);
+
+ char ch = 'a';
+
+ for (int i = 1; i <= size; i++)
+ {
+ builder.append(ch);
+
+ if ((i % 1000) == 0)
+ {
+ ch++;
+ if (ch == ('z' + 1))
+ {
+ ch = 'a';
+ }
+ }
+ }
+
+ return builder.toString();
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(LargeMessageTest.class);
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/MapMessageTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/MapMessageTest.java
new file mode 100644
index 0000000000..1b9c9fcb17
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/MapMessageTest.java
@@ -0,0 +1,1269 @@
+/*
+ *
+ * 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.qpid.test.unit.basic;
+
+import org.junit.Assert;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.message.JMSMapMessage;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+import javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.MapMessage;
+import javax.jms.Message;
+import javax.jms.MessageFormatException;
+import javax.jms.MessageListener;
+import javax.jms.MessageNotWriteableException;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public class MapMessageTest extends QpidBrokerTestCase implements MessageListener
+{
+ private static final Logger _logger = LoggerFactory.getLogger(MapMessageTest.class);
+
+ private AMQConnection _connection;
+ private Destination _destination;
+ private AMQSession _session;
+ private final List<JMSMapMessage> received = new ArrayList<JMSMapMessage>();
+
+ private static final String MESSAGE = "Message ";
+ private int _count = 100;
+ public String _connectionString = "vm://:1";
+ private byte[] _bytes = { 99, 98, 97, 96, 95 };
+ private static final float _smallfloat = 100.0f;
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ try
+ {
+ init((AMQConnection) getConnection("guest", "guest"));
+ }
+ catch (Exception e)
+ {
+ fail("Unable to initialilse connection: " + e);
+ }
+ }
+
+ protected void tearDown() throws Exception
+ {
+ _logger.info("Tearing Down unit.basic.MapMessageTest");
+ super.tearDown();
+ }
+
+ private void init(AMQConnection connection) throws Exception
+ {
+ Destination destination = new AMQQueue(connection, randomize("MapMessageTest"), true);
+ init(connection, destination);
+ }
+
+ private void init(AMQConnection connection, Destination destination) throws Exception
+ {
+ _connection = connection;
+ _destination = destination;
+ _session = (AMQSession) connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ // set up a slow consumer
+ _session.createConsumer(destination).setMessageListener(this);
+ connection.start();
+ }
+
+ public void test() throws Exception
+ {
+ int count = _count;
+ send(count);
+ waitFor(count);
+ check();
+ _connection.close();
+ }
+
+ void send(int count) throws JMSException
+ {
+ // create a publisher
+ MessageProducer producer = _session.createProducer(_destination);
+ for (int i = 0; i < count; i++)
+ {
+ MapMessage message = _session.createMapMessage();
+
+ setMapValues(message, i);
+
+ producer.send(message);
+ }
+ }
+
+ private void setMapValues(MapMessage message, int i) throws JMSException
+ {
+ message.setBoolean("odd", (i / 2) == 0);
+ message.setByte("byte",Byte.MAX_VALUE);
+ message.setBytes("bytes", _bytes);
+ message.setChar("char",'c');
+ message.setDouble("double", Double.MAX_VALUE);
+ message.setFloat("float", Float.MAX_VALUE);
+ message.setFloat("smallfloat", 100);
+ message.setInt("messageNumber", i);
+ message.setInt("int", Integer.MAX_VALUE);
+ message.setLong("long", Long.MAX_VALUE);
+ message.setShort("short", Short.MAX_VALUE);
+ message.setString("message", MESSAGE + i);
+
+ // Test Setting Object Values
+ message.setObject("object-bool", true);
+ message.setObject("object-byte", Byte.MAX_VALUE);
+ message.setObject("object-bytes", _bytes);
+ message.setObject("object-char", 'c');
+ message.setObject("object-double", Double.MAX_VALUE);
+ message.setObject("object-float", Float.MAX_VALUE);
+ message.setObject("object-int", Integer.MAX_VALUE);
+ message.setObject("object-long", Long.MAX_VALUE);
+ message.setObject("object-short", Short.MAX_VALUE);
+
+ // Set a null String value
+ message.setString("nullString", null);
+ // Highlight protocol problem
+ message.setString("emptyString", "");
+
+ }
+
+ void waitFor(int count) throws Exception
+ {
+ long waitTime = 30000L;
+ long waitUntilTime = System.currentTimeMillis() + 30000L;
+
+ synchronized (received)
+ {
+ while ((received.size() < count) && (waitTime > 0))
+ {
+ if (received.size() < count)
+ {
+ received.wait(waitTime);
+ }
+
+ if (received.size() < count)
+ {
+ waitTime = waitUntilTime - System.currentTimeMillis();
+ }
+ }
+
+ if (received.size() < count)
+ {
+ throw new Exception("Timed-out. Waiting for " + count + " only got " + received.size());
+ }
+ }
+ }
+
+ void check() throws JMSException
+ {
+ int count = 0;
+ for (JMSMapMessage m : received)
+ {
+ testMapValues(m, count);
+
+ testCorrectExceptions(m);
+
+ testMessageWriteStatus(m);
+
+ testPropertyWriteStatus(m);
+
+ count++;
+ }
+ }
+
+ private void testCorrectExceptions(JMSMapMessage m) throws JMSException
+ {
+ testBoolean(m);
+
+ testByte(m);
+
+ testBytes(m);
+
+ testChar(m);
+
+ testDouble(m);
+
+ testFloat(m);
+
+ testInt(m);
+
+ testLong(m);
+
+ testShort(m);
+
+ testString(m);
+ }
+
+ private void testString(JMSMapMessage m) throws JMSException
+ {
+
+ Assert.assertFalse(m.getBoolean("message"));
+
+ try
+ {
+ m.getByte("message");
+ fail("Exception Expected.");
+ }
+ catch (NumberFormatException nfe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getShort("message");
+ fail("Exception Expected.");
+ }
+ catch (NumberFormatException nfe)
+ {
+ // normal execution
+ }
+
+ // Try bad reads
+ try
+ {
+ m.getChar("message");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException npe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getInt("message");
+ fail("Exception Expected.");
+ }
+ catch (NumberFormatException nfe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getLong("message");
+ fail("Exception Expected.");
+ }
+ catch (NumberFormatException nfe)
+ {
+ // normal execution
+ }
+
+ // Try bad reads
+ try
+ {
+ m.getFloat("message");
+ fail("Exception Expected.");
+ }
+ catch (NumberFormatException nfe)
+ {
+ // normal execution
+ }
+ // Try bad reads
+ try
+ {
+ m.getDouble("message");
+ fail("Exception Expected.");
+ }
+ catch (NumberFormatException nfe)
+ {
+ // normal execution
+ }
+ // Try bad reads
+ try
+ {
+ m.getBytes("message");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ Assert.assertEquals(MESSAGE + m.getInt("messageNumber"), m.getString("message"));
+ }
+
+ private void testShort(JMSMapMessage m) throws JMSException
+ {
+
+ // Try bad reads
+ try
+ {
+ m.getBoolean("short");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getByte("short");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ Assert.assertEquals(Short.MAX_VALUE, m.getShort("short"));
+
+ // Try bad reads
+ try
+ {
+ m.getChar("short");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException npe)
+ {
+ // normal execution
+ }
+
+ Assert.assertEquals(Short.MAX_VALUE, m.getInt("short"));
+
+ Assert.assertEquals(Short.MAX_VALUE, m.getLong("short"));
+
+ // Try bad reads
+ try
+ {
+ m.getFloat("short");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+ // Try bad reads
+ try
+ {
+ m.getDouble("short");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+ // Try bad reads
+ try
+ {
+ m.getBytes("short");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ Assert.assertEquals("" + Short.MAX_VALUE, m.getString("short"));
+ }
+
+ private void testLong(JMSMapMessage m) throws JMSException
+ {
+
+ // Try bad reads
+ try
+ {
+ m.getBoolean("long");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getByte("long");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getShort("long");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ // Try bad reads
+ try
+ {
+ m.getChar("long");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException npe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getInt("long");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ Assert.assertEquals(Long.MAX_VALUE, m.getLong("long"));
+
+ // Try bad reads
+ try
+ {
+ m.getFloat("long");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+ // Try bad reads
+ try
+ {
+ m.getDouble("long");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+ // Try bad reads
+ try
+ {
+ m.getBytes("long");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ Assert.assertEquals("" + Long.MAX_VALUE, m.getString("long"));
+ }
+
+ private void testDouble(JMSMapMessage m) throws JMSException
+ {
+
+ // Try bad reads
+ try
+ {
+ m.getBoolean("double");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getByte("double");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getShort("double");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ // Try bad reads
+ try
+ {
+ m.getChar("double");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException npe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getInt("double");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getLong("double");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ // Try bad reads
+ try
+ {
+ m.getFloat("double");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ Assert.assertEquals(Double.MAX_VALUE, m.getDouble("double"), 0d);
+
+ // Try bad reads
+ try
+ {
+ m.getBytes("double");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ Assert.assertEquals("" + Double.MAX_VALUE, m.getString("double"));
+ }
+
+ private void testFloat(JMSMapMessage m) throws JMSException
+ {
+
+ // Try bad reads
+ try
+ {
+ m.getBoolean("float");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getByte("float");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getShort("float");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ // Try bad reads
+ try
+ {
+ m.getChar("float");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException npe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getInt("float");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getLong("float");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ Assert.assertEquals(Float.MAX_VALUE, m.getFloat("float"), 0f);
+
+ Assert.assertEquals(_smallfloat, m.getDouble("smallfloat"), 0f);
+
+ // Try bad reads
+ try
+ {
+ m.getBytes("float");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ Assert.assertEquals("" + Float.MAX_VALUE, m.getString("float"));
+ }
+
+ private void testInt(JMSMapMessage m) throws JMSException
+ {
+
+ // Try bad reads
+ try
+ {
+ m.getBoolean("int");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getByte("int");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getShort("int");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ // Try bad reads
+ try
+ {
+ m.getChar("int");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException npe)
+ {
+ // normal execution
+ }
+
+ Assert.assertEquals(Integer.MAX_VALUE, m.getInt("int"));
+
+ Assert.assertEquals(Integer.MAX_VALUE, (int) m.getLong("int"));
+
+ // Try bad reads
+ try
+ {
+ m.getFloat("int");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+ // Try bad reads
+ try
+ {
+ m.getDouble("int");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+ // Try bad reads
+ try
+ {
+ m.getBytes("int");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ Assert.assertEquals("" + Integer.MAX_VALUE, m.getString("int"));
+ }
+
+ private void testChar(JMSMapMessage m) throws JMSException
+ {
+
+ // Try bad reads
+ try
+ {
+ m.getBoolean("char");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getByte("char");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getShort("char");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ Assert.assertEquals('c', m.getChar("char"));
+
+ try
+ {
+ m.getInt("char");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getLong("char");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ // Try bad reads
+ try
+ {
+ m.getFloat("char");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+ // Try bad reads
+ try
+ {
+ m.getDouble("char");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+ // Try bad reads
+ try
+ {
+ m.getBytes("char");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ Assert.assertEquals("" + 'c', m.getString("char"));
+ }
+
+ private void testBytes(JMSMapMessage m) throws JMSException
+ {
+ // Try bad reads
+ try
+ {
+ m.getBoolean("bytes");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getByte("bytes");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getShort("bytes");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ // Try bad reads
+ try
+ {
+ m.getChar("bytes");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException npe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getInt("bytes");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getLong("bytes");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ // Try bad reads
+ try
+ {
+ m.getFloat("bytes");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+ // Try bad reads
+ try
+ {
+ m.getDouble("bytes");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ assertBytesEqual(_bytes, m.getBytes("bytes"));
+
+ try
+ {
+ m.getString("bytes");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ }
+
+ private void testByte(JMSMapMessage m) throws JMSException
+ {
+ // Try bad reads
+ try
+ {
+ m.getBoolean("byte");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ Assert.assertEquals(Byte.MAX_VALUE, m.getByte("byte"));
+
+ Assert.assertEquals((short) Byte.MAX_VALUE, m.getShort("byte"));
+
+ // Try bad reads
+ try
+ {
+ m.getChar("byte");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException npe)
+ {
+ // normal execution
+ }
+
+ // Reading a byte as an int is ok
+ Assert.assertEquals((short) Byte.MAX_VALUE, m.getInt("byte"));
+
+ Assert.assertEquals((short) Byte.MAX_VALUE, m.getLong("byte"));
+
+ // Try bad reads
+ try
+ {
+ m.getFloat("byte");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+ // Try bad reads
+ try
+ {
+ m.getDouble("byte");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+ // Try bad reads
+ try
+ {
+ m.getBytes("byte");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ Assert.assertEquals("" + Byte.MAX_VALUE, m.getString("byte"));
+
+ }
+
+ private void testBoolean(JMSMapMessage m) throws JMSException
+ {
+
+ Assert.assertEquals((m.getInt("messageNumber") / 2) == 0, m.getBoolean("odd"));
+
+ // Try bad reads
+ try
+ {
+ m.getByte("odd");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ // Try bad reads
+ try
+ {
+ m.getShort("odd");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+ // Try bad reads
+ try
+ {
+ m.getChar("odd");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException npe)
+ {
+ // normal execution
+ }
+ // Try bad reads
+ try
+ {
+ m.getInt("odd");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+ // Try bad reads
+ try
+ {
+ m.getLong("odd");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+ // Try bad reads
+ try
+ {
+ m.getFloat("odd");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+ // Try bad reads
+ try
+ {
+ m.getDouble("odd");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+ // Try bad reads
+ try
+ {
+ m.getBytes("odd");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ Assert.assertEquals("" + ((m.getInt("messageNumber") / 2) == 0), m.getString("odd"));
+ }
+
+ private void testPropertyWriteStatus(JMSMapMessage m) throws JMSException
+ {
+ // Check property write status
+ try
+ {
+ m.setStringProperty("test", "test");
+ Assert.fail("Message should not be writeable");
+ }
+ catch (MessageNotWriteableException mnwe)
+ {
+ // normal execution
+ }
+
+ m.clearProperties();
+
+ try
+ {
+ m.setStringProperty("test", "test");
+ }
+ catch (MessageNotWriteableException mnwe)
+ {
+ Assert.fail("Message should be writeable");
+ }
+ }
+
+ private void testMessageWriteStatus(JMSMapMessage m) throws JMSException
+ {
+ try
+ {
+ m.setInt("testint", 3);
+ fail("Message should not be writeable");
+ }
+ catch (MessageNotWriteableException mnwe)
+ {
+ // normal execution
+ }
+
+ m.clearBody();
+
+ try
+ {
+ m.setInt("testint", 3);
+ }
+ catch (MessageNotWriteableException mnwe)
+ {
+ Assert.fail("Message should be writeable");
+ }
+ }
+
+ private void testMapValues(JMSMapMessage m, int count) throws JMSException
+ {
+ // Test get<Primiative>
+
+ // Boolean
+ assertEqual((count / 2) == 0, m.getBoolean("odd"));
+ assertEqual("" + ((count / 2) == 0), m.getString("odd"));
+
+ // Byte
+ assertEqual(Byte.MAX_VALUE, m.getByte("byte"));
+ assertEqual("" + Byte.MAX_VALUE, m.getString("byte"));
+
+ // Bytes
+ assertBytesEqual(_bytes, m.getBytes("bytes"));
+
+ // Char
+ assertEqual('c', m.getChar("char"));
+
+ // Double
+ assertEqual(Double.MAX_VALUE, m.getDouble("double"));
+ assertEqual("" + Double.MAX_VALUE, m.getString("double"));
+
+ // Float
+ assertEqual(Float.MAX_VALUE, m.getFloat("float"));
+ assertEqual(_smallfloat, (float) m.getDouble("smallfloat"));
+ assertEqual("" + Float.MAX_VALUE, m.getString("float"));
+
+ // Integer
+ assertEqual(Integer.MAX_VALUE, m.getInt("int"));
+ assertEqual("" + Integer.MAX_VALUE, m.getString("int"));
+ assertEqual(count, m.getInt("messageNumber"));
+
+ // long
+ assertEqual(Long.MAX_VALUE, m.getLong("long"));
+ assertEqual("" + Long.MAX_VALUE, m.getString("long"));
+
+ // Short
+ assertEqual(Short.MAX_VALUE, m.getShort("short"));
+ assertEqual("" + Short.MAX_VALUE, m.getString("short"));
+ assertEqual((int) Short.MAX_VALUE, m.getInt("short"));
+
+ // String
+ assertEqual(MESSAGE + count, m.getString("message"));
+
+ // Test getObjects
+ assertEqual(true, m.getObject("object-bool"));
+ assertEqual(Byte.MAX_VALUE, m.getObject("object-byte"));
+ assertBytesEqual(_bytes, (byte[]) m.getObject("object-bytes"));
+ assertEqual('c', m.getObject("object-char"));
+ assertEqual(Double.MAX_VALUE, m.getObject("object-double"));
+ assertEqual(Float.MAX_VALUE, m.getObject("object-float"));
+ assertEqual(Integer.MAX_VALUE, m.getObject("object-int"));
+ assertEqual(Long.MAX_VALUE, m.getObject("object-long"));
+ assertEqual(Short.MAX_VALUE, m.getObject("object-short"));
+
+ // Check Special values
+ assertTrue(m.getString("nullString") == null);
+ assertEqual("", m.getString("emptyString"));
+ }
+
+ private void assertBytesEqual(byte[] expected, byte[] actual)
+ {
+ Assert.assertEquals(expected.length, actual.length);
+
+ for (int index = 0; index < expected.length; index++)
+ {
+ Assert.assertEquals(expected[index], actual[index]);
+ }
+ }
+
+ private static void assertEqual(Iterator expected, Iterator actual)
+ {
+ List<String> errors = new ArrayList<String>();
+ while (expected.hasNext() && actual.hasNext())
+ {
+ try
+ {
+ assertEqual(expected.next(), actual.next());
+ }
+ catch (Exception e)
+ {
+ errors.add(e.getMessage());
+ }
+ }
+ while (expected.hasNext())
+ {
+ errors.add("Expected " + expected.next() + " but no more actual values.");
+ }
+ while (actual.hasNext())
+ {
+ errors.add("Found " + actual.next() + " but no more expected values.");
+ }
+
+ if (!errors.isEmpty())
+ {
+ throw new RuntimeException(errors.toString());
+ }
+ }
+
+ private static void assertEqual(Object expected, Object actual)
+ {
+ if (!expected.equals(actual))
+ {
+ throw new RuntimeException("Expected '" + expected + "' found '" + actual + "'");
+ }
+ }
+
+ public void onMessage(Message message)
+ {
+ synchronized (received)
+ {
+ _logger.info("****************** Recevied Messgage:" + message);
+ received.add((JMSMapMessage) message);
+ received.notify();
+ }
+ }
+
+ private static String randomize(String in)
+ {
+ return in + System.currentTimeMillis();
+ }
+
+ public static void main(String[] argv) throws Exception
+ {
+ MapMessageTest test = new MapMessageTest();
+ test._connectionString = (argv.length == 0) ? "vm://:1" : argv[0];
+ test.setUp();
+ if (argv.length > 1)
+ {
+ test._count = Integer.parseInt(argv[1]);
+ }
+
+ test.test();
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(MapMessageTest.class);
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/MultipleConnectionTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/MultipleConnectionTest.java
new file mode 100644
index 0000000000..2d8847ea33
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/MultipleConnectionTest.java
@@ -0,0 +1,223 @@
+/*
+ * 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.qpid.test.unit.basic;
+
+import org.apache.qpid.framing.AMQShortString;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQDestination;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.AMQTopic;
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageListener;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+
+public class MultipleConnectionTest extends QpidBrokerTestCase
+{
+ private static final Logger _logger = LoggerFactory.getLogger(MultipleConnectionTest.class);
+
+ public static final String _defaultBroker = "vm://:1";
+ public String _connectionString = _defaultBroker;
+
+ private class Receiver
+ {
+ private AMQConnection _connection;
+ private Session[] _sessions;
+ private MessageCounter[] _counters;
+
+ Receiver(String broker, AMQDestination dest, int sessions) throws Exception
+ {
+ this((AMQConnection) getConnection("guest", "guest"), dest, sessions);
+ }
+
+ Receiver(AMQConnection connection, AMQDestination dest, int sessions) throws Exception
+ {
+ _connection = connection;
+ _sessions = new AMQSession[sessions];
+ _counters = new MessageCounter[sessions];
+ for (int i = 0; i < sessions; i++)
+ {
+ _sessions[i] = _connection.createSession(false, AMQSession.NO_ACKNOWLEDGE);
+ _counters[i] = new MessageCounter(_sessions[i].toString());
+ _sessions[i].createConsumer(dest).setMessageListener(_counters[i]);
+ }
+
+ _connection.start();
+ }
+
+ void close() throws JMSException
+ {
+ _connection.close();
+ }
+
+ public MessageCounter[] getCounters()
+ {
+ return _counters;
+ }
+ }
+
+ private class Publisher
+ {
+ private AMQConnection _connection;
+ private Session _session;
+ private MessageProducer _producer;
+
+ Publisher(String broker, AMQDestination dest) throws Exception
+ {
+ this((AMQConnection) getConnection("guest", "guest"), dest);
+ }
+
+ Publisher(AMQConnection connection, AMQDestination dest) throws Exception
+ {
+ _connection = connection;
+ _session = _connection.createSession(false, AMQSession.NO_ACKNOWLEDGE);
+ _producer = _session.createProducer(dest);
+ }
+
+ void send(String msg) throws JMSException
+ {
+ _producer.send(_session.createTextMessage(msg));
+ }
+
+ void close() throws JMSException
+ {
+ _connection.close();
+ }
+ }
+
+ private static class MessageCounter implements MessageListener
+ {
+ private final String _name;
+ private int _count;
+
+ MessageCounter(String name)
+ {
+ _name = name;
+ }
+
+ public synchronized void onMessage(Message message)
+ {
+ _count++;
+ notify();
+ }
+
+ synchronized boolean waitUntil(int expected, long maxWait) throws InterruptedException
+ {
+ long start = System.currentTimeMillis();
+ while (expected > _count)
+ {
+ long timeLeft = maxWait - timeSince(start);
+ if (timeLeft <= 0)
+ {
+ break;
+ }
+
+ wait(timeLeft);
+ }
+
+ return expected <= _count;
+ }
+
+ private long timeSince(long start)
+ {
+ return System.currentTimeMillis() - start;
+ }
+
+ public synchronized String toString()
+ {
+ return _name + ": " + _count;
+ }
+ }
+
+ private static void waitForCompletion(int expected, long wait, Receiver[] receivers) throws InterruptedException
+ {
+ for (int i = 0; i < receivers.length; i++)
+ {
+ waitForCompletion(expected, wait, receivers[i].getCounters());
+ }
+ }
+
+ private static void waitForCompletion(int expected, long wait, MessageCounter[] counters) throws InterruptedException
+ {
+ for (int i = 0; i < counters.length; i++)
+ {
+ if (!counters[i].waitUntil(expected, wait))
+ {
+ throw new RuntimeException("Expected: " + expected + " got " + counters[i]);
+ }
+ }
+ }
+
+ private static String randomize(String in)
+ {
+ return in + System.currentTimeMillis();
+ }
+
+ public static void main(String[] argv) throws Exception
+ {
+ String broker = (argv.length > 0) ? argv[0] : _defaultBroker;
+
+ MultipleConnectionTest test = new MultipleConnectionTest();
+ test._connectionString = broker;
+ test.test();
+ }
+
+ public void test() throws Exception
+ {
+ String broker = _connectionString;
+ int messages = 10;
+
+ AMQTopic topic = new AMQTopic(AMQShortString.valueOf(ExchangeDefaults.TOPIC_EXCHANGE_NAME), "amq.topic");
+
+ Receiver[] receivers = new Receiver[] { new Receiver(broker, topic, 2), new Receiver(broker, topic, 14) };
+
+ Publisher publisher = new Publisher(broker, topic);
+ for (int i = 0; i < messages; i++)
+ {
+ publisher.send("Message " + (i + 1));
+ }
+
+ try
+ {
+ waitForCompletion(messages, 5000, receivers);
+ _logger.info("All receivers received all expected messages");
+ }
+ finally
+ {
+ publisher.close();
+ for (int i = 0; i < receivers.length; i++)
+ {
+ receivers[i].close();
+ }
+ }
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(MultipleConnectionTest.class);
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/ObjectMessageTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/ObjectMessageTest.java
new file mode 100644
index 0000000000..4b5922902d
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/ObjectMessageTest.java
@@ -0,0 +1,276 @@
+/*
+ * 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.qpid.test.unit.basic;
+
+import org.junit.Assert;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQDestination;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.message.JMSObjectMessage;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageListener;
+import javax.jms.MessageNotWriteableException;
+import javax.jms.MessageProducer;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public class ObjectMessageTest extends QpidBrokerTestCase implements MessageListener
+{
+ private static final Logger _logger = LoggerFactory.getLogger(ObjectMessageTest.class);
+
+ private AMQConnection _connection;
+ private AMQDestination _destination;
+ private AMQSession _session;
+ private final List<JMSObjectMessage> received = new ArrayList<JMSObjectMessage>();
+ private final List<Payload> messages = new ArrayList<Payload>();
+ private int _count = 100;
+ public String _connectionString = "vm://:1";
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ try
+ {
+ init( (AMQConnection) getConnection("guest", "guest"));
+ }
+ catch (Exception e)
+ {
+ fail("Uable to initialise: " + e);
+ }
+ }
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ }
+
+ private void init(AMQConnection connection) throws Exception
+ {
+ init(connection, new AMQQueue(connection, randomize("ObjectMessageTest"), true));
+ }
+
+ private void init(AMQConnection connection, AMQDestination destination) throws Exception
+ {
+ _connection = connection;
+ _destination = destination;
+ _session = (AMQSession) connection.createSession(false, AMQSession.NO_ACKNOWLEDGE);
+
+ // set up a slow consumer
+ _session.createConsumer(destination).setMessageListener(this);
+ connection.start();
+ }
+
+ public void test() throws Exception
+ {
+ int count = _count;
+ send(count);
+ waitFor(count);
+ check();
+ _logger.info("Completed without failure");
+ _connection.close();
+ }
+
+ void send(int count) throws JMSException
+ {
+ // create a publisher
+ MessageProducer producer = _session.createProducer(_destination);
+ for (int i = 0; i < count; i++)
+ {
+ Payload payload = new Payload("Message " + i);
+ messages.add(payload);
+ producer.send(_session.createObjectMessage(payload));
+ }
+ }
+
+ void waitFor(int count) throws InterruptedException
+ {
+ synchronized (received)
+ {
+ long endTime = System.currentTimeMillis() + 30000L;
+ while (received.size() < count)
+ {
+ received.wait(30000);
+ if(received.size() < count && System.currentTimeMillis() > endTime)
+ {
+ throw new RuntimeException("Only received " + received.size() + " messages, was expecting " + count);
+ }
+ }
+ }
+ }
+
+ void check() throws JMSException
+ {
+ List<Object> actual = new ArrayList<Object>();
+ for (JMSObjectMessage m : received)
+ {
+ actual.add(m.getObject());
+
+ try
+ {
+ m.setObject("Test text");
+ Assert.fail("Message should not be writeable");
+ }
+ catch (MessageNotWriteableException mnwe)
+ {
+ // normal execution
+ }
+
+ m.clearBody();
+
+ try
+ {
+ m.setObject("Test text");
+ }
+ catch (MessageNotWriteableException mnwe)
+ {
+ Assert.fail("Message should be writeable");
+ }
+
+ // Check property write status
+ try
+ {
+ m.setStringProperty("test", "test");
+ Assert.fail("Message should not be writeable");
+ }
+ catch (MessageNotWriteableException mnwe)
+ {
+ // normal execution
+ }
+
+ m.clearProperties();
+
+ try
+ {
+ m.setStringProperty("test", "test");
+ }
+ catch (MessageNotWriteableException mnwe)
+ {
+ Assert.fail("Message should be writeable");
+ }
+
+ }
+
+ assertEqual(messages.iterator(), actual.iterator());
+
+ }
+
+ private static void assertEqual(Iterator expected, Iterator actual)
+ {
+ List<String> errors = new ArrayList<String>();
+ while (expected.hasNext() && actual.hasNext())
+ {
+ try
+ {
+ assertEqual(expected.next(), actual.next());
+ }
+ catch (Exception e)
+ {
+ errors.add(e.getMessage());
+ }
+ }
+ while (expected.hasNext())
+ {
+ errors.add("Expected " + expected.next() + " but no more actual values.");
+ }
+ while (actual.hasNext())
+ {
+ errors.add("Found " + actual.next() + " but no more expected values.");
+ }
+
+ if (!errors.isEmpty())
+ {
+ throw new RuntimeException(errors.toString());
+ }
+ }
+
+ private static void assertEqual(Object expected, Object actual)
+ {
+ if (!expected.equals(actual))
+ {
+ throw new RuntimeException("Expected '" + expected + "' found '" + actual + "'");
+ }
+ }
+
+ public void onMessage(Message message)
+ {
+ synchronized (received)
+ {
+ received.add((JMSObjectMessage) message);
+ received.notify();
+ }
+ }
+
+ private static String randomize(String in)
+ {
+ return in + System.currentTimeMillis();
+ }
+
+ private static class Payload implements Serializable
+ {
+ private final String data;
+
+ Payload(String data)
+ {
+ this.data = data;
+ }
+
+ public int hashCode()
+ {
+ return data.hashCode();
+ }
+
+ public boolean equals(Object o)
+ {
+ return (o instanceof Payload) && ((Payload) o).data.equals(data);
+ }
+
+ public String toString()
+ {
+ return "Payload[" + data + "]";
+ }
+ }
+
+ public static void main(String[] argv) throws Exception
+ {
+ ObjectMessageTest test = new ObjectMessageTest();
+ test._connectionString = (argv.length == 0) ? "vm://:1" : argv[0];
+ test.setUp();
+ if (argv.length > 1)
+ {
+ test._count = Integer.parseInt(argv[1]);
+ }
+
+ test.test();
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(ObjectMessageTest.class);
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/PropertyValueTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/PropertyValueTest.java
new file mode 100644
index 0000000000..c7ff564beb
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/PropertyValueTest.java
@@ -0,0 +1,386 @@
+/*
+ *
+ * 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.qpid.test.unit.basic;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.jms.Connection;
+import javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageFormatException;
+import javax.jms.MessageListener;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.Session;
+
+import org.junit.Assert;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.message.JMSTextMessage;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class PropertyValueTest extends QpidBrokerTestCase implements MessageListener
+{
+ private static final Logger _logger = LoggerFactory.getLogger(PropertyValueTest.class);
+
+ private AMQConnection _connection;
+ private Destination _destination;
+ private AMQSession _session;
+ private final List<JMSTextMessage> received = new ArrayList<JMSTextMessage>();
+ private final List<String> messages = new ArrayList<String>();
+ private Map<String, Destination> _replyToDestinations;
+ private int _count = 1;
+ public String _connectionString = "vm://:1";
+ private static final String USERNAME = "guest";
+
+ protected void setUp() throws Exception
+ {
+ _replyToDestinations = new HashMap<String, Destination>();
+ super.setUp();
+ }
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ }
+
+ private void init(AMQConnection connection) throws Exception
+ {
+ Destination destination = new AMQQueue(connection, randomize("PropertyValueTest"), true);
+ init(connection, destination);
+ }
+
+ private void init(AMQConnection connection, Destination destination) throws Exception
+ {
+ _connection = connection;
+ _destination = destination;
+ _session = (AMQSession) connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ // set up a slow consumer
+ _session.createConsumer(destination).setMessageListener(this);
+ connection.start();
+ }
+
+ private Message getTestMessage() throws Exception
+ {
+ Connection conn = getConnection();
+ Session ssn = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ return ssn.createTextMessage();
+ }
+
+ public void testGetNonexistent() throws Exception
+ {
+ Message m = getTestMessage();
+ String s = m.getStringProperty("nonexistent");
+ assertNull(s);
+ }
+
+ private static final String[] NAMES = {
+ "setBooleanProperty", "setByteProperty", "setShortProperty",
+ "setIntProperty", "setLongProperty", "setFloatProperty",
+ "setDoubleProperty", "setObjectProperty"
+ };
+
+ private static final Class[] TYPES = {
+ boolean.class, byte.class, short.class, int.class, long.class,
+ float.class, double.class, Object.class
+ };
+
+ private static final Object[] VALUES = {
+ true, (byte) 0, (short) 0, 0, (long) 0, (float) 0, (double) 0,
+ new Object()
+ };
+
+ public void testSetEmptyPropertyName() throws Exception
+ {
+ Message m = getTestMessage();
+
+ for (int i = 0; i < NAMES.length; i++)
+ {
+ Method meth = m.getClass().getMethod(NAMES[i], String.class, TYPES[i]);
+ try
+ {
+ meth.invoke(m, "", VALUES[i]);
+ fail("expected illegal argument exception");
+ }
+ catch (InvocationTargetException e)
+ {
+ assertEquals(e.getCause().getClass(), IllegalArgumentException.class);
+ }
+ }
+ }
+
+ public void testSetDisallowedClass() throws Exception
+ {
+ Message m = getTestMessage();
+ try
+ {
+ m.setObjectProperty("foo", new Object());
+ fail("expected a MessageFormatException");
+ }
+ catch (MessageFormatException e)
+ {
+ // pass
+ }
+ }
+
+ public void testOnce()
+ {
+ runBatch(1);
+ }
+
+ public void test50()
+ {
+ runBatch(50);
+ }
+
+ private void runBatch(int runSize)
+ {
+ try
+ {
+ int run = 0;
+ while (run < runSize)
+ {
+ _logger.error("Run Number:" + run++);
+ try
+ {
+ init( (AMQConnection) getConnection("guest", "guest"));
+ }
+ catch (Exception e)
+ {
+ _logger.error("exception:", e);
+ fail("Unable to initialilse connection: " + e);
+ }
+
+ int count = _count;
+ send(count);
+ waitFor(count);
+ check();
+ _logger.info("Completed without failure");
+
+ Thread.sleep(10);
+ _connection.close();
+
+ _logger.error("End Run Number:" + (run - 1));
+ }
+ }
+ catch (Exception e)
+ {
+ _logger.error(e.getMessage(), e);
+ }
+ }
+
+ void send(int count) throws JMSException
+ {
+ // create a publisher
+ MessageProducer producer = _session.createProducer(_destination);
+ for (int i = 0; i < count; i++)
+ {
+ String text = "Message " + i;
+ messages.add(text);
+ Message m = _session.createTextMessage(text);
+
+ m.setBooleanProperty("Bool", true);
+
+ m.setByteProperty("Byte", (byte) Byte.MAX_VALUE);
+ m.setDoubleProperty("Double", (double) Double.MAX_VALUE);
+ m.setFloatProperty("Float", (float) Float.MAX_VALUE);
+ m.setIntProperty("Int", (int) Integer.MAX_VALUE);
+
+ m.setJMSCorrelationID("Correlation");
+ // fixme the m.setJMSMessage has no effect
+ producer.setPriority(8);
+ m.setJMSPriority(3);
+
+ // Queue
+ Queue q;
+
+ if ((i / 2) == 0)
+ {
+ q = _session.createTemporaryQueue();
+ }
+ else
+ {
+ q = new AMQQueue(_connection, "TestReply");
+ }
+
+ m.setJMSReplyTo(q);
+
+ m.setStringProperty("ReplyToIndex", String.valueOf(i));
+ _replyToDestinations.put(String.valueOf(i), q);
+
+ _logger.debug("Message:" + m);
+
+ m.setJMSType("Test");
+ m.setLongProperty("UnsignedInt", (long) 4294967295L);
+ m.setLongProperty("Long", (long) Long.MAX_VALUE);
+
+ m.setShortProperty("Short", (short) Short.MAX_VALUE);
+ m.setStringProperty("String", "Test");
+
+ _logger.debug("Sending Msg:" + m);
+ producer.send(m);
+ }
+ }
+
+ void waitFor(int count) throws InterruptedException
+ {
+ synchronized (received)
+ {
+ while (received.size() < count)
+ {
+ received.wait();
+ }
+ }
+ }
+
+ void check() throws JMSException, URISyntaxException
+ {
+ List<String> actual = new ArrayList<String>();
+ for (JMSTextMessage m : received)
+ {
+ actual.add(m.getText());
+
+ // Check Properties
+
+ Assert.assertEquals("Check Boolean properties are correctly transported", true, m.getBooleanProperty("Bool"));
+ Assert.assertEquals("Check Byte properties are correctly transported", Byte.MAX_VALUE,
+ m.getByteProperty("Byte"));
+ Assert.assertEquals("Check Double properties are correctly transported", Double.MAX_VALUE,
+ m.getDoubleProperty("Double"), 0d);
+ Assert.assertEquals("Check Float properties are correctly transported", Float.MAX_VALUE,
+ m.getFloatProperty("Float"), 0f);
+ Assert.assertEquals("Check Int properties are correctly transported", Integer.MAX_VALUE,
+ m.getIntProperty("Int"));
+ Assert.assertEquals("Check CorrelationID properties are correctly transported", "Correlation",
+ m.getJMSCorrelationID());
+ Assert.assertEquals("Check Priority properties are correctly transported", 8, m.getJMSPriority());
+
+ // Queue
+ String replyToIndex = m.getStringProperty("ReplyToIndex");
+ Assert.assertEquals("Check ReplyTo properties are correctly transported", _replyToDestinations.get(replyToIndex), m.getJMSReplyTo());
+
+ Assert.assertEquals("Check Type properties are correctly transported", "Test", m.getJMSType());
+
+ Assert.assertEquals("Check Short properties are correctly transported", (short) Short.MAX_VALUE,
+ m.getShortProperty("Short"));
+ Assert.assertEquals("Check UnsignedInt properties are correctly transported", (long) 4294967295L,
+ m.getLongProperty("UnsignedInt"));
+ Assert.assertEquals("Check Long properties are correctly transported", (long) Long.MAX_VALUE,
+ m.getLongProperty("Long"));
+ Assert.assertEquals("Check String properties are correctly transported", "Test", m.getStringProperty("String"));
+
+ //JMSXUserID
+ if (m.getStringProperty("JMSXUserID") != null)
+ {
+ Assert.assertEquals("Check 'JMSXUserID' is supported ", USERNAME,
+ m.getStringProperty("JMSXUserID"));
+ }
+ }
+
+ received.clear();
+
+ assertEqual(messages.iterator(), actual.iterator());
+
+ messages.clear();
+ }
+
+ private static void assertEqual(Iterator expected, Iterator actual)
+ {
+ List<String> errors = new ArrayList<String>();
+ while (expected.hasNext() && actual.hasNext())
+ {
+ try
+ {
+ assertEqual(expected.next(), actual.next());
+ }
+ catch (Exception e)
+ {
+ errors.add(e.getMessage());
+ }
+ }
+ while (expected.hasNext())
+ {
+ errors.add("Expected " + expected.next() + " but no more actual values.");
+ }
+ while (actual.hasNext())
+ {
+ errors.add("Found " + actual.next() + " but no more expected values.");
+ }
+
+ if (!errors.isEmpty())
+ {
+ throw new RuntimeException(errors.toString());
+ }
+ }
+
+ private static void assertEqual(Object expected, Object actual)
+ {
+ if (!expected.equals(actual))
+ {
+ throw new RuntimeException("Expected '" + expected + "' found '" + actual + "'");
+ }
+ }
+
+ public void onMessage(Message message)
+ {
+ synchronized (received)
+ {
+ received.add((JMSTextMessage) message);
+ received.notify();
+ }
+ }
+
+ private static String randomize(String in)
+ {
+ return in + System.currentTimeMillis();
+ }
+
+ public static void main(String[] argv) throws Exception
+ {
+ PropertyValueTest test = new PropertyValueTest();
+ test._connectionString = (argv.length == 0) ? "vm://:1" : argv[0];
+ test.setUp();
+ if (argv.length > 1)
+ {
+ test._count = Integer.parseInt(argv[1]);
+ }
+
+ test.testOnce();
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(PropertyValueTest.class);
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/PubSubTwoConnectionTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/PubSubTwoConnectionTest.java
new file mode 100644
index 0000000000..3ef8524656
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/PubSubTwoConnectionTest.java
@@ -0,0 +1,75 @@
+/*
+ *
+ * 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.qpid.test.unit.basic;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.AMQTopic;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+import javax.jms.Connection;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import javax.jms.Topic;
+
+/**
+ * @author Apache Software Foundation
+ */
+public class PubSubTwoConnectionTest extends QpidBrokerTestCase
+{
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ }
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ }
+
+ /**
+ * This tests that a consumer is set up synchronously
+ * @throws Exception
+ */
+ public void testTwoConnections() throws Exception
+ {
+
+ AMQConnection con1 = (AMQConnection) getConnection("guest", "guest");
+
+ Topic topic = new AMQTopic(con1, "MyTopic");
+
+ Session session1 = con1.createSession(false, AMQSession.NO_ACKNOWLEDGE);
+ MessageProducer producer = session1.createProducer(topic);
+
+ Connection con2 = (AMQConnection) getConnection("guest", "guest") ;
+ Session session2 = con2.createSession(false, AMQSession.NO_ACKNOWLEDGE);
+ MessageConsumer consumer = session2.createConsumer(topic);
+ con2.start();
+ producer.send(session1.createTextMessage("Hello"));
+ TextMessage tm1 = (TextMessage) consumer.receive(2000);
+ assertNotNull(tm1);
+ assertEquals("Hello", tm1.getText());
+ con1.close();
+ con2.close();
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/SessionStartTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/SessionStartTest.java
new file mode 100644
index 0000000000..cc64dbb125
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/SessionStartTest.java
@@ -0,0 +1,115 @@
+/*
+ *
+ * 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.qpid.test.unit.basic;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQDestination;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageListener;
+
+public class SessionStartTest extends QpidBrokerTestCase implements MessageListener
+{
+ private static final Logger _logger = LoggerFactory.getLogger(SessionStartTest.class);
+
+ private AMQConnection _connection;
+ private AMQDestination _destination;
+ private AMQSession _session;
+ private int count;
+ public String _connectionString = "vm://:1";
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ init((AMQConnection) getConnection("guest", "guest"));
+ }
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ }
+
+ private void init(AMQConnection connection) throws Exception
+ {
+ init(connection,
+ new AMQQueue(connection.getDefaultQueueExchangeName(), new AMQShortString(randomize("SessionStartTest")), true));
+ }
+
+ private void init(AMQConnection connection, AMQDestination destination) throws Exception
+ {
+ _connection = connection;
+ _destination = destination;
+ connection.start();
+
+ _session = (AMQSession) connection.createSession(false, AMQSession.NO_ACKNOWLEDGE);
+ _session.createConsumer(destination).setMessageListener(this);
+ }
+
+ public synchronized void test() throws JMSException, InterruptedException
+ {
+ try
+ {
+ _session.createProducer(_destination).send(_session.createTextMessage("Message"));
+ _logger.info("Message sent, waiting for response...");
+ wait(1000);
+ if (count > 0)
+ {
+ _logger.info("Got message");
+ }
+ else
+ {
+ throw new RuntimeException("Did not get message!");
+ }
+ }
+ finally
+ {
+ _session.close();
+ _connection.close();
+ }
+ }
+
+ public synchronized void onMessage(Message message)
+ {
+ count++;
+ notify();
+ }
+
+ private static String randomize(String in)
+ {
+ return in + System.currentTimeMillis();
+ }
+
+ public static void main(String[] argv) throws Exception
+ {
+ SessionStartTest test = new SessionStartTest();
+ test._connectionString = (argv.length == 0) ? "localhost:5672" : argv[0];
+ test.setUp();
+ test.test();
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/TextMessageTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/TextMessageTest.java
new file mode 100644
index 0000000000..d4081817ee
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/TextMessageTest.java
@@ -0,0 +1,246 @@
+/*
+ *
+ * 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.qpid.test.unit.basic;
+
+import org.junit.Assert;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.message.JMSTextMessage;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+import javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageListener;
+import javax.jms.MessageNotWriteableException;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class TextMessageTest extends QpidBrokerTestCase implements MessageListener
+{
+ private static final Logger _logger = LoggerFactory.getLogger(TextMessageTest.class);
+
+ private AMQConnection _connection;
+ private Destination _destination;
+ private AMQSession _session;
+ private final List<JMSTextMessage> received = new ArrayList<JMSTextMessage>();
+ private final List<String> messages = new ArrayList<String>();
+ private int _count = 100;
+ public String _connectionString = "vm://:1";
+ private CountDownLatch _waitForCompletion;
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ try
+ {
+ init((AMQConnection) getConnection("guest", "guest"));
+ }
+ catch (Exception e)
+ {
+ fail("Unable to initialilse connection: " + e);
+ }
+ }
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ }
+
+ private void init(AMQConnection connection) throws Exception
+ {
+ Destination destination =
+ new AMQQueue(connection.getDefaultQueueExchangeName(), new AMQShortString(randomize("TextMessageTest")), true);
+ init(connection, destination);
+ }
+
+ private void init(AMQConnection connection, Destination destination) throws Exception
+ {
+ _connection = connection;
+ _destination = destination;
+ _session = (AMQSession) connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ // set up a slow consumer
+ try
+ {
+ _session.createConsumer(destination).setMessageListener(this);
+ }
+ catch (Throwable e)
+ {
+ _logger.error("Error creating consumer", e);
+ }
+ connection.start();
+ }
+
+ public void test() throws Exception
+ {
+ int count = _count;
+ _waitForCompletion = new CountDownLatch(_count);
+ send(count);
+ _waitForCompletion.await(20, TimeUnit.SECONDS);
+ check();
+ _logger.info("Completed without failure");
+ _connection.close();
+ }
+
+ void send(int count) throws JMSException
+ {
+ // create a publisher
+ MessageProducer producer = _session.createProducer(_destination);
+ for (int i = 0; i < count; i++)
+ {
+ String text = "Message " + i;
+ messages.add(text);
+ Message m = _session.createTextMessage(text);
+ //m.setStringProperty("String", "hello");
+
+ _logger.info("Sending Msg:" + m);
+ producer.send(m);
+ }
+ _logger.info("sent " + count + " mesages");
+ }
+
+
+ void check() throws JMSException
+ {
+ List<String> actual = new ArrayList<String>();
+ for (JMSTextMessage m : received)
+ {
+ actual.add(m.getText());
+
+ // Check body write status
+ try
+ {
+ m.setText("Test text");
+ Assert.fail("Message should not be writeable");
+ }
+ catch (MessageNotWriteableException mnwe)
+ {
+ // normal execution
+ }
+
+ m.clearBody();
+
+ try
+ {
+ m.setText("Test text");
+ }
+ catch (MessageNotWriteableException mnwe)
+ {
+ Assert.fail("Message should be writeable");
+ }
+
+ // Check property write status
+ try
+ {
+ m.setStringProperty("test", "test");
+ Assert.fail("Message should not be writeable");
+ }
+ catch (MessageNotWriteableException mnwe)
+ {
+ // normal execution
+ }
+
+ m.clearProperties();
+
+ try
+ {
+ m.setStringProperty("test", "test");
+ }
+ catch (MessageNotWriteableException mnwe)
+ {
+ Assert.fail("Message should be writeable");
+ }
+
+ }
+
+ assertEqual(messages.iterator(), actual.iterator());
+ }
+
+ private static void assertEqual(Iterator expected, Iterator actual)
+ {
+ List<String> errors = new ArrayList<String>();
+ while (expected.hasNext() && actual.hasNext())
+ {
+ try
+ {
+ assertEqual(expected.next(), actual.next());
+ }
+ catch (Exception e)
+ {
+ errors.add(e.getMessage());
+ }
+ }
+ while (expected.hasNext())
+ {
+ errors.add("Expected " + expected.next() + " but no more actual values.");
+ }
+ while (actual.hasNext())
+ {
+ errors.add("Found " + actual.next() + " but no more expected values.");
+ }
+
+ if (!errors.isEmpty())
+ {
+ throw new RuntimeException(errors.toString());
+ }
+ }
+
+ private static void assertEqual(Object expected, Object actual)
+ {
+ if (!expected.equals(actual))
+ {
+ throw new RuntimeException("Expected '" + expected + "' found '" + actual + "'");
+ }
+ }
+
+ public void onMessage(Message message)
+ {
+ synchronized (received)
+ {
+ _logger.info("===== received one message");
+ received.add((JMSTextMessage) message);
+ _waitForCompletion.countDown();
+ }
+ }
+
+ private static String randomize(String in)
+ {
+ return in + System.currentTimeMillis();
+ }
+
+
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(TextMessageTest.class);
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/close/CloseTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/close/CloseTest.java
new file mode 100644
index 0000000000..48d290c986
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/basic/close/CloseTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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.qpid.test.unit.basic.close;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.Session;
+
+public class CloseTest extends QpidBrokerTestCase
+{
+ private static final Logger _logger = LoggerFactory.getLogger(CloseTest.class);
+
+ public void testCloseQueueReceiver() throws Exception
+ {
+ AMQConnection connection = (AMQConnection) getConnection("guest", "guest");
+
+ Session session = connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ Queue queue = session.createQueue("test-queue");
+ MessageConsumer consumer = session.createConsumer(queue);
+
+ MessageProducer producer_not_used_but_created_for_testing = session.createProducer(queue);
+
+ connection.start();
+
+ _logger.info("About to close consumer");
+
+ consumer.close();
+
+ _logger.info("Closed Consumer");
+ connection.close();
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/AMQSessionTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/AMQSessionTest.java
new file mode 100644
index 0000000000..0d81b66be0
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/AMQSessionTest.java
@@ -0,0 +1,104 @@
+/*
+ *
+ * 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.qpid.test.unit.client;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.AMQTopic;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+import javax.jms.JMSException;
+import javax.jms.QueueReceiver;
+import javax.jms.TopicSubscriber;
+
+/**
+ * Tests for QueueReceiver and TopicSubscriber creation methods on AMQSession
+ */
+public class AMQSessionTest extends QpidBrokerTestCase
+{
+
+ private static AMQSession _session;
+ private static AMQTopic _topic;
+ private static AMQQueue _queue;
+ private static AMQConnection _connection;
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ _connection = (AMQConnection) getConnection("guest", "guest");
+ _topic = new AMQTopic(_connection,"mytopic");
+ _queue = new AMQQueue(_connection,"myqueue");
+ _session = (AMQSession) _connection.createSession(false, AMQSession.NO_ACKNOWLEDGE);
+ }
+
+ protected void tearDown() throws Exception
+ {
+ try
+ {
+ _connection.close();
+ }
+ catch (JMSException e)
+ {
+ //just close
+ }
+ super.tearDown();
+ }
+
+ public void testCreateSubscriber() throws JMSException
+ {
+ TopicSubscriber subscriber = _session.createSubscriber(_topic);
+ assertEquals("Topic names should match from TopicSubscriber", _topic.getTopicName(), subscriber.getTopic().getTopicName());
+
+ subscriber = _session.createSubscriber(_topic, "abc", false);
+ assertEquals("Topic names should match from TopicSubscriber with selector", _topic.getTopicName(), subscriber.getTopic().getTopicName());
+ }
+
+ public void testCreateDurableSubscriber() throws JMSException
+ {
+ TopicSubscriber subscriber = _session.createDurableSubscriber(_topic, "mysubname");
+ assertEquals("Topic names should match from durable TopicSubscriber", _topic.getTopicName(), subscriber.getTopic().getTopicName());
+
+ subscriber = _session.createDurableSubscriber(_topic, "mysubname2", "abc", false);
+ assertEquals("Topic names should match from durable TopicSubscriber with selector", _topic.getTopicName(), subscriber.getTopic().getTopicName());
+ _session.unsubscribe("mysubname");
+ _session.unsubscribe("mysubname2");
+ }
+
+ public void testCreateQueueReceiver() throws JMSException
+ {
+ QueueReceiver receiver = _session.createQueueReceiver(_queue);
+ assertEquals("Queue names should match from QueueReceiver", _queue.getQueueName(), receiver.getQueue().getQueueName());
+
+ receiver = _session.createQueueReceiver(_queue, "abc");
+ assertEquals("Queue names should match from QueueReceiver with selector", _queue.getQueueName(), receiver.getQueue().getQueueName());
+ }
+
+ public void testCreateReceiver() throws JMSException
+ {
+ QueueReceiver receiver = _session.createReceiver(_queue);
+ assertEquals("Queue names should match from QueueReceiver", _queue.getQueueName(), receiver.getQueue().getQueueName());
+
+ receiver = _session.createReceiver(_queue, "abc");
+ assertEquals("Queue names should match from QueueReceiver with selector", _queue.getQueueName(), receiver.getQueue().getQueueName());
+ }
+
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/DynamicQueueExchangeCreateTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/DynamicQueueExchangeCreateTest.java
new file mode 100644
index 0000000000..77df6c58d9
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/DynamicQueueExchangeCreateTest.java
@@ -0,0 +1,258 @@
+/*
+ *
+ * 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.qpid.test.unit.client;
+
+import java.io.IOException;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.configuration.ClientProperties;
+import org.apache.qpid.management.common.mbeans.ManagedExchange;
+import org.apache.qpid.protocol.AMQConstant;
+import org.apache.qpid.test.utils.JMXTestUtils;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+import org.apache.qpid.url.BindingURL;
+
+import javax.jms.Connection;
+import javax.jms.InvalidDestinationException;
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.Session;
+
+public class DynamicQueueExchangeCreateTest extends QpidBrokerTestCase
+{
+ private JMXTestUtils _jmxUtils;
+
+ @Override
+ public void setUp() throws Exception
+ {
+ getBrokerConfiguration().addJmxManagementConfiguration();
+
+ _jmxUtils = new JMXTestUtils(this);
+
+ super.setUp();
+ _jmxUtils.open();
+ }
+
+ @Override
+ public void tearDown() throws Exception
+ {
+ try
+ {
+ if (_jmxUtils != null)
+ {
+ _jmxUtils.close();
+ }
+ }
+ finally
+ {
+ super.tearDown();
+ }
+ }
+
+ /*
+ * Tests to validate that setting the respective qpid.declare_queues,
+ * qpid.declare_exchanges system properties functions as expected.
+ */
+
+ public void testQueueNotDeclaredDuringConsumerCreation() throws Exception
+ {
+ setSystemProperty(ClientProperties.QPID_DECLARE_QUEUES_PROP_NAME, "false");
+
+ Connection connection = getConnection();
+
+ Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ Queue queue = session.createQueue(getTestQueueName());
+
+ try
+ {
+ session.createConsumer(queue);
+ fail("JMSException should be thrown as the queue does not exist");
+ }
+ catch (JMSException e)
+ {
+ checkExceptionErrorCode(e, AMQConstant.NOT_FOUND);
+ }
+ }
+
+ public void testExchangeNotDeclaredDuringConsumerCreation() throws Exception
+ {
+ setSystemProperty(ClientProperties.QPID_DECLARE_EXCHANGES_PROP_NAME, "false");
+
+ Connection connection = getConnection();
+
+ Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ String exchangeName = getTestQueueName();
+ Queue queue = session.createQueue("direct://" + exchangeName + "/queue/queue");
+
+ try
+ {
+ session.createConsumer(queue);
+ fail("JMSException should be thrown as the exchange does not exist");
+ }
+ catch (JMSException e)
+ {
+ checkExceptionErrorCode(e, AMQConstant.NOT_FOUND);
+ }
+
+ //verify the exchange was not declared
+ String exchangeObjectName = _jmxUtils.getExchangeObjectName("test", exchangeName);
+ assertFalse("exchange should not exist", _jmxUtils.doesManagedObjectExist(exchangeObjectName));
+ }
+
+ /**
+ * Checks that setting {@value ClientProperties#QPID_DECLARE_EXCHANGES_PROP_NAME} false results in
+ * disabling implicit ExchangeDeclares during producer creation when using a {@link BindingURL}
+ */
+ public void testExchangeNotDeclaredDuringProducerCreation() throws Exception
+ {
+ Connection connection = getConnection();
+ Session session1 = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ String exchangeName1 = getTestQueueName() + "1";
+
+
+ Queue queue = session1.createQueue("direct://" + exchangeName1 + "/queue/queue");
+ session1.createProducer(queue);
+
+ //close the session to ensure any previous commands were fully processed by
+ //the broker before observing their effect
+ session1.close();
+
+ //verify the exchange was declared
+ String exchangeObjectName = _jmxUtils.getExchangeObjectName("test", exchangeName1);
+ assertTrue("exchange should exist", _jmxUtils.doesManagedObjectExist(exchangeObjectName));
+
+ //Now disable the implicit exchange declares and try again
+ setSystemProperty(ClientProperties.QPID_DECLARE_EXCHANGES_PROP_NAME, "false");
+
+ Session session2 = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ String exchangeName2 = getTestQueueName() + "2";
+
+ Queue queue2 = session2.createQueue("direct://" + exchangeName2 + "/queue/queue");
+ session2.createProducer(queue2);
+
+ //close the session to ensure any previous commands were fully processed by
+ //the broker before observing their effect
+ session2.close();
+
+ //verify the exchange was not declared
+ String exchangeObjectName2 = _jmxUtils.getExchangeObjectName("test", exchangeName2);
+ assertFalse("exchange should not exist", _jmxUtils.doesManagedObjectExist(exchangeObjectName2));
+ }
+
+ public void testQueueNotBoundDuringConsumerCreation() throws Exception
+ {
+ setSystemProperty(ClientProperties.QPID_BIND_QUEUES_PROP_NAME, "false");
+ setSystemProperty(ClientProperties.VERIFY_QUEUE_ON_SEND, "true");
+
+ Connection connection = getConnection();
+
+ Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ Queue queue = session.createQueue(getTestQueueName());
+ session.createConsumer(queue);
+
+ try
+ {
+ session.createProducer(queue).send(session.createMessage());
+ fail("JMSException should be thrown as the queue does not exist");
+ }
+ catch (InvalidDestinationException ide)
+ {
+ //PASS
+ }
+ }
+ private void checkExceptionErrorCode(JMSException original, AMQConstant code)
+ {
+ Exception linked = original.getLinkedException();
+ assertNotNull("Linked exception should have been set", linked);
+ assertTrue("Linked exception should be an AMQException", linked instanceof AMQException);
+ assertEquals("Error code should be " + code.getCode(), code, ((AMQException) linked).getErrorCode());
+ }
+
+ /*
+ * Tests to validate that the custom exchanges declared by the client during
+ * consumer and producer creation have the expected properties.
+ */
+
+ public void testPropertiesOfCustomExchangeDeclaredDuringProducerCreation() throws Exception
+ {
+ implTestPropertiesOfCustomExchange(true, false);
+ }
+
+ public void testPropertiesOfCustomExchangeDeclaredDuringConsumerCreation() throws Exception
+ {
+ implTestPropertiesOfCustomExchange(false, true);
+ }
+
+ private void implTestPropertiesOfCustomExchange(boolean createProducer, boolean createConsumer) throws Exception
+ {
+ Connection connection = getConnection();
+
+ Session session1 = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ String exchangeName1 = getTestQueueName() + "1";
+ String queueName1 = getTestQueueName() + "1";
+
+ Queue queue = session1.createQueue("direct://" + exchangeName1 + "/" + queueName1 + "/" + queueName1 + "?" + BindingURL.OPTION_EXCHANGE_AUTODELETE + "='true'");
+ if(createProducer)
+ {
+ session1.createProducer(queue);
+ }
+
+ if(createConsumer)
+ {
+ session1.createConsumer(queue);
+ }
+ session1.close();
+
+ //verify the exchange was declared to expectation
+ verifyDeclaredExchange(exchangeName1, true, false);
+
+ Session session2 = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ String exchangeName2 = getTestQueueName() + "2";
+ String queueName2 = getTestQueueName() + "2";
+
+ Queue queue2 = session2.createQueue("direct://" + exchangeName2 + "/" + queueName2 + "/" + queueName2 + "?" + BindingURL.OPTION_EXCHANGE_DURABLE + "='true'");
+ if(createProducer)
+ {
+ session2.createProducer(queue2);
+ }
+
+ if(createConsumer)
+ {
+ session2.createConsumer(queue2);
+ }
+ session2.close();
+
+ //verify the exchange was declared to expectation
+ verifyDeclaredExchange(exchangeName2, false, true);
+ }
+
+ private void verifyDeclaredExchange(String exchangeName, boolean isAutoDelete, boolean isDurable) throws IOException
+ {
+ String exchangeObjectName = _jmxUtils.getExchangeObjectName("test", exchangeName);
+ assertTrue("exchange should exist", _jmxUtils.doesManagedObjectExist(exchangeObjectName));
+ ManagedExchange exchange = _jmxUtils.getManagedExchange(exchangeName);
+ assertEquals(isAutoDelete, exchange.isAutoDelete());
+ assertEquals(isDurable,exchange.isDurable());
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/MaxDeliveryCountTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/MaxDeliveryCountTest.java
new file mode 100644
index 0000000000..5e1e38106a
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/MaxDeliveryCountTest.java
@@ -0,0 +1,660 @@
+/*
+ *
+ * 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.qpid.test.unit.client;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import javax.jms.Connection;
+import javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageListener;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import javax.jms.Topic;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.client.AMQDestination;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.RejectBehaviour;
+import org.apache.qpid.configuration.ClientProperties;
+import org.apache.qpid.server.virtualhost.AbstractVirtualHost;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+import org.apache.qpid.test.utils.TestBrokerConfiguration;
+
+/**
+ * Test that the MaxRedelivery feature works as expected, allowing the client to reject
+ * messages during rollback/recover whilst specifying they not be requeued if delivery
+ * to an application has been attempted a specified number of times.
+ *
+ * General approach: specify a set of messages which will cause the test client to then
+ * deliberately rollback/recover the session after consuming, and monitor that they are
+ * re-delivered the specified number of times before the client rejects them without requeue
+ * and then verify that they are not subsequently redelivered.
+ *
+ * Additionally, the queue used in the test is configured for DLQ'ing, and the test verifies
+ * that the messages rejected without requeue are then present on the appropriate DLQ.
+ */
+public class MaxDeliveryCountTest extends QpidBrokerTestCase
+{
+ private static final Logger _logger = Logger.getLogger(MaxDeliveryCountTest.class);
+ private boolean _failed;
+ private String _failMsg;
+ private static final int MSG_COUNT = 15;
+ private static final int MAX_DELIVERY_COUNT = 2;
+ private CountDownLatch _awaitCompletion;
+
+ /** index numbers of messages to be redelivered */
+ private final List<Integer> _redeliverMsgs = Arrays.asList(1, 2, 5, 14);
+
+ public void setUp() throws Exception
+ {
+ //enable DLQ/maximumDeliveryCount support for all queues at the vhost level
+
+ TestBrokerConfiguration brokerConfiguration = getBrokerConfiguration();
+ setTestSystemProperty("queue.deadLetterQueueEnabled","true");
+ setTestSystemProperty("queue.maximumDeliveryAttempts", String.valueOf(MAX_DELIVERY_COUNT));
+
+ //Ensure management is on
+ brokerConfiguration.addJmxManagementConfiguration();
+
+ // Set client-side flag to allow the server to determine if messages
+ // dead-lettered or requeued.
+ if (!isBroker010())
+ {
+ setTestClientSystemProperty(ClientProperties.REJECT_BEHAVIOUR_PROP_NAME, RejectBehaviour.SERVER.toString());
+ }
+ super.setUp();
+
+ boolean durableSub = isDurSubTest();
+
+ //declare the test queue
+ Connection consumerConnection = getConnection();
+ Session consumerSession = consumerConnection.createSession(false,Session.AUTO_ACKNOWLEDGE);
+ Destination destination = getDestination(consumerSession, durableSub);
+ if(durableSub)
+ {
+ consumerSession.createDurableSubscriber((Topic)destination, getName()).close();
+ }
+ else
+ {
+ consumerSession.createConsumer(destination).close();
+ }
+
+ consumerConnection.close();
+
+ //Create Producer put some messages on the queue
+ Connection producerConnection = getConnection();
+ producerConnection.start();
+ Session producerSession = producerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ MessageProducer producer = producerSession.createProducer(getDestination(producerSession, durableSub));
+
+ for (int count = 1; count <= MSG_COUNT; count++)
+ {
+ Message msg = producerSession.createTextMessage(generateContent(count));
+ msg.setIntProperty("count", count);
+ producer.send(msg);
+ }
+
+ producerConnection.close();
+
+ _failed = false;
+ _awaitCompletion = new CountDownLatch(1);
+ }
+
+ private Destination getDestination(Session consumerSession, boolean durableSub) throws JMSException
+ {
+ if(durableSub)
+ {
+ return consumerSession.createTopic(getTestQueueName());
+ }
+ else
+ {
+ return consumerSession.createQueue(getTestQueueName());
+ }
+ }
+
+ private String generateContent(int count)
+ {
+ return "Message " + count + " content.";
+ }
+
+ /**
+ * Test that Max Redelivery is enforced when using onMessage() on a
+ * Client-Ack session.
+ */
+ public void testAsynchronousClientAckSession() throws Exception
+ {
+ doTest(Session.CLIENT_ACKNOWLEDGE, _redeliverMsgs, false, false);
+ }
+
+ /**
+ * Test that Max Redelivery is enforced when using onMessage() on a
+ * transacted session.
+ */
+ public void testAsynchronousTransactedSession() throws Exception
+ {
+ doTest(Session.SESSION_TRANSACTED, _redeliverMsgs, false, false);
+ }
+
+ /**
+ * Test that Max Redelivery is enforced when using onMessage() on an
+ * Auto-Ack session.
+ */
+ public void testAsynchronousAutoAckSession() throws Exception
+ {
+ doTest(Session.AUTO_ACKNOWLEDGE, _redeliverMsgs, false, false);
+ }
+
+ /**
+ * Test that Max Redelivery is enforced when using onMessage() on a
+ * Dups-OK session.
+ */
+ public void testAsynchronousDupsOkSession() throws Exception
+ {
+ doTest(Session.DUPS_OK_ACKNOWLEDGE, _redeliverMsgs, false, false);
+ }
+
+ /**
+ * Test that Max Redelivery is enforced when using recieve() on a
+ * Client-Ack session.
+ */
+ public void testSynchronousClientAckSession() throws Exception
+ {
+ doTest(Session.CLIENT_ACKNOWLEDGE, _redeliverMsgs, true, false);
+ }
+
+ /**
+ * Test that Max Redelivery is enforced when using recieve() on a
+ * transacted session.
+ */
+ public void testSynchronousTransactedSession() throws Exception
+ {
+ doTest(Session.SESSION_TRANSACTED, _redeliverMsgs, true, false);
+ }
+
+ public void testDurableSubscription() throws Exception
+ {
+ doTest(Session.SESSION_TRANSACTED, _redeliverMsgs, false, true);
+ }
+
+ public void testWhenBrokerIsRestartedAfterEnqeuingMessages() throws Exception
+ {
+ restartBroker();
+
+ doTest(Session.SESSION_TRANSACTED, _redeliverMsgs, true, false);
+ }
+
+ private void doTest(final int deliveryMode, final List<Integer> redeliverMsgs, final boolean synchronous, final boolean durableSub) throws Exception
+ {
+ final Connection clientConnection = getConnection();
+
+ final boolean transacted = deliveryMode == Session.SESSION_TRANSACTED ? true : false;
+ final Session clientSession = clientConnection.createSession(transacted, deliveryMode);
+
+ MessageConsumer consumer;
+ Destination dest = getDestination(clientSession, durableSub);
+ AMQQueue checkQueue;
+ if(durableSub)
+ {
+ consumer = clientSession.createDurableSubscriber((Topic)dest, getName());
+ checkQueue = new AMQQueue("amq.topic", "clientid" + ":" + getName());
+ }
+ else
+ {
+ consumer = clientSession.createConsumer(dest);
+ checkQueue = (AMQQueue) dest;
+ }
+
+ assertEquals("The queue should have " + MSG_COUNT + " msgs at start",
+ MSG_COUNT, ((AMQSession<?,?>) clientSession).getQueueDepth(checkQueue));
+
+ clientConnection.start();
+
+ int expectedDeliveries = MSG_COUNT + ((MAX_DELIVERY_COUNT -1) * redeliverMsgs.size());
+
+ if(synchronous)
+ {
+ doSynchronousTest(clientSession, consumer, clientSession.getAcknowledgeMode(),
+ MAX_DELIVERY_COUNT, expectedDeliveries, redeliverMsgs);
+ }
+ else
+ {
+ addMessageListener(clientSession, consumer, clientSession.getAcknowledgeMode(),
+ MAX_DELIVERY_COUNT, expectedDeliveries, redeliverMsgs);
+
+ try
+ {
+ if (!_awaitCompletion.await(20, TimeUnit.SECONDS))
+ {
+ fail("Test did not complete in 20 seconds.");
+ }
+ }
+ catch (InterruptedException e)
+ {
+ fail("Unable to wait for test completion");
+ throw e;
+ }
+
+ if(_failed)
+ {
+ fail(_failMsg);
+ }
+ }
+ consumer.close();
+
+ //check the source queue is now empty
+ assertEquals("The queue should have 0 msgs left", 0, ((AMQSession<?,?>) clientSession).getQueueDepth(checkQueue, true));
+
+ //check the DLQ has the required number of rejected-without-requeue messages
+ verifyDLQdepth(redeliverMsgs.size(), clientSession, durableSub);
+
+ if(isBrokerStorePersistent())
+ {
+ //restart the broker to verify persistence of the DLQ and the messages on it
+ clientConnection.close();
+
+ restartBroker();
+
+ final Connection clientConnection2 = getConnection();
+ clientConnection2.start();
+
+ //verify the messages on the DLQ
+ verifyDLQcontent(clientConnection2, redeliverMsgs, getTestQueueName(), durableSub);
+ clientConnection2.close();
+ }
+ else
+ {
+
+ //verify the messages on the DLQ
+ verifyDLQcontent(clientConnection, redeliverMsgs, getTestQueueName(), durableSub);
+ clientConnection.close();
+ }
+
+ }
+
+ private void verifyDLQdepth(int expected, Session clientSession, boolean durableSub) throws AMQException
+ {
+ AMQDestination checkQueueDLQ;
+ if(durableSub)
+ {
+ checkQueueDLQ = new AMQQueue("amq.topic", "clientid" + ":" + getName() + AbstractVirtualHost.DEFAULT_DLQ_NAME_SUFFIX);
+ }
+ else
+ {
+ checkQueueDLQ = new AMQQueue("amq.direct", getTestQueueName() + AbstractVirtualHost.DEFAULT_DLQ_NAME_SUFFIX);
+ }
+
+ assertEquals("The DLQ should have " + expected + " msgs on it", expected,
+ ((AMQSession<?,?>) clientSession).getQueueDepth(checkQueueDLQ, true));
+ }
+
+ private void verifyDLQcontent(Connection clientConnection, List<Integer> redeliverMsgs, String destName, boolean durableSub) throws JMSException
+ {
+ Session clientSession = clientConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ MessageConsumer consumer;
+ if(durableSub)
+ {
+ consumer = clientSession.createConsumer(clientSession.createQueue("clientid:" +getName() + AbstractVirtualHost.DEFAULT_DLQ_NAME_SUFFIX));
+ }
+ else
+ {
+ consumer = clientSession.createConsumer(
+ clientSession.createQueue(destName + AbstractVirtualHost.DEFAULT_DLQ_NAME_SUFFIX));
+ }
+
+ //keep track of the message we expect to still be on the DLQ
+ List<Integer> outstandingMessages = new ArrayList<Integer>(redeliverMsgs);
+ int numMsg = outstandingMessages.size();
+
+ for(int i = 0; i < numMsg; i++)
+ {
+ Message message = consumer.receive(250);
+
+ assertNotNull("failed to consume expected message " + i + " from DLQ", message);
+ assertTrue("message " + i + " was the wrong type", message instanceof TextMessage);
+
+ //using Integer here to allow removing the value from the list, using int
+ //would instead result in removal of the element at that index
+ Integer msgId = message.getIntProperty("count");
+
+ TextMessage txt = (TextMessage) message;
+ _logger.info("Received message " + msgId + " at " + i + " from the DLQ: " + txt.getText());
+
+ assertTrue("message " + i + " was not one of those which should have been on the DLQ",
+ redeliverMsgs.contains(msgId));
+ assertTrue("message " + i + " was not one of those expected to still be on the DLQ",
+ outstandingMessages.contains(msgId));
+ assertEquals("Message " + i + " content was not as expected", generateContent(msgId), txt.getText());
+
+ //remove from the list of outstanding msgs
+ outstandingMessages.remove(msgId);
+ }
+
+ if(outstandingMessages.size() > 0)
+ {
+ String failures = "";
+ for(Integer msg : outstandingMessages)
+ {
+ failures = failures.concat(msg + " ");
+ }
+ fail("some DLQ'd messages were not found on the DLQ: " + failures);
+ }
+ }
+
+ private void addMessageListener(final Session session, final MessageConsumer consumer, final int deliveryMode, final int maxDeliveryCount,
+ final int expectedTotalNumberOfDeliveries, final List<Integer> redeliverMsgs) throws JMSException
+ {
+ if(deliveryMode == org.apache.qpid.jms.Session.NO_ACKNOWLEDGE
+ || deliveryMode == org.apache.qpid.jms.Session.PRE_ACKNOWLEDGE)
+ {
+ failAsyncTest("Max Delivery feature is not supported with this acknowledgement mode" +
+ "when using asynchronous message delivery.");
+ }
+
+ consumer.setMessageListener(new MessageListener()
+ {
+ private int _deliveryAttempts = 0; //number of times given message(s) have been seen
+ private int _numMsgsToBeRedelivered = 0; //number of messages to rollback/recover
+ private int _totalNumDeliveries = 0;
+ private int _expectedMessage = 1;
+
+ public void onMessage(Message message)
+ {
+ if(_failed || _awaitCompletion.getCount() == 0L)
+ {
+ //don't process anything else
+ return;
+ }
+
+ _totalNumDeliveries++;
+
+ if (message == null)
+ {
+ failAsyncTest("Should not get null messages");
+ return;
+ }
+
+ try
+ {
+ int msgId = message.getIntProperty("count");
+
+ _logger.info("Received message: " + msgId);
+
+ //check the message is the one we expected
+ if(_expectedMessage != msgId)
+ {
+ failAsyncTest("Expected message " + _expectedMessage + " , got message " + msgId);
+ return;
+ }
+
+ _expectedMessage++;
+
+ //keep track of the overall deliveries to ensure we don't see more than expected
+ if(_totalNumDeliveries > expectedTotalNumberOfDeliveries)
+ {
+ failAsyncTest("Expected total of " + expectedTotalNumberOfDeliveries +
+ " message deliveries, reached " + _totalNumDeliveries);
+ }
+
+ //check if this message is one chosen to be rolled back / recovered
+ if(redeliverMsgs.contains(msgId))
+ {
+ _numMsgsToBeRedelivered++;
+
+ //check if next message is going to be rolled back / recovered too
+ if(redeliverMsgs.contains(msgId +1))
+ {
+ switch(deliveryMode)
+ {
+ case Session.SESSION_TRANSACTED:
+ //skip on to next message immediately
+ return;
+ case Session.CLIENT_ACKNOWLEDGE:
+ //skip on to next message immediately
+ return;
+ case Session.DUPS_OK_ACKNOWLEDGE:
+ //fall through
+ case Session.AUTO_ACKNOWLEDGE:
+ //must recover session now or onMessage will ack, so
+ //just fall through the if
+ break;
+ }
+ }
+
+ _deliveryAttempts++; //increment count of times the current rolled back/recovered message(s) have been seen
+
+ _logger.debug("ROLLBACK/RECOVER");
+ switch(deliveryMode)
+ {
+ case Session.SESSION_TRANSACTED:
+ session.rollback();
+ break;
+ case Session.CLIENT_ACKNOWLEDGE:
+ //fall through
+ case Session.DUPS_OK_ACKNOWLEDGE:
+ //fall through
+ case Session.AUTO_ACKNOWLEDGE:
+ session.recover();
+ break;
+ }
+
+ if( _deliveryAttempts >= maxDeliveryCount)
+ {
+ //the client should have rejected the latest messages upon then
+ //above recover/rollback, adjust counts to compensate
+ _deliveryAttempts = 0;
+ }
+ else
+ {
+ //the message(s) should be redelivered, adjust expected message
+ _expectedMessage -= _numMsgsToBeRedelivered;
+ }
+ _logger.debug("XXX _expectedMessage: " + _expectedMessage + " _deliveryAttempts : " + _deliveryAttempts + " _numMsgsToBeRedelivered=" + _numMsgsToBeRedelivered);
+ //reset count of messages expected to be redelivered
+ _numMsgsToBeRedelivered = 0;
+ }
+ else
+ {
+ //consume the message
+ switch(deliveryMode)
+ {
+ case Session.SESSION_TRANSACTED:
+ session.commit();
+ break;
+ case Session.CLIENT_ACKNOWLEDGE:
+ message.acknowledge();
+ break;
+ case Session.DUPS_OK_ACKNOWLEDGE:
+ //fall-through
+ case Session.AUTO_ACKNOWLEDGE:
+ //do nothing, onMessage will ack on exit.
+ break;
+ }
+ }
+
+ if (msgId == MSG_COUNT)
+ {
+ //if this is the last message let the test complete.
+ if (expectedTotalNumberOfDeliveries == _totalNumDeliveries)
+ {
+ _awaitCompletion.countDown();
+ }
+ else
+ {
+ failAsyncTest("Last message received, but we have not had the " +
+ "expected number of total delivieres. Received " + _totalNumDeliveries + " Expecting : " + expectedTotalNumberOfDeliveries);
+ }
+ }
+ }
+ catch (JMSException e)
+ {
+ failAsyncTest(e.getMessage());
+ }
+ }
+ });
+ }
+
+ private void failAsyncTest(String msg)
+ {
+ _logger.error("Failing test because: " + msg);
+ _failMsg = msg;
+ _failed = true;
+ _awaitCompletion.countDown();
+ }
+
+ private void doSynchronousTest(final Session session, final MessageConsumer consumer, final int deliveryMode, final int maxDeliveryCount,
+ final int expectedTotalNumberOfDeliveries, final List<Integer> redeliverMsgs) throws JMSException, AMQException, InterruptedException
+ {
+ if(deliveryMode == Session.AUTO_ACKNOWLEDGE
+ || deliveryMode == Session.DUPS_OK_ACKNOWLEDGE
+ || deliveryMode == org.apache.qpid.jms.Session.PRE_ACKNOWLEDGE
+ || deliveryMode == org.apache.qpid.jms.Session.NO_ACKNOWLEDGE)
+ {
+ fail("Max Delivery feature is not supported with this acknowledgement mode" +
+ "when using synchronous message delivery.");
+ }
+
+ int _deliveryAttempts = 0; //number of times given message(s) have been seen
+ int _numMsgsToBeRedelivered = 0; //number of messages to rollback/recover
+ int _totalNumDeliveries = 0;
+ int _expectedMessage = 1;
+
+ while(!_failed)
+ {
+ Message message = consumer.receive(1000);
+
+ _totalNumDeliveries++;
+
+ if (message == null)
+ {
+ fail("Should not get null messages");
+ return;
+ }
+
+ try
+ {
+ int msgId = message.getIntProperty("count");
+
+ _logger.info("Received message: " + msgId);
+
+ //check the message is the one we expected
+ assertEquals("Unexpected message.", _expectedMessage, msgId);
+
+ _expectedMessage++;
+
+ //keep track of the overall deliveries to ensure we don't see more than expected
+ assertTrue("Exceeded expected total number of deliveries.",
+ _totalNumDeliveries <= expectedTotalNumberOfDeliveries );
+
+ //check if this message is one chosen to be rolled back / recovered
+ if(redeliverMsgs.contains(msgId))
+ {
+ //keep track of the number of messages we will have redelivered
+ //upon rollback/recover
+ _numMsgsToBeRedelivered++;
+
+ if(redeliverMsgs.contains(msgId +1))
+ {
+ //next message is going to be rolled back / recovered too.
+ //skip ahead to it
+ continue;
+ }
+
+ _deliveryAttempts++; //increment count of times the current rolled back/recovered message(s) have been seen
+
+ switch(deliveryMode)
+ {
+ case Session.SESSION_TRANSACTED:
+ session.rollback();
+ break;
+ case Session.CLIENT_ACKNOWLEDGE:
+ session.recover();
+
+ //sleep then do a synchronous op to give the broker
+ //time to resend all the messages
+ Thread.sleep(500);
+ ((AMQSession<?,?>) session).sync();
+ break;
+ }
+
+ if( _deliveryAttempts >= maxDeliveryCount)
+ {
+ //the client should have rejected the latest messages upon then
+ //above recover/rollback, adjust counts to compensate
+ _deliveryAttempts = 0;
+ }
+ else
+ {
+ //the message(s) should be redelivered, adjust expected message
+ _expectedMessage -= _numMsgsToBeRedelivered;
+ }
+
+ //As we just rolled back / recovered, we must reset the
+ //count of messages expected to be redelivered
+ _numMsgsToBeRedelivered = 0;
+ }
+ else
+ {
+ //consume the message
+ switch(deliveryMode)
+ {
+ case Session.SESSION_TRANSACTED:
+ session.commit();
+ break;
+ case Session.CLIENT_ACKNOWLEDGE:
+ message.acknowledge();
+ break;
+ }
+ }
+
+ if (msgId == MSG_COUNT)
+ {
+ //if this is the last message let the test complete.
+ assertTrue("Last message received, but we have not had the " +
+ "expected number of total delivieres",
+ expectedTotalNumberOfDeliveries == _totalNumDeliveries);
+
+ break;
+ }
+ }
+ catch (JMSException e)
+ {
+ fail(e.getMessage());
+ }
+ }
+ }
+
+ private boolean isDurSubTest()
+ {
+ return getTestQueueName().contains("DurableSubscription");
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/QueueSessionFactoryTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/QueueSessionFactoryTest.java
new file mode 100644
index 0000000000..370e44b3d5
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/QueueSessionFactoryTest.java
@@ -0,0 +1,113 @@
+/*
+ *
+ * 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.qpid.test.unit.client;
+
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+import javax.jms.QueueConnection;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.jms.Topic;
+import javax.jms.TopicSession;
+
+/**
+ * Ensures that queue specific session factory method {@link QueueConnection#createQueueSession()} create sessions
+ * of type {@link QueueSession} and that those sessions correctly restrict the available JMS operations
+ * operations to exclude those applicable to only topics.
+ *
+ * @see TopicSessionFactoryTest
+ */
+public class QueueSessionFactoryTest extends QpidBrokerTestCase
+{
+ public void testQueueSessionIsNotATopicSession() throws Exception
+ {
+ QueueSession queueSession = getQueueSession();
+ assertFalse(queueSession instanceof TopicSession);
+ }
+
+ public void testQueueSessionCannotCreateTemporaryTopics() throws Exception
+ {
+ QueueSession queueSession = getQueueSession();
+ try
+ {
+ queueSession.createTemporaryTopic();
+ fail("expected exception did not occur");
+ }
+ catch (javax.jms.IllegalStateException s)
+ {
+ // PASS
+ assertEquals("Cannot call createTemporaryTopic from QueueSession", s.getMessage());
+ }
+ }
+
+ public void testQueueSessionCannotCreateTopics() throws Exception
+ {
+ QueueSession queueSession = getQueueSession();
+ try
+ {
+ queueSession.createTopic("abc");
+ fail("expected exception did not occur");
+ }
+ catch (javax.jms.IllegalStateException s)
+ {
+ // PASS
+ assertEquals("Cannot call createTopic from QueueSession", s.getMessage());
+ }
+ }
+
+ public void testQueueSessionCannotCreateDurableSubscriber() throws Exception
+ {
+ QueueSession queueSession = getQueueSession();
+ Topic topic = getTestTopic();
+
+ try
+ {
+ queueSession.createDurableSubscriber(topic, "abc");
+ fail("expected exception did not occur");
+ }
+ catch (javax.jms.IllegalStateException s)
+ {
+ // PASS
+ assertEquals("Cannot call createDurableSubscriber from QueueSession", s.getMessage());
+ }
+ }
+
+ public void testQueueSessionCannoutUnsubscribe() throws Exception
+ {
+ QueueSession queueSession = getQueueSession();
+ try
+ {
+ queueSession.unsubscribe("abc");
+ fail("expected exception did not occur");
+ }
+ catch (javax.jms.IllegalStateException s)
+ {
+ // PASS
+ assertEquals("Cannot call unsubscribe from QueueSession", s.getMessage());
+ }
+ }
+
+ private QueueSession getQueueSession() throws Exception
+ {
+ QueueConnection queueConnection = (QueueConnection)getConnection();
+ return queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/TopicSessionFactoryTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/TopicSessionFactoryTest.java
new file mode 100644
index 0000000000..ce15d452ab
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/TopicSessionFactoryTest.java
@@ -0,0 +1,98 @@
+/*
+ *
+ * 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.qpid.test.unit.client;
+
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+import javax.jms.Queue;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.jms.TopicConnection;
+import javax.jms.TopicSession;
+
+/**
+ * Ensures that topic specific session factory method {@link TopicConnection#createTopicSession()} create sessions
+ * of type {@link TopicSession} and that those sessions correctly restrict the available JMS operations
+ * operations to exclude those applicable to only queues.
+ *
+ * @see QueueSessionFactoryTest
+ */
+public class TopicSessionFactoryTest extends QpidBrokerTestCase
+{
+ public void testTopicSessionIsNotAQueueSession() throws Exception
+ {
+ TopicSession topicSession = getTopicSession();
+ assertFalse(topicSession instanceof QueueSession);
+ }
+
+ public void testTopicSessionCannotCreateCreateBrowser() throws Exception
+ {
+ TopicSession topicSession = getTopicSession();
+ Queue queue = getTestQueue();
+ try
+ {
+ topicSession.createBrowser(queue);
+ fail("expected exception did not occur");
+ }
+ catch (javax.jms.IllegalStateException s)
+ {
+ // PASS
+ assertEquals("Cannot call createBrowser from TopicSession", s.getMessage());
+ }
+ }
+
+ public void testTopicSessionCannotCreateQueues() throws Exception
+ {
+ TopicSession topicSession = getTopicSession();
+ try
+ {
+ topicSession.createQueue("abc");
+ fail("expected exception did not occur");
+ }
+ catch (javax.jms.IllegalStateException s)
+ {
+ // PASS
+ assertEquals("Cannot call createQueue from TopicSession", s.getMessage());
+ }
+ }
+
+ public void testTopicSessionCannotCreateTemporaryQueues() throws Exception
+ {
+ TopicSession topicSession = getTopicSession();
+ try
+ {
+ topicSession.createTemporaryQueue();
+ fail("expected exception did not occur");
+ }
+ catch (javax.jms.IllegalStateException s)
+ {
+ // PASS
+ assertEquals("Cannot call createTemporaryQueue from TopicSession", s.getMessage());
+ }
+ }
+
+ private TopicSession getTopicSession() throws Exception
+ {
+ TopicConnection topicConnection = (TopicConnection)getConnection();
+ return topicConnection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
+ }
+
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/channelclose/CloseWithBlockingReceiveTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/channelclose/CloseWithBlockingReceiveTest.java
new file mode 100644
index 0000000000..58f1bfe372
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/channelclose/CloseWithBlockingReceiveTest.java
@@ -0,0 +1,73 @@
+/*
+ *
+ * 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.qpid.test.unit.client.channelclose;
+
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+import javax.jms.Connection;
+import javax.jms.Destination;
+import javax.jms.MessageConsumer;
+import javax.jms.Session;
+
+/**
+ * @author Apache Software Foundation
+ */
+public class CloseWithBlockingReceiveTest extends QpidBrokerTestCase
+{
+
+
+ public void testReceiveReturnsNull() throws Exception
+ {
+ final Connection connection = getConnection();
+ Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ Destination destination = session.createQueue(getTestQueueName());
+ MessageConsumer consumer = session.createConsumer(destination);
+ connection.start();
+
+ Runnable r = new Runnable()
+ {
+
+ public void run()
+ {
+ try
+ {
+ Thread.sleep(1000);
+ connection.close();
+ }
+ catch (Exception e)
+ {
+ }
+ }
+ };
+ long startTime = System.currentTimeMillis();
+ Thread thread = new Thread(r);
+ thread.start();
+ try
+ {
+ consumer.receive(10000);
+ assertTrue(System.currentTimeMillis() - startTime < 10000);
+ }
+ finally
+ {
+ thread.join();
+ }
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/connection/BrokerClosesClientConnectionTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/connection/BrokerClosesClientConnectionTest.java
new file mode 100644
index 0000000000..4a92728d82
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/connection/BrokerClosesClientConnectionTest.java
@@ -0,0 +1,215 @@
+/*
+ *
+ * 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.qpid.test.unit.client.connection;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageListener;
+import javax.jms.MessageProducer;
+import javax.naming.NamingException;
+import org.apache.qpid.AMQConnectionClosedException;
+import org.apache.qpid.AMQDisconnectedException;
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+import org.apache.qpid.transport.ConnectionException;
+
+import javax.jms.Connection;
+import javax.jms.ExceptionListener;
+import javax.jms.JMSException;
+import javax.jms.Session;
+
+/**
+ * Tests the behaviour of the client when the Broker terminates client connection
+ * by the Broker being shutdown gracefully or otherwise.
+ *
+ * @see ManagedConnectionMBeanTest
+ */
+public class BrokerClosesClientConnectionTest extends QpidBrokerTestCase
+{
+ private Connection _connection;
+ private boolean _isExternalBroker;
+ private final RecordingExceptionListener _recordingExceptionListener = new RecordingExceptionListener();
+ private Session _session;
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+
+ _connection = getConnection();
+ _session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ _connection.setExceptionListener(_recordingExceptionListener);
+
+ _isExternalBroker = isExternalBroker();
+ }
+
+ public void testClientCloseOnNormalBrokerShutdown() throws Exception
+ {
+ final Class<? extends Exception> expectedLinkedException = isBroker010() ? ConnectionException.class : AMQConnectionClosedException.class;
+
+ assertConnectionOpen();
+
+ stopBroker();
+
+ JMSException exception = _recordingExceptionListener.awaitException(10000);
+ assertConnectionCloseWasReported(exception, expectedLinkedException);
+ assertConnectionClosed();
+
+ ensureCanCloseWithoutException();
+ }
+
+ public void testClientCloseOnBrokerKill() throws Exception
+ {
+ final Class<? extends Exception> expectedLinkedException = isBroker010() ? ConnectionException.class : AMQDisconnectedException.class;
+
+ if (!_isExternalBroker)
+ {
+ return;
+ }
+
+ assertConnectionOpen();
+
+ killBroker();
+
+ JMSException exception = _recordingExceptionListener.awaitException(10000);
+ assertConnectionCloseWasReported(exception, expectedLinkedException);
+ assertConnectionClosed();
+
+ ensureCanCloseWithoutException();
+ }
+
+ private void ensureCanCloseWithoutException()
+ {
+ try
+ {
+ _connection.close();
+ }
+ catch (JMSException e)
+ {
+ fail("Connection should close without exception" + e.getMessage());
+ }
+ }
+
+ private void assertConnectionCloseWasReported(JMSException exception, Class<? extends Exception> linkedExceptionClass)
+ {
+ assertNotNull("Broker shutdown should be reported to the client via the ExceptionListener", exception);
+ assertNotNull("JMXException should have linked exception", exception.getLinkedException());
+
+ assertEquals("Unexpected linked exception", linkedExceptionClass, exception.getLinkedException().getClass());
+ }
+
+ private void assertConnectionClosed()
+ {
+ assertTrue("Connection should be marked as closed", ((AMQConnection)_connection).isClosed());
+ }
+
+ private void assertConnectionOpen()
+ {
+ assertFalse("Connection should not be marked as closed", ((AMQConnection)_connection).isClosed());
+ }
+
+ private final class RecordingExceptionListener implements ExceptionListener
+ {
+ private final CountDownLatch _exceptionReceivedLatch = new CountDownLatch(1);
+ private volatile JMSException _exception;
+
+ @Override
+ public void onException(JMSException exception)
+ {
+ _exception = exception;
+ }
+
+ public JMSException awaitException(long timeoutInMillis) throws InterruptedException
+ {
+ _exceptionReceivedLatch.await(timeoutInMillis, TimeUnit.MILLISECONDS);
+ return _exception;
+ }
+ }
+
+
+ private class Listener implements MessageListener
+ {
+ int _messageCount;
+
+ @Override
+ public synchronized void onMessage(Message message)
+ {
+ _messageCount++;
+ }
+
+ public synchronized int getCount()
+ {
+ return _messageCount;
+ }
+ }
+
+ public void testNoDeliveryAfterBrokerClose() throws JMSException, NamingException, InterruptedException
+ {
+
+ Listener listener = new Listener();
+
+ Session session = _connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ MessageConsumer consumer1 = session.createConsumer(getTestQueue());
+ consumer1.setMessageListener(listener);
+
+ MessageProducer producer = _session.createProducer(getTestQueue());
+ producer.send(_session.createTextMessage("test message"));
+
+ _connection.start();
+
+
+ synchronized (listener)
+ {
+ long currentTime = System.currentTimeMillis();
+ long until = currentTime + 2000l;
+ while(listener.getCount() == 0 && currentTime < until)
+ {
+ listener.wait(until - currentTime);
+ currentTime = System.currentTimeMillis();
+ }
+ }
+ assertEquals(1, listener.getCount());
+
+ Connection connection2 = getConnection();
+ Session session2 = connection2.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ MessageConsumer consumer2 = session2.createConsumer(getTestQueue());
+ consumer2.setMessageListener(listener);
+ connection2.start();
+
+
+ Connection connection3 = getConnection();
+ Session session3 = connection3.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ MessageConsumer consumer3 = session3.createConsumer(getTestQueue());
+ consumer3.setMessageListener(listener);
+ connection3.start();
+
+ assertEquals(1, listener.getCount());
+
+ stopBroker();
+
+ assertEquals(1, listener.getCount());
+
+
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/connection/ConnectionFactoryTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/connection/ConnectionFactoryTest.java
new file mode 100644
index 0000000000..bf1fbbf1a3
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/connection/ConnectionFactoryTest.java
@@ -0,0 +1,78 @@
+/*
+ *
+ * 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.qpid.test.unit.client.connection;
+
+import javax.jms.Connection;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQConnectionFactory;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+public class ConnectionFactoryTest extends QpidBrokerTestCase
+{
+
+ /**
+ * The username & password specified should not override the default
+ * specified in the URL.
+ */
+ public void testCreateConnectionWithUsernamePassword() throws Exception
+ {
+
+ String brokerUrl = getBroker().toString();
+ String URL = "amqp://guest:guest@clientID/test?brokerlist='" + brokerUrl + "'";
+ AMQConnectionFactory factory = new AMQConnectionFactory(URL);
+
+ AMQConnection con = (AMQConnection)factory.createConnection();
+ assertEquals("Usernames used is different from the one in URL","guest",con.getConnectionURL().getUsername());
+ assertEquals("Password used is different from the one in URL","guest",con.getConnectionURL().getPassword());
+
+ try
+ {
+ AMQConnection con2 = (AMQConnection)factory.createConnection("user","pass");
+ assertEquals("Usernames used is different from the one in URL","user",con2.getConnectionURL().getUsername());
+ assertEquals("Password used is different from the one in URL","pass",con2.getConnectionURL().getPassword());
+ }
+ catch(Exception e)
+ {
+ // ignore
+ }
+
+ AMQConnection con3 = (AMQConnection)factory.createConnection();
+ assertEquals("Usernames used is different from the one in URL","guest",con3.getConnectionURL().getUsername());
+ assertEquals("Password used is different from the one in URL","guest",con3.getConnectionURL().getPassword());
+ }
+
+ /**
+ * Verifies that a connection can be made using an instance of AMQConnectionFactory created with the
+ * default constructor and provided with the connection url via setter.
+ */
+ public void testCreatingConnectionWithInstanceMadeUsingDefaultConstructor() throws Exception
+ {
+ String broker = getBroker().toString();
+ String url = "amqp://guest:guest@clientID/test?brokerlist='" + broker + "'";
+
+ AMQConnectionFactory factory = new AMQConnectionFactory();
+ factory.setConnectionURLString(url);
+
+ Connection con = factory.createConnection();
+ con.close();
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/connection/ConnectionStartTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/connection/ConnectionStartTest.java
new file mode 100644
index 0000000000..6ea1582bb8
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/connection/ConnectionStartTest.java
@@ -0,0 +1,157 @@
+/*
+ *
+ * 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.qpid.test.unit.client.connection;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageListener;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class ConnectionStartTest extends QpidBrokerTestCase
+{
+
+ private String _broker = "vm://:1";
+
+ private AMQConnection _connection;
+ private Session _consumerSess;
+ private MessageConsumer _consumer;
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ try
+ {
+
+
+ AMQConnection pubCon = (AMQConnection) getConnection("guest", "guest");
+
+ AMQQueue queue = new AMQQueue(pubCon,"ConnectionStartTest");
+
+ Session pubSess = pubCon.createSession(false, AMQSession.AUTO_ACKNOWLEDGE);
+
+ MessageProducer pub = pubSess.createProducer(queue);
+
+ _connection = (AMQConnection) getConnection("guest", "guest");
+
+ _consumerSess = _connection.createSession(false, AMQSession.AUTO_ACKNOWLEDGE);
+
+ _consumer = _consumerSess.createConsumer(queue);
+
+ //publish after queue is created to ensure it can be routed as expected
+ pub.send(pubSess.createTextMessage("Initial Message"));
+
+ pubCon.close();
+
+ }
+ catch (Exception e)
+ {
+ _logger.error("Connection to " + _broker + " should succeed.", e);
+ fail("Connection to " + _broker + " should succeed. Reason: " + e);
+ }
+ }
+
+ protected void tearDown() throws Exception
+ {
+ _connection.close();
+ super.tearDown();
+ }
+
+ public void testSimpleReceiveConnection()
+ {
+ try
+ {
+ assertTrue("Connection should not be started", !_connection.started());
+ //Note that this next line will start the dispatcher in the session
+ // should really not be called before _connection start
+ //assertTrue("There should not be messages waiting for the consumer", _consumer.receiveNoWait() == null);
+ _connection.start();
+ assertTrue("There should be messages waiting for the consumer", _consumer.receive(10*1000) != null);
+ assertTrue("Connection should be started", _connection.started());
+
+ }
+ catch (JMSException e)
+ {
+ fail("An error occured during test because:" + e);
+ }
+
+ }
+
+ public void testMessageListenerConnection()
+ {
+ final CountDownLatch _gotMessage = new CountDownLatch(1);
+
+ try
+ {
+ assertTrue("Connection should not be started", !_connection.started());
+ _consumer.setMessageListener(new MessageListener()
+ {
+ public void onMessage(Message message)
+ {
+ try
+ {
+ assertTrue("Connection should be started", _connection.started());
+ assertEquals("Mesage Received", "Initial Message", ((TextMessage) message).getText());
+ _gotMessage.countDown();
+ }
+ catch (JMSException e)
+ {
+ fail("Couldn't get message text because:" + e.getCause());
+ }
+ }
+ });
+
+ assertTrue("Connection should not be started", !_connection.started());
+ _connection.start();
+ assertTrue("Connection should be started", _connection.started());
+
+ try
+ {
+ assertTrue("Listener was never called", _gotMessage.await(10 * 1000, TimeUnit.MILLISECONDS));
+ }
+ catch (InterruptedException e)
+ {
+ fail("Timed out awaiting message via onMessage");
+ }
+
+ }
+ catch (JMSException e)
+ {
+ fail("Failed because:" + e.getCause());
+ }
+
+ }
+
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(ConnectionStartTest.class);
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/connection/ConnectionTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/connection/ConnectionTest.java
new file mode 100644
index 0000000000..ed03e83292
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/connection/ConnectionTest.java
@@ -0,0 +1,378 @@
+/*
+ *
+ * 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.qpid.test.unit.client.connection;
+
+import javax.jms.Connection;
+import javax.jms.QueueSession;
+import javax.jms.TopicSession;
+
+import org.apache.qpid.AMQConnectionFailureException;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.AMQUnresolvedAddressException;
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQConnectionURL;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.AMQTopic;
+import org.apache.qpid.configuration.ClientProperties;
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.jms.BrokerDetails;
+import org.apache.qpid.jms.ConnectionURL;
+import org.apache.qpid.jms.Session;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+public class ConnectionTest extends QpidBrokerTestCase
+{
+
+ private String _broker_NotRunning = "tcp://localhost:" + findFreePort();
+
+ private String _broker_BadDNS = "tcp://hg3sgaaw4lgihjs";
+
+ public void testSimpleConnection() throws Exception
+ {
+ AMQConnection conn = null;
+ try
+ {
+ conn = new AMQConnection(getBroker().toString(), "guest", "guest", "fred", "test");
+ }
+ catch (Exception e)
+ {
+ fail("Connection to " + getBroker() + " should succeed. Reason: " + e);
+ }
+ finally
+ {
+ if(conn != null)
+ {
+ conn.close();
+ }
+ }
+ }
+
+ public void testDefaultExchanges() throws Exception
+ {
+ AMQConnection conn = null;
+ try
+ {
+ BrokerDetails broker = getBroker();
+ broker.setProperty(BrokerDetails.OPTIONS_RETRY, "1");
+ ConnectionURL url = new AMQConnectionURL("amqp://guest:guest@clientid/test?brokerlist='"
+ + broker
+ + "'&defaultQueueExchange='test.direct'"
+ + "&defaultTopicExchange='test.topic'"
+ + "&temporaryQueueExchange='tmp.direct'"
+ + "&temporaryTopicExchange='tmp.topic'");
+
+ System.err.println(url.toString());
+ conn = new AMQConnection(url);
+
+
+ AMQSession sess = (AMQSession) conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ sess.declareExchange(new AMQShortString("test.direct"),
+ AMQShortString.valueOf(ExchangeDefaults.DIRECT_EXCHANGE_CLASS), false);
+
+ sess.declareExchange(new AMQShortString("tmp.direct"),
+ AMQShortString.valueOf(ExchangeDefaults.DIRECT_EXCHANGE_CLASS), false);
+
+ sess.declareExchange(new AMQShortString("tmp.topic"),
+ AMQShortString.valueOf(ExchangeDefaults.TOPIC_EXCHANGE_CLASS), false);
+
+ sess.declareExchange(new AMQShortString("test.topic"),
+ AMQShortString.valueOf(ExchangeDefaults.TOPIC_EXCHANGE_CLASS), false);
+
+ QueueSession queueSession = conn.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ AMQQueue queue = (AMQQueue) queueSession.createQueue("MyQueue");
+
+ assertEquals(queue.getExchangeName().toString(), "test.direct");
+
+ AMQQueue tempQueue = (AMQQueue) queueSession.createTemporaryQueue();
+
+ assertEquals(tempQueue.getExchangeName().toString(), "tmp.direct");
+
+ queueSession.close();
+
+ TopicSession topicSession = conn.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ AMQTopic topic = (AMQTopic) topicSession.createTopic("silly.topic");
+
+ assertEquals(topic.getExchangeName().toString(), "test.topic");
+
+ AMQTopic tempTopic = (AMQTopic) topicSession.createTemporaryTopic();
+
+ assertEquals(tempTopic.getExchangeName().toString(), "tmp.topic");
+
+ topicSession.close();
+
+ }
+ catch (Exception e)
+ {
+ fail("Connection to " + getBroker() + " should succeed. Reason: " + e);
+ }
+ finally
+ {
+ conn.close();
+ }
+ }
+
+ public void testPasswordFailureConnection() throws Exception
+ {
+ AMQConnection conn = null;
+ try
+ {
+ BrokerDetails broker = getBroker();
+ broker.setProperty(BrokerDetails.OPTIONS_RETRY, "0");
+ conn = new AMQConnection("amqp://guest:rubbishpassword@clientid/test?brokerlist='" + broker + "'");
+ fail("Connection should not be established password is wrong.");
+ }
+ catch (AMQConnectionFailureException amqe)
+ {
+ assertNotNull("No cause set:" + amqe.getMessage(), amqe.getCause());
+ assertTrue("Exception was wrong type", amqe.getCause() instanceof AMQException);
+ }
+ finally
+ {
+ if (conn != null)
+ {
+ conn.close();
+ }
+ }
+ }
+
+ public void testConnectionFailure() throws Exception
+ {
+ AMQConnection conn = null;
+ try
+ {
+ conn = new AMQConnection("amqp://guest:guest@clientid/testpath?brokerlist='" + _broker_NotRunning + "?retries='0''");
+ fail("Connection should not be established");
+ }
+ catch (AMQException amqe)
+ {
+ if (!(amqe instanceof AMQConnectionFailureException))
+ {
+ fail("Correct exception not thrown. Excpected 'AMQConnectionException' got: " + amqe);
+ }
+ }
+ finally
+ {
+ if (conn != null)
+ {
+ conn.close();
+ }
+ }
+
+ }
+
+ public void testUnresolvedHostFailure() throws Exception
+ {
+ AMQConnection conn = null;
+ try
+ {
+ conn = new AMQConnection("amqp://guest:guest@clientid/testpath?brokerlist='" + _broker_BadDNS + "?retries='0''");
+ fail("Connection should not be established");
+ }
+ catch (AMQException amqe)
+ {
+ if (!(amqe instanceof AMQUnresolvedAddressException))
+ {
+ fail("Correct exception not thrown. Excpected 'AMQUnresolvedAddressException' got: " + amqe);
+ }
+ }
+ finally
+ {
+ if (conn != null)
+ {
+ conn.close();
+ }
+ }
+
+ }
+
+ public void testUnresolvedVirtualHostFailure() throws Exception
+ {
+ AMQConnection conn = null;
+ try
+ {
+ BrokerDetails broker = getBroker();
+ broker.setProperty(BrokerDetails.OPTIONS_RETRY, "0");
+ conn = new AMQConnection("amqp://guest:guest@clientid/rubbishhost?brokerlist='" + broker + "'");
+ fail("Connection should not be established");
+ }
+ catch (AMQException amqe)
+ {
+ if (!(amqe instanceof AMQConnectionFailureException))
+ {
+ fail("Correct exception not thrown. Excpected 'AMQConnectionFailureException' got: " + amqe);
+ }
+ }
+ finally
+ {
+ if (conn != null)
+ {
+ conn.close();
+ }
+ }
+ }
+
+ public void testClientIdCannotBeChanged() throws Exception
+ {
+ Connection connection = new AMQConnection(getBroker().toString(), "guest", "guest",
+ "fred", "test");
+ try
+ {
+ connection.setClientID("someClientId");
+ fail("No IllegalStateException thrown when resetting clientid");
+ }
+ catch (javax.jms.IllegalStateException e)
+ {
+ // PASS
+ }
+ finally
+ {
+ if (connection != null)
+ {
+ connection.close();
+ }
+ }
+ }
+
+ public void testClientIdIsPopulatedAutomatically() throws Exception
+ {
+ Connection connection = new AMQConnection(getBroker().toString(), "guest", "guest",
+ null, "test");
+ try
+ {
+ assertNotNull(connection.getClientID());
+ }
+ finally
+ {
+ connection.close();
+ }
+ connection.close();
+ }
+
+ public void testUnsupportedSASLMechanism() throws Exception
+ {
+ BrokerDetails broker = getBroker();
+ broker.setProperty(BrokerDetails.OPTIONS_SASL_MECHS, "MY_MECH");
+
+ try
+ {
+ Connection connection = new AMQConnection(broker.toString(), "guest", "guest",
+ null, "test");
+ connection.close();
+ fail("The client should throw a ConnectionException stating the" +
+ " broker does not support the SASL mech specified by the client");
+ }
+ catch (Exception e)
+ {
+ assertTrue("Unexpected exception message : " + e.getMessage(),
+ e.getMessage().contains("Client and broker have no SASL mechanisms in common."));
+ assertTrue("Unexpected exception message : " + e.getMessage(),
+ e.getMessage().contains("Client restricted itself to : MY_MECH"));
+
+ }
+ }
+
+ /**
+ * Tests that when the same user connects twice with same clientid, the second connection
+ * fails if the clientid verification feature is enabled (which uses a dummy 0-10 Session
+ * with the clientid as its name to detect the previous usage of the clientid by the user)
+ */
+ public void testClientIDVerificationForSameUser() throws Exception
+ {
+ setTestSystemProperty(ClientProperties.QPID_VERIFY_CLIENT_ID, "true");
+
+ BrokerDetails broker = getBroker();
+ try
+ {
+ Connection con = new AMQConnection(broker.toString(), "guest", "guest",
+ "client_id", "test");
+
+ Connection con2 = new AMQConnection(broker.toString(), "guest", "guest",
+ "client_id", "test");
+
+ fail("The client should throw a ConnectionException stating the" +
+ " client ID is not unique");
+ }
+ catch (Exception e)
+ {
+ assertTrue("Incorrect exception thrown: " + e.getMessage(),
+ e.getMessage().contains("ClientID must be unique"));
+ }
+ }
+
+ /**
+ * Tests that when different users connects with same clientid, the second connection
+ * succeeds even though the clientid verification feature is enabled (which uses a dummy
+ * 0-10 Session with the clientid as its name; these are only verified unique on a
+ * per-principal basis)
+ */
+ public void testClientIDVerificationForDifferentUsers() throws Exception
+ {
+ setTestSystemProperty(ClientProperties.QPID_VERIFY_CLIENT_ID, "true");
+
+ BrokerDetails broker = getBroker();
+ try
+ {
+ Connection con = new AMQConnection(broker.toString(), "guest", "guest",
+ "client_id", "test");
+
+ Connection con2 = new AMQConnection(broker.toString(), "admin", "admin",
+ "client_id", "test");
+ }
+ catch (Exception e)
+ {
+ fail("Unexpected exception thrown, client id was not unique but usernames were different! " + e.getMessage());
+ }
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(ConnectionTest.class);
+ }
+
+ public void testExceptionWhenUserPassIsRequired() throws Exception
+ {
+ AMQConnection conn = null;
+ try
+ {
+ BrokerDetails broker = getBroker();
+ String url = "amqp:///test?brokerlist='" + broker + "?sasl_mechs='PLAIN%2520CRAM-MD5''";
+ conn = new AMQConnection(url);
+ conn.close();
+ fail("Exception should be thrown as user name and password is required");
+ }
+ catch (Exception e)
+ {
+ if (!e.getMessage().contains("Username and Password is required for the selected mechanism"))
+ {
+ if (conn != null && !conn.isClosed())
+ {
+ conn.close();
+ }
+ fail("Incorrect Exception thrown! The exception thrown is : " + e.getMessage());
+ }
+ }
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/connection/ExceptionListenerTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/connection/ExceptionListenerTest.java
new file mode 100644
index 0000000000..141de1e5a8
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/connection/ExceptionListenerTest.java
@@ -0,0 +1,244 @@
+/*
+ * 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.qpid.test.unit.client.connection;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.jms.Connection;
+import javax.jms.ExceptionListener;
+import javax.jms.IllegalStateException;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageListener;
+import javax.jms.Queue;
+import javax.jms.Session;
+
+import org.apache.qpid.AMQConnectionClosedException;
+import org.apache.qpid.client.AMQNoRouteException;
+import org.apache.qpid.jms.ConnectionURL;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+import org.apache.qpid.transport.ConnectionException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ExceptionListenerTest extends QpidBrokerTestCase
+{
+ private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionListenerTest.class);
+
+ private volatile Throwable _lastExceptionListenerException = null;
+
+ public void testExceptionListenerHearsBrokerShutdown() throws Exception
+ {
+ final CountDownLatch exceptionReceivedLatch = new CountDownLatch(1);
+ final AtomicInteger exceptionCounter = new AtomicInteger(0);
+ final ExceptionListener listener = new ExceptionListener()
+ {
+ public void onException(JMSException exception)
+ {
+ exceptionCounter.incrementAndGet();
+ _lastExceptionListenerException = exception;
+ exceptionReceivedLatch.countDown();
+ }
+ };
+
+ Connection connection = getConnection();
+ connection.setExceptionListener(listener);
+
+ stopBroker();
+
+ exceptionReceivedLatch.await(10, TimeUnit.SECONDS);
+
+ assertEquals("Unexpected number of exceptions received", 1, exceptionCounter.intValue());
+ LOGGER.debug("exception was", _lastExceptionListenerException);
+ assertNotNull("Exception should have cause", _lastExceptionListenerException.getCause());
+ Class<? extends Exception> expectedExceptionClass = isBroker010() ? ConnectionException.class : AMQConnectionClosedException.class;
+ assertEquals(expectedExceptionClass, _lastExceptionListenerException.getCause().getClass());
+ }
+
+ /**
+ * It is reasonable for an application to perform Connection#close within the exception
+ * listener. This test verifies that close is allowed, and proceeds without generating
+ * further exceptions.
+ */
+ public void testExceptionListenerClosesConnection_IsAllowed() throws Exception
+ {
+ final CountDownLatch exceptionReceivedLatch = new CountDownLatch(1);
+ final Connection connection = getConnection();
+ final ExceptionListener listener = new ExceptionListener()
+ {
+ public void onException(JMSException exception)
+ {
+ try
+ {
+ connection.close();
+ // PASS
+ }
+ catch (Throwable t)
+ {
+ _lastExceptionListenerException = t;
+ }
+ finally
+ {
+ exceptionReceivedLatch.countDown();
+ }
+ }
+ };
+ connection.setExceptionListener(listener);
+
+
+ stopBroker();
+
+ boolean exceptionReceived = exceptionReceivedLatch.await(10, TimeUnit.SECONDS);
+ assertTrue("Exception listener did not hear exception within timeout", exceptionReceived);
+ assertNull("Connection#close() should not have thrown exception", _lastExceptionListenerException);
+ }
+
+ /**
+ * Spring's SingleConnectionFactory installs an ExceptionListener that calls stop()
+ * and ignores any IllegalStateException that result. This test serves to test this
+ * scenario.
+ */
+ public void testExceptionListenerStopsConnection_ThrowsIllegalStateException() throws Exception
+ {
+ final CountDownLatch exceptionReceivedLatch = new CountDownLatch(1);
+ final Connection connection = getConnection();
+ final ExceptionListener listener = new ExceptionListener()
+ {
+ public void onException(JMSException exception)
+ {
+ try
+ {
+ connection.stop();
+ fail("Exception not thrown");
+ }
+ catch (IllegalStateException ise)
+ {
+ // PASS
+ }
+ catch (Throwable t)
+ {
+ _lastExceptionListenerException = t;
+ }
+ finally
+ {
+ exceptionReceivedLatch.countDown();
+ }
+ }
+ };
+ connection.setExceptionListener(listener);
+
+ stopBroker();
+
+ boolean exceptionReceived = exceptionReceivedLatch.await(10, TimeUnit.SECONDS);
+ assertTrue("Exception listener did not hear exception within timeout", exceptionReceived);
+ assertNull("Connection#stop() should not have thrown unexpected exception", _lastExceptionListenerException);
+ }
+
+ /**
+ * This test reproduces a deadlock that was the subject of a support call. A Spring based
+ * application was using SingleConnectionFactory. It installed an ExceptionListener that
+ * stops and closes the connection in response to any exception. On receipt of a message
+ * the application would create a new session then send a response message (within onMessage).
+ * It appears that a misconfiguration in the application meant that some of these messages
+ * were bounced (no-route). Bounces are treated like connection exceptions and are passed
+ * back to the application via the ExceptionListener. The deadlock occurred between the
+ * ExceptionListener's call to stop() and the MessageListener's attempt to create a new
+ * session.
+ */
+ public void testExceptionListenerConnectionStopDeadlock() throws Exception
+ {
+ Queue messageQueue = getTestQueue();
+
+ Map<String, String> options = new HashMap<String, String>();
+ options.put(ConnectionURL.OPTIONS_CLOSE_WHEN_NO_ROUTE, Boolean.toString(false));
+
+ final Connection connection = getConnectionWithOptions(options);
+
+ Session session = connection.createSession(true, Session.SESSION_TRANSACTED);
+ session.createConsumer(messageQueue).close(); // Create queue by side-effect
+
+ // Put 10 messages onto messageQueue
+ sendMessage(session, messageQueue, 10);
+
+ // Install an exception listener that stops/closes the connection on receipt of 2nd AMQNoRouteException.
+ // (Triggering on the 2nd (rather than 1st) seems to increase the probability that the test ends in deadlock,
+ // at least on my machine).
+ final CountDownLatch exceptionReceivedLatch = new CountDownLatch(2);
+ final ExceptionListener listener = new ExceptionListener()
+ {
+ public void onException(JMSException exception)
+ {
+ try
+ {
+ assertNotNull("JMS Exception must have cause", exception.getCause() );
+ assertEquals("JMS Exception is of wrong type", AMQNoRouteException.class, exception.getCause().getClass());
+ exceptionReceivedLatch.countDown();
+ if (exceptionReceivedLatch.getCount() == 0)
+ {
+ connection.stop(); // ** Deadlock
+ connection.close();
+ }
+ }
+ catch (Throwable t)
+ {
+ _lastExceptionListenerException = t;
+ }
+ }
+ };
+ connection.setExceptionListener(listener);
+
+ // Create a message listener that receives from testQueue and tries to forward them to unknown queue (thus
+ // provoking AMQNoRouteException exceptions to be delivered to the ExceptionListener).
+ final Queue unknownQueue = session.createQueue(getTestQueueName() + "_unknown");;
+ MessageListener redirectingMessageListener = new MessageListener()
+ {
+ @Override
+ public void onMessage(Message msg)
+ {
+ try
+ {
+ Session mlSession = connection.createSession(true, Session.SESSION_TRANSACTED); // ** Deadlock
+ mlSession.createProducer(unknownQueue).send(msg);
+ mlSession.commit();
+ }
+ catch (JMSException je)
+ {
+ // Connection is closed by the listener, so exceptions here are expected.
+ LOGGER.debug("Expected exception - message listener got exception", je);
+ }
+ }
+ };
+
+ MessageConsumer consumer = session.createConsumer(messageQueue);
+ consumer.setMessageListener(redirectingMessageListener);
+ connection.start();
+
+ // Await the 2nd exception
+ boolean exceptionReceived = exceptionReceivedLatch.await(10, TimeUnit.SECONDS);
+ assertTrue("Exception listener did not hear exception within timeout", exceptionReceived);
+ assertNull("Exception listener should not have had experienced exception", _lastExceptionListenerException);
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/message/ObjectMessageTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/message/ObjectMessageTest.java
new file mode 100644
index 0000000000..99dc5ff216
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/message/ObjectMessageTest.java
@@ -0,0 +1,334 @@
+/*
+ *
+ * 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.qpid.test.unit.client.message;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQDestination;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageListener;
+import javax.jms.MessageProducer;
+import javax.jms.ObjectMessage;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+
+public class ObjectMessageTest extends QpidBrokerTestCase implements MessageListener
+{
+ private static final Logger _logger = LoggerFactory.getLogger(ObjectMessageTest.class);
+
+ private AMQConnection connection;
+ private AMQDestination destination;
+ private AMQSession session;
+ private MessageProducer producer;
+ private Serializable[] data;
+ private volatile boolean waiting;
+ private int received;
+ private final ArrayList items = new ArrayList();
+
+ private String _broker = "vm://:1";
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ connection = (AMQConnection) getConnection("guest", "guest");
+ destination = new AMQQueue(connection, randomize("LatencyTest"), true);
+ session = (AMQSession) connection.createSession(false, AMQSession.NO_ACKNOWLEDGE);
+
+ // set up a consumer
+ session.createConsumer(destination).setMessageListener(this);
+ connection.start();
+
+ // create a publisher
+ producer = session.createProducer(destination, false, false);
+ A a1 = new A(1, "A");
+ A a2 = new A(2, "a");
+ B b = new B(1, "B");
+ C c = new C();
+ c.put("A1", a1);
+ c.put("a2", a2);
+ c.put("B", b);
+ c.put("String", "String");
+
+ data = new Serializable[] { a1, a2, b, c, "Hello World!", new Integer(1001) };
+ }
+
+ protected void tearDown() throws Exception
+ {
+ close();
+ super.tearDown();
+ }
+
+ public ObjectMessageTest()
+ { }
+
+ ObjectMessageTest(String broker) throws Exception
+ {
+ _broker = broker;
+ }
+
+ public void testSendAndReceive() throws Exception
+ {
+ try
+ {
+ send();
+ waitUntilReceived(data.length);
+ check();
+ _logger.info("All " + data.length + " items matched.");
+ }
+ catch (Exception e)
+ {
+ _logger.error("This Test should succeed but failed", e);
+ fail("This Test should succeed but failed due to: " + e);
+ }
+ }
+
+ public void testSetObjectPropertyForString() throws Exception
+ {
+ String testStringProperty = "TestStringProperty";
+ ObjectMessage msg = session.createObjectMessage(data[0]);
+ msg.setObjectProperty("TestStringProperty", testStringProperty);
+ assertEquals(testStringProperty, msg.getObjectProperty("TestStringProperty"));
+ }
+
+ public void testSetObjectPropertyForBoolean() throws Exception
+ {
+ ObjectMessage msg = session.createObjectMessage(data[0]);
+ msg.setObjectProperty("TestBooleanProperty", Boolean.TRUE);
+ assertEquals(Boolean.TRUE, msg.getObjectProperty("TestBooleanProperty"));
+ }
+
+ public void testSetObjectPropertyForByte() throws Exception
+ {
+ ObjectMessage msg = session.createObjectMessage(data[0]);
+ msg.setObjectProperty("TestByteProperty", Byte.MAX_VALUE);
+ assertEquals(Byte.MAX_VALUE, msg.getObjectProperty("TestByteProperty"));
+ }
+
+ public void testSetObjectPropertyForShort() throws Exception
+ {
+ ObjectMessage msg = session.createObjectMessage(data[0]);
+ msg.setObjectProperty("TestShortProperty", Short.MAX_VALUE);
+ assertEquals(Short.MAX_VALUE, msg.getObjectProperty("TestShortProperty"));
+ }
+
+ public void testSetObjectPropertyForInteger() throws Exception
+ {
+ ObjectMessage msg = session.createObjectMessage(data[0]);
+ msg.setObjectProperty("TestIntegerProperty", Integer.MAX_VALUE);
+ assertEquals(Integer.MAX_VALUE, msg.getObjectProperty("TestIntegerProperty"));
+ }
+
+ public void testSetObjectPropertyForDouble() throws Exception
+ {
+ ObjectMessage msg = session.createObjectMessage(data[0]);
+ msg.setObjectProperty("TestDoubleProperty", Double.MAX_VALUE);
+ assertEquals(Double.MAX_VALUE, msg.getObjectProperty("TestDoubleProperty"));
+ }
+
+ public void testSetObjectPropertyForFloat() throws Exception
+ {
+ ObjectMessage msg = session.createObjectMessage(data[0]);
+ msg.setObjectProperty("TestFloatProperty", Float.MAX_VALUE);
+ assertEquals(Float.MAX_VALUE, msg.getObjectProperty("TestFloatProperty"));
+ }
+
+ public void testSetObjectPropertyForByteArray() throws Exception
+ {
+ byte[] array = { 1, 2, 3, 4, 5 };
+ ObjectMessage msg = session.createObjectMessage(data[0]);
+ msg.setObjectProperty("TestByteArrayProperty", array);
+ assertTrue(Arrays.equals(array, (byte[]) msg.getObjectProperty("TestByteArrayProperty")));
+ }
+
+ public void testSetObjectForNull() throws Exception
+ {
+ ObjectMessage msg = session.createObjectMessage();
+ msg.setObject(null);
+ assertNull(msg.getObject());
+ }
+
+ private void send() throws Exception
+ {
+ for (int i = 0; i < data.length; i++)
+ {
+ ObjectMessage msg;
+ if ((i % 2) == 0)
+ {
+ msg = session.createObjectMessage(data[i]);
+ }
+ else
+ {
+ msg = session.createObjectMessage();
+ msg.setObject(data[i]);
+ }
+
+ producer.send(msg);
+ }
+ }
+
+ public void check() throws Exception
+ {
+ Object[] actual = (Object[]) items.toArray();
+ if (actual.length != data.length)
+ {
+ throw new Exception("Expected " + data.length + " objects, got " + actual.length);
+ }
+
+ for (int i = 0; i < data.length; i++)
+ {
+ if (actual[i] instanceof Exception)
+ {
+ throw new Exception("Error on receive of " + data[i], ((Exception) actual[i]));
+ }
+
+ if (actual[i] == null)
+ {
+ throw new Exception("Expected " + data[i] + " got null");
+ }
+
+ if (!data[i].equals(actual[i]))
+ {
+ throw new Exception("Expected " + data[i] + " got " + actual[i]);
+ }
+ }
+ }
+
+ private void close() throws Exception
+ {
+ session.close();
+ connection.close();
+ }
+
+ private synchronized void waitUntilReceived(int count) throws InterruptedException
+ {
+ waiting = true;
+ while (received < count)
+ {
+ wait();
+ }
+
+ waiting = false;
+ }
+
+ public void onMessage(Message message)
+ {
+
+ try
+ {
+ if (message instanceof ObjectMessage)
+ {
+ items.add(((ObjectMessage) message).getObject());
+ }
+ else
+ {
+ _logger.error("ERROR: Got " + message.getClass().getName() + " not ObjectMessage");
+ items.add(message);
+ }
+ }
+ catch (JMSException e)
+ {
+ _logger.error("Error getting object from message", e);
+ items.add(e);
+ }
+
+ synchronized (this)
+ {
+ received++;
+ notify();
+ }
+ }
+
+ public static void main(String[] argv) throws Exception
+ {
+ String broker = (argv.length > 0) ? argv[0] : "vm://:1";
+ if ("-help".equals(broker))
+ {
+ System.out.println("Usage: <broker>");
+ }
+
+ new ObjectMessageTest(broker).testSendAndReceive();
+ }
+
+ private static class A implements Serializable
+ {
+ private String sValue;
+ private int iValue;
+
+ A(int i, String s)
+ {
+ sValue = s;
+ iValue = i;
+ }
+
+ public int hashCode()
+ {
+ return iValue;
+ }
+
+ public boolean equals(Object o)
+ {
+ return (o instanceof A) && equals((A) o);
+ }
+
+ protected boolean equals(A a)
+ {
+ return areEqual(a.sValue, sValue) && (a.iValue == iValue);
+ }
+ }
+
+ private static class B extends A
+ {
+ private long time;
+
+ B(int i, String s)
+ {
+ super(i, s);
+ time = System.currentTimeMillis();
+ }
+
+ protected boolean equals(A a)
+ {
+ return super.equals(a) && (a instanceof B) && (time == ((B) a).time);
+ }
+ }
+
+ private static class C extends HashMap implements Serializable
+ { }
+
+ private static boolean areEqual(Object a, Object b)
+ {
+ return (a == null) ? (b == null) : a.equals(b);
+ }
+
+ private static String randomize(String in)
+ {
+ return in + System.currentTimeMillis();
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/protocol/AMQProtocolSessionTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/protocol/AMQProtocolSessionTest.java
new file mode 100644
index 0000000000..3ffa73b9b7
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/protocol/AMQProtocolSessionTest.java
@@ -0,0 +1,198 @@
+/*
+ *
+ * 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.qpid.test.unit.client.protocol;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.security.Principal;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.protocol.AMQProtocolHandler;
+import org.apache.qpid.client.protocol.AMQProtocolSession;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+import org.apache.qpid.transport.Sender;
+import org.apache.qpid.transport.network.NetworkConnection;
+
+public class AMQProtocolSessionTest extends QpidBrokerTestCase
+{
+ private static class TestProtocolSession extends AMQProtocolSession
+ {
+
+ public TestProtocolSession(AMQProtocolHandler protocolHandler, AMQConnection connection)
+ {
+ super(protocolHandler,connection);
+ }
+
+ public TestNetworkConnection getNetworkConnection()
+ {
+ return (TestNetworkConnection) getProtocolHandler().getNetworkConnection();
+ }
+
+ public AMQShortString genQueueName()
+ {
+ return generateQueueName();
+ }
+ }
+
+ private TestProtocolSession _testSession;
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+
+ AMQConnection con = (AMQConnection) getConnection("guest", "guest");
+ AMQProtocolHandler protocolHandler = new AMQProtocolHandler(con);
+ protocolHandler.setNetworkConnection(new TestNetworkConnection());
+
+ //don't care about the values set here apart from the dummy IoSession
+ _testSession = new TestProtocolSession(protocolHandler , con);
+ }
+
+ public void testTemporaryQueueWildcard() throws UnknownHostException
+ {
+ checkTempQueueName(new InetSocketAddress(1234), "tmp_0_0_0_0_0_0_0_0_1234_1");
+ }
+
+ public void testTemporaryQueueLocalhostAddr() throws UnknownHostException
+ {
+ checkTempQueueName(new InetSocketAddress(InetAddress.getByName("127.0.0.1"), 1234), "tmp_127_0_0_1_1234_1");
+ }
+
+ public void testTemporaryQueueLocalhostName() throws UnknownHostException
+ {
+ checkTempQueueName(new InetSocketAddress(InetAddress.getByName("localhost"), 1234), "tmp_localhost_127_0_0_1_1234_1");
+ }
+
+ public void testTemporaryQueueInet4() throws UnknownHostException
+ {
+ checkTempQueueName(new InetSocketAddress(InetAddress.getByName("192.168.1.2"), 1234), "tmp_192_168_1_2_1234_1");
+ }
+
+ public void testTemporaryQueueInet6() throws UnknownHostException
+ {
+ checkTempQueueName(new InetSocketAddress(InetAddress.getByName("1080:0:0:0:8:800:200C:417A"), 1234), "tmp_1080_0_0_0_8_800_200c_417a_1234_1");
+ }
+
+ private void checkTempQueueName(SocketAddress address, String queueName)
+ {
+ _testSession.getNetworkConnection().setLocalAddress(address);
+ assertEquals("Wrong queue name", queueName, _testSession.genQueueName().asString());
+ }
+
+ private static class TestNetworkConnection implements NetworkConnection
+ {
+ private String _remoteHost = "127.0.0.1";
+ private String _localHost = "127.0.0.1";
+ private int _port = 1;
+ private SocketAddress _localAddress = null;
+ private final Sender<ByteBuffer> _sender;
+
+ public TestNetworkConnection()
+ {
+ _sender = new Sender<ByteBuffer>()
+ {
+
+ public void setIdleTimeout(int i)
+ {
+
+ }
+
+ public void send(ByteBuffer msg)
+ {
+
+ }
+
+ public void flush()
+ {
+
+ }
+
+ public void close()
+ {
+
+ }
+ };
+ }
+
+ @Override
+ public SocketAddress getLocalAddress()
+ {
+ return (_localAddress != null) ? _localAddress : new InetSocketAddress(_localHost, _port);
+ }
+
+ @Override
+ public SocketAddress getRemoteAddress()
+ {
+ return new InetSocketAddress(_remoteHost, _port);
+ }
+
+ @Override
+ public void setMaxReadIdle(int idleTime)
+ {
+ }
+
+ @Override
+ public Principal getPeerPrincipal()
+ {
+ return null;
+ }
+
+ @Override
+ public int getMaxReadIdle()
+ {
+ return 0;
+ }
+
+ @Override
+ public int getMaxWriteIdle()
+ {
+ return 0;
+ }
+
+ @Override
+ public void setMaxWriteIdle(int idleTime)
+ {
+ }
+
+ @Override
+ public void close()
+ {
+ }
+
+ public void setLocalAddress(SocketAddress address)
+ {
+ _localAddress = address;
+ }
+
+ public Sender<ByteBuffer> getSender()
+ {
+ return _sender;
+ }
+
+ public void start()
+ {
+ }
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/temporaryqueue/TemporaryQueueTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/temporaryqueue/TemporaryQueueTest.java
new file mode 100644
index 0000000000..41ab35f233
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/client/temporaryqueue/TemporaryQueueTest.java
@@ -0,0 +1,166 @@
+/*
+ *
+ * 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.qpid.test.unit.client.temporaryqueue;
+
+import org.apache.qpid.client.AMQDestination;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+import javax.jms.Connection;
+import javax.jms.JMSException;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+import javax.jms.TemporaryQueue;
+import javax.jms.TextMessage;
+
+/**
+ * Tests the behaviour of {@link TemporaryQueue}.
+ */
+public class TemporaryQueueTest extends QpidBrokerTestCase
+{
+ /**
+ * Tests the basic produce/consume behaviour of a temporary queue.
+ */
+ public void testMessageDeliveryUsingTemporaryQueue() throws Exception
+ {
+ final Connection conn = getConnection();
+ final Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ final TemporaryQueue queue = session.createTemporaryQueue();
+ assertNotNull(queue);
+ final MessageProducer producer = session.createProducer(queue);
+ final MessageConsumer consumer = session.createConsumer(queue);
+ conn.start();
+ producer.send(session.createTextMessage("hello"));
+ TextMessage tm = (TextMessage) consumer.receive(2000);
+ assertNotNull("Message not received", tm);
+ assertEquals("hello", tm.getText());
+ }
+
+ /**
+ * Tests that a temporary queue cannot be used by another {@link Session}.
+ */
+ public void testUseFromAnotherSessionProhibited() throws Exception
+ {
+ final Connection conn = getConnection();
+ final Session session1 = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ final Session session2 = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ final TemporaryQueue queue = session1.createTemporaryQueue();
+ assertNotNull(queue);
+
+ try
+ {
+ session2.createConsumer(queue);
+ fail("Expected a JMSException when subscribing to a temporary queue created on a different session");
+ }
+ catch (JMSException je)
+ {
+ //pass
+ assertEquals("Cannot consume from a temporary destination created on another session", je.getMessage());
+ }
+ }
+
+ /**
+ * Tests that the client is able to explicitly delete a temporary queue using
+ * {@link TemporaryQueue#delete()} and is prevented from deleting one that
+ * still has consumers.
+ *
+ * Note: Under < 0-10 {@link TemporaryQueue#delete()} only marks the queue as deleted
+ * on the client. 0-10 causes the queue to be deleted from the Broker.
+ */
+ public void testExplictTemporaryQueueDeletion() throws Exception
+ {
+ final Connection conn = getConnection();
+ final Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ final AMQSession<?, ?> amqSession = (AMQSession<?, ?>)session; // Required to observe the queue binding on the Broker
+ final TemporaryQueue queue = session.createTemporaryQueue();
+ assertNotNull(queue);
+ final MessageConsumer consumer = session.createConsumer(queue);
+ conn.start();
+
+ assertTrue("Queue should be bound", amqSession.isQueueBound((AMQDestination)queue));
+
+ try
+ {
+ queue.delete();
+ fail("Expected JMSException : should not be able to delete while there are active consumers");
+ }
+ catch (JMSException je)
+ {
+ //pass
+ assertEquals("Temporary Queue has consumers so cannot be deleted", je.getMessage());
+ }
+ consumer.close();
+
+ // Now deletion should succeed.
+ queue.delete();
+
+ try
+ {
+ session.createConsumer(queue);
+ fail("Exception not thrown");
+ }
+ catch (JMSException je)
+ {
+ //pass
+ assertEquals("Cannot consume from a deleted destination", je.getMessage());
+ }
+
+ if (isBroker010())
+ {
+ assertFalse("Queue should no longer be bound", amqSession.isQueueBound((AMQDestination)queue));
+ }
+ }
+
+ /**
+ * Tests that a temporary queue remains available for reuse even after its initial
+ * consumer has disconnected.
+ *
+ * This test would fail under < 0-10 as their temporary queues are deleted automatically
+ * (broker side) after the last consumer disconnects (so message2 would be lost). For this
+ * reason this test is excluded from those profiles.
+ */
+ public void testTemporaryQueueReused() throws Exception
+ {
+ final Connection conn = getConnection();
+ final Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ final TemporaryQueue queue = session.createTemporaryQueue();
+ assertNotNull(queue);
+
+ final MessageProducer producer1 = session.createProducer(queue);
+ final MessageConsumer consumer1 = session.createConsumer(queue);
+ conn.start();
+ producer1.send(session.createTextMessage("message1"));
+ producer1.send(session.createTextMessage("message2"));
+ TextMessage tm = (TextMessage) consumer1.receive(2000);
+ assertNotNull("Message not received by first consumer", tm);
+ assertEquals("message1", tm.getText());
+ consumer1.close();
+
+ final MessageConsumer consumer2 = session.createConsumer(queue);
+ conn.start();
+ tm = (TextMessage) consumer2.receive(2000);
+ assertNotNull("Message not received by second consumer", tm);
+ assertEquals("message2", tm.getText());
+ consumer2.close();
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/close/JavaServerCloseRaceConditionTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/close/JavaServerCloseRaceConditionTest.java
new file mode 100644
index 0000000000..b43fe35a09
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/close/JavaServerCloseRaceConditionTest.java
@@ -0,0 +1,118 @@
+/*
+ *
+ * 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.qpid.test.unit.close;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQSession_0_8;
+import org.apache.qpid.framing.AMQFrame;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.ExchangeDeclareBody;
+import org.apache.qpid.framing.ExchangeDeclareOkBody;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+import javax.jms.Session;
+
+/** QPID-1809
+ *
+ * Race condition on error handling and close logic.
+ *
+ * See most often with SimpleACLTest as this test is the expects the server to
+ * shut the connection/channels. This sort of testing is not performed by many,
+ * if any, of the other system tests.
+ *
+ * The problem is that we have two threads
+ *
+ * MainThread Exception(Mina)Thread
+ * | |
+ * Performs |
+ * ACtion |
+ * | Receives Server
+ * | Close
+ * Blocks for |
+ * Response |
+ * | Starts To Notify
+ * | client
+ * | |
+ * | <----- Notify Main Thread
+ * Notification |
+ * wakes client |
+ * | |
+ * Client then |
+ * processes Error. |
+ * | |
+ * Potentially Attempting Close Channel/Connection
+ * Connection Close
+ *
+ * The two threads both attempt to close the connection but the main thread does
+ * so assuming that the connection is open and valid.
+ *
+ * The Exception thread must modify the connection so that no furter syncWait
+ * commands are performed.
+ *
+ * This test sends an ExchangeDeclare that is Asynchronous and will fail and
+ * so cause a ChannelClose error but we perform a syncWait so that we can be
+ * sure to test that the BlockingWaiter is correctly awoken.
+ *
+ */
+public class JavaServerCloseRaceConditionTest extends QpidBrokerTestCase
+{
+ private static final String EXCHANGE_NAME = "NewExchangeNametoFailLookup";
+
+ public void test() throws Exception
+ {
+
+ AMQConnection connection = (AMQConnection) getConnection();
+
+ AMQSession_0_8 session = (AMQSession_0_8) connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ // Set no wait true so that we block the connection
+ // Also set a different exchange class string so the attempt to declare
+ // the exchange causes an exchange.
+ ExchangeDeclareBody body = session.getMethodRegistry().createExchangeDeclareBody(session.getTicket(), new AMQShortString(EXCHANGE_NAME), null,
+ true, false, false, false, true, null);
+
+ AMQFrame exchangeDeclare = body.generateFrame(session.getChannelId());
+
+ try
+ {
+ // block our thread so that can times out
+ connection.getProtocolHandler().syncWrite(exchangeDeclare, ExchangeDeclareOkBody.class);
+ }
+ catch (Exception e)
+ {
+ assertTrue("Exception should say the exchange is not known.", e.getMessage().contains("Unknown exchange: " + EXCHANGE_NAME));
+ }
+
+ try
+ {
+ // Depending on if the notification thread has closed the connection
+ // or not we may get an exception here when we attempt to close the
+ // connection. If we do get one then it should be the same as above
+ // an AMQAuthenticationException.
+ connection.close();
+ }
+ catch (Exception e)
+ {
+ assertTrue("Exception should say the exchange is not known.", e.getMessage().contains("Unknown exchange: " + EXCHANGE_NAME));
+ }
+
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/close/MessageConsumerCloseTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/close/MessageConsumerCloseTest.java
new file mode 100644
index 0000000000..df32bd7858
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/close/MessageConsumerCloseTest.java
@@ -0,0 +1,77 @@
+/*
+ *
+ * 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.qpid.test.unit.close;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import javax.jms.Connection;
+import javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageListener;
+import javax.jms.Session;
+
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+public class MessageConsumerCloseTest extends QpidBrokerTestCase
+{
+ Exception _exception;
+
+ public void testConsumerCloseAndSessionRollback() throws Exception
+ {
+ Connection connection = getConnection();
+ final CountDownLatch receiveLatch = new CountDownLatch(1);
+ final Session session = connection.createSession(true, Session.SESSION_TRANSACTED);
+ Destination destination = getTestQueue();
+ MessageConsumer consumer = session.createConsumer(destination);
+ sendMessage(session, destination, 2);
+ connection.start();
+ consumer.setMessageListener(new MessageListener()
+ {
+ @Override
+ public void onMessage(Message message)
+ {
+ try
+ {
+ receiveLatch.countDown();
+ session.rollback();
+ }
+ catch (JMSException e)
+ {
+ _exception = e;
+ }
+ }
+ });
+ boolean messageReceived = receiveLatch.await(1l, TimeUnit.SECONDS);
+ consumer.close();
+
+ assertNull("Exception occured on rollback:" + _exception, _exception);
+ assertTrue("Message is not received", messageReceived);
+
+ consumer = session.createConsumer(destination);
+ Message message1 = consumer.receive(1000l);
+ assertNotNull("message1 is not received", message1);
+ Message message2 = consumer.receive(1000l);
+ assertNotNull("message2 is not received", message2);
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/close/MessageRequeueTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/close/MessageRequeueTest.java
new file mode 100644
index 0000000000..5895d670a7
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/close/MessageRequeueTest.java
@@ -0,0 +1,371 @@
+/*
+ * 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.qpid.test.unit.close;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.client.message.AbstractJMSMessage;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+import org.apache.qpid.test.utils.QpidClientConnection;
+import org.apache.qpid.url.URLSyntaxException;
+
+import javax.jms.Connection;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.Queue;
+import javax.jms.Session;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class MessageRequeueTest extends QpidBrokerTestCase
+{
+ private static final Logger _logger = LoggerFactory.getLogger(MessageRequeueTest.class);
+
+ protected static AtomicInteger consumerIds = new AtomicInteger(0);
+ protected final Integer numTestMessages = 150;
+
+ protected final int consumeTimeout = 3000;
+
+ protected final String queue = "direct://amq.direct//message-requeue-test-queue";
+ protected String payload = "Message:";
+
+ protected final String BROKER = "tcp://127.0.0.1:5672";
+ private boolean testReception = true;
+
+ private long[] receieved = new long[numTestMessages + 1];
+ private boolean passed = false;
+ private QpidClientConnection conn;
+
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+
+ conn = new QpidClientConnection(BROKER);
+
+ conn.connect();
+ // clear queue
+ conn.consume(queue, consumeTimeout);
+ // load test data
+ _logger.info("creating test data, " + numTestMessages + " messages");
+ conn.put(queue, payload, numTestMessages);
+ // close this connection
+ conn.disconnect();
+ }
+
+ protected void tearDown() throws Exception
+ {
+
+ if (!passed) // clean up
+ {
+ QpidClientConnection conn = new QpidClientConnection(BROKER);
+
+ conn.connect();
+ // clear queue
+ conn.consume(queue, consumeTimeout);
+
+ conn.disconnect();
+ }
+
+ super.tearDown();
+ }
+
+ /**
+ * multiple consumers
+ *
+ * @throws javax.jms.JMSException if a JMS problem occurs
+ * @throws InterruptedException on timeout
+ */
+ public void testDrain() throws Exception
+ {
+ QpidClientConnection conn = new QpidClientConnection(BROKER);
+
+ conn.connect();
+
+ _logger.info("consuming queue " + queue);
+ Queue q = conn.getSession().createQueue(queue);
+
+ final MessageConsumer consumer = conn.getSession().createConsumer(q);
+ int messagesReceived = 0;
+
+ long[] messageLog = new long[numTestMessages + 1];
+
+ _logger.info("consuming...");
+ Message msg = consumer.receive(1000);
+ while (msg != null)
+ {
+ messagesReceived++;
+
+ long dt = ((AbstractJMSMessage) msg).getDeliveryTag();
+
+ int msgindex = msg.getIntProperty("index");
+ if (messageLog[msgindex] != 0)
+ {
+ _logger.error("Received Message(" + msgindex + ":" + ((AbstractJMSMessage) msg).getDeliveryTag()
+ + ") more than once.");
+ }
+
+ if (_logger.isInfoEnabled())
+ {
+ _logger.info("Received Message(" + System.identityHashCode(msgindex) + ") " + "DT:" + dt + "IN:" + msgindex);
+ }
+
+ if (dt == 0)
+ {
+ _logger.error("DT is zero for msg:" + msgindex);
+ }
+
+ messageLog[msgindex] = dt;
+
+ // get Next message
+ msg = consumer.receive(1000);
+ }
+
+ _logger.info("consuming done.");
+ conn.getSession().commit();
+ consumer.close();
+
+ int index = 0;
+ StringBuilder list = new StringBuilder();
+ list.append("Failed to receive:");
+ int failed = 0;
+
+ _logger.info("consumed: " + messagesReceived);
+
+ assertEquals("number of consumed messages does not match initial data", (int) numTestMessages, messagesReceived);
+ // with 0_10 we can have a delivery tag of 0
+ if (!conn.isBroker010())
+ {
+ for (long b : messageLog)
+ {
+ if ((b == 0) && (index != 0)) // delivery tag of zero shouldn't exist
+ {
+ _logger.error("Index: " + index + " was not received.");
+ list.append(" ");
+ list.append(index);
+ list.append(":");
+ list.append(b);
+ failed++;
+ }
+
+ index++;
+ }
+
+ assertEquals(list.toString(), 0, failed);
+ }
+
+ conn.disconnect();
+ passed = true;
+ }
+
+ /** multiple consumers
+ * Based on code subbmitted by client FT-304
+ */
+ public void testTwoCompetingConsumers()
+ {
+ Consumer c1 = new Consumer();
+ Consumer c2 = new Consumer();
+ Consumer c3 = new Consumer();
+ Consumer c4 = new Consumer();
+
+ Thread t1 = new Thread(c1);
+ Thread t2 = new Thread(c2);
+ Thread t3 = new Thread(c3);
+ Thread t4 = new Thread(c4);
+
+ t1.start();
+ t2.start();
+ t3.start();
+ // t4.start();
+
+ try
+ {
+ t1.join();
+ t2.join();
+ t3.join();
+ t4.join();
+ }
+ catch (InterruptedException e)
+ {
+ fail("Unable to join to Consumer theads");
+ }
+
+ _logger.info("consumer 1 count is " + c1.getCount());
+ _logger.info("consumer 2 count is " + c2.getCount());
+ _logger.info("consumer 3 count is " + c3.getCount());
+ _logger.info("consumer 4 count is " + c4.getCount());
+
+ Integer totalConsumed = c1.getCount() + c2.getCount() + c3.getCount() + c4.getCount();
+
+ // Check all messages were correctly delivered
+ int index = 0;
+ StringBuilder list = new StringBuilder();
+ list.append("Failed to receive:");
+ int failed = 0;
+ if (!conn.isBroker010())
+ {
+ for (long b : receieved)
+ {
+ if ((b == 0) && (index != 0)) // delivery tag of zero shouldn't exist (and we don't have msg 0)
+ {
+ _logger.error("Index: " + index + " was not received.");
+ list.append(" ");
+ list.append(index);
+ list.append(":");
+ list.append(b);
+ failed++;
+ }
+
+ index++;
+ }
+
+ assertEquals(list.toString() + "-" + numTestMessages + "-" + totalConsumed, 0, failed);
+ }
+ assertEquals("number of consumed messages does not match initial data", numTestMessages, totalConsumed);
+ passed = true;
+ }
+
+ class Consumer implements Runnable
+ {
+ private Integer count = 0;
+ private Integer id;
+
+ public Consumer()
+ {
+ id = consumerIds.addAndGet(1);
+ }
+
+ public void run()
+ {
+ try
+ {
+ _logger.info("consumer-" + id + ": starting");
+ QpidClientConnection conn = new QpidClientConnection(BROKER);
+
+ conn.connect();
+
+ _logger.info("consumer-" + id + ": connected, consuming...");
+ Message result;
+ do
+ {
+ result = conn.getNextMessage(queue, consumeTimeout);
+ if (result != null)
+ {
+
+ long dt = ((AbstractJMSMessage) result).getDeliveryTag();
+
+ if (testReception)
+ {
+ int msgindex = result.getIntProperty("index");
+ if (receieved[msgindex] != 0)
+ {
+ _logger.error("Received Message(" + msgindex + ":"
+ + ((AbstractJMSMessage) result).getDeliveryTag() + ") more than once.");
+ }
+
+ if (_logger.isInfoEnabled())
+ {
+ _logger.info("Received Message(" + System.identityHashCode(msgindex) + ") " + "DT:" + dt
+ + "IN:" + msgindex);
+ }
+
+ if (dt == 0)
+ {
+ _logger.error("DT is zero for msg:" + msgindex);
+ }
+
+ receieved[msgindex] = dt;
+ }
+
+ count++;
+ if ((count % 100) == 0)
+ {
+ _logger.info("consumer-" + id + ": got " + result + ", new count is " + count);
+ }
+ }
+ }
+ while (result != null);
+
+ _logger.info("consumer-" + id + ": complete");
+ conn.disconnect();
+
+ }
+ catch (Exception e)
+ {
+ _logger.error("Consumer run error",e);
+ }
+ }
+
+ public Integer getCount()
+ {
+ return count;
+ }
+
+ public Integer getId()
+ {
+ return id;
+ }
+ }
+
+ public void testRequeue() throws JMSException, AMQException, URLSyntaxException
+ {
+ int run = 0;
+ // while (run < 10)
+ {
+ run++;
+
+ if (_logger.isInfoEnabled())
+ {
+ _logger.info("testRequeue run " + run);
+ }
+
+ String virtualHost = "/test";
+ String brokerlist = BROKER;
+ String brokerUrl = "amqp://guest:guest@" + virtualHost + "?brokerlist='" + brokerlist + "'";
+ QpidClientConnection qpc = new QpidClientConnection(BROKER);
+ qpc.connect();
+ Connection conn = qpc. getConnection();
+
+ Session session = conn.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ Queue q = session.createQueue(queue);
+
+ _logger.debug("Create Consumer");
+ MessageConsumer consumer = session.createConsumer(q);
+
+ conn.start();
+
+ _logger.debug("Receiving msg");
+ Message msg = consumer.receive(2000);
+
+ assertNotNull("Message should not be null", msg);
+
+ // As we have not ack'd message will be requeued.
+ _logger.debug("Close Consumer");
+ consumer.close();
+
+ _logger.debug("Close Connection");
+ conn.close();
+ }
+ }
+
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/close/TopicPublisherCloseTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/close/TopicPublisherCloseTest.java
new file mode 100644
index 0000000000..957063b2e1
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/close/TopicPublisherCloseTest.java
@@ -0,0 +1,69 @@
+/*
+ *
+ * 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.qpid.test.unit.close;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQTopic;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+import javax.jms.Session;
+import javax.jms.Topic;
+import javax.jms.TopicPublisher;
+import javax.jms.TopicSession;
+
+/**
+ * @author Apache Software Foundation
+ */
+public class TopicPublisherCloseTest extends QpidBrokerTestCase
+{
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ }
+
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ }
+
+ public void testAllMethodsThrowAfterConnectionClose() throws Exception
+ {
+ // give external brokers a chance to start up
+ Thread.sleep(3000);
+
+ AMQConnection connection = (AMQConnection) getConnection("guest", "guest");
+
+ Topic destination1 = new AMQTopic(connection, "t1");
+ TopicSession session1 = connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
+ TopicPublisher pub = session1.createPublisher(destination1);
+ connection.close();
+ try
+ {
+ pub.getDeliveryMode();
+ fail("Expected exception not thrown");
+ }
+ catch (javax.jms.IllegalStateException e)
+ {
+ // PASS
+ }
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/ct/DurableSubscriberTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/ct/DurableSubscriberTest.java
new file mode 100644
index 0000000000..c292c718bb
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/ct/DurableSubscriberTest.java
@@ -0,0 +1,503 @@
+/* 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.qpid.test.unit.ct;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.AMQTopic;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+import javax.jms.Connection;
+import javax.jms.Message;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import javax.jms.Topic;
+import javax.jms.TopicConnection;
+import javax.jms.TopicConnectionFactory;
+import javax.jms.TopicPublisher;
+import javax.jms.TopicSession;
+import javax.jms.TopicSubscriber;
+
+/**
+ * Crash Recovery tests for durable subscription
+ *
+ */
+public class DurableSubscriberTest extends QpidBrokerTestCase
+{
+ private final String _topicName = "durableSubscriberTopic";
+
+ /**
+ * test strategy:
+ * create and register a durable subscriber then close it
+ * create a publisher and send a persistant message followed by a non persistant message
+ * crash and restart the broker
+ * recreate the durable subscriber and check that only the first message is received
+ */
+ public void testDurSubRestoredAfterNonPersistentMessageSent() throws Exception
+ {
+ if (isBrokerStorePersistent())
+ {
+ TopicConnectionFactory factory = getConnectionFactory();
+ Topic topic = (Topic) getInitialContext().lookup(_topicName);
+ //create and register a durable subscriber then close it
+ TopicConnection durConnection = factory.createTopicConnection("guest", "guest");
+ TopicSession durSession = durConnection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
+ TopicSubscriber durSub1 = durSession.createDurableSubscriber(topic, "dursub");
+ durConnection.start();
+ durSub1.close();
+ durSession.close();
+ durConnection.stop();
+
+ //create a publisher and send a persistant message followed by a non persistant message
+ TopicConnection pubConnection = factory.createTopicConnection("guest", "guest");
+ TopicSession pubSession = pubConnection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
+ TopicPublisher publisher = pubSession.createPublisher(topic);
+ Message message = pubSession.createMessage();
+ message.setIntProperty("count", 1);
+ publisher.publish(message, javax.jms.DeliveryMode.PERSISTENT, javax.jms.Message.DEFAULT_PRIORITY,
+ javax.jms.Message.DEFAULT_TIME_TO_LIVE);
+ message.setIntProperty("count", 2);
+ publisher.publish(message, javax.jms.DeliveryMode.NON_PERSISTENT, javax.jms.Message.DEFAULT_PRIORITY,
+ javax.jms.Message.DEFAULT_TIME_TO_LIVE);
+ publisher.close();
+ pubSession.close();
+ //now stop the server
+ try
+ {
+ restartBroker();
+ }
+ catch (Exception e)
+ {
+ _logger.error("problems restarting broker: " + e);
+ throw e;
+ }
+ //now recreate the durable subscriber and check the received messages
+ factory = getConnectionFactory();
+ topic = (Topic) getInitialContext().lookup(_topicName);
+ TopicConnection durConnection2 = factory.createTopicConnection("guest", "guest");
+ TopicSession durSession2 = durConnection2.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
+ TopicSubscriber durSub2 = durSession2.createDurableSubscriber(topic, "dursub");
+ durConnection2.start();
+ Message m1 = durSub2.receive(1000);
+ if (m1 == null)
+ {
+ assertTrue("testDurSubRestoredAfterNonPersistentMessageSent test failed. no message was returned",
+ false);
+ }
+ assertTrue("testDurSubRestoredAfterNonPersistentMessageSent test failed. Wrong message was returned.",
+ m1.getIntProperty("count") == 1);
+ durSession2.unsubscribe("dursub");
+ durConnection2.close();
+ }
+ }
+
+ /**
+ * create and register a durable subscriber with a message selector and then close it
+ * crash the broker
+ * create a publisher and send 5 right messages and 5 wrong messages
+ * recreate the durable subscriber and check we receive the 5 expected messages
+ */
+ public void testDurSubRestoresMessageSelector() throws Exception
+ {
+ if (isBrokerStorePersistent())
+ {
+ TopicConnectionFactory factory = getConnectionFactory();
+ Topic topic = (Topic) getInitialContext().lookup(_topicName);
+ //create and register a durable subscriber with a message selector and then close it
+ TopicConnection durConnection = factory.createTopicConnection("guest", "guest");
+ TopicSession durSession = durConnection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
+ TopicSubscriber durSub1 = durSession.createDurableSubscriber(topic, "dursub", "testprop='true'", false);
+ durConnection.start();
+ durSub1.close();
+ durSession.close();
+ durConnection.stop();
+ //now stop the server
+ try
+ {
+ restartBroker();
+ }
+ catch (Exception e)
+ {
+ _logger.error("problems restarting broker: " + e);
+ throw e;
+ }
+ topic = (Topic) getInitialContext().lookup(_topicName);
+ factory = getConnectionFactory();
+ TopicConnection pubConnection = factory.createTopicConnection("guest", "guest");
+ TopicSession pubSession = pubConnection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
+ TopicPublisher publisher = pubSession.createPublisher(topic);
+ for (int i = 0; i < 5; i++)
+ {
+ Message message = pubSession.createMessage();
+ message.setStringProperty("testprop", "true");
+ publisher.publish(message);
+ message = pubSession.createMessage();
+ message.setStringProperty("testprop", "false");
+ publisher.publish(message);
+ }
+ publisher.close();
+ pubSession.close();
+
+ //now recreate the durable subscriber and check the received messages
+ TopicConnection durConnection2 = factory.createTopicConnection("guest", "guest");
+ TopicSession durSession2 = durConnection2.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
+ TopicSubscriber durSub2 = durSession2.createDurableSubscriber(topic, "dursub", "testprop='true'", false);
+ durConnection2.start();
+ for (int i = 0; i < 5; i++)
+ {
+ Message message = durSub2.receive(1000);
+ if (message == null)
+ {
+ assertTrue("testDurSubRestoresMessageSelector test failed. no message was returned", false);
+ }
+ else
+ {
+ assertTrue("testDurSubRestoresMessageSelector test failed. message selector not reset",
+ message.getStringProperty("testprop").equals("true"));
+ }
+ }
+ durSession2.unsubscribe("dursub");
+ durConnection2.close();
+ }
+ }
+
+ /**
+ * create and register a durable subscriber without a message selector and then unsubscribe it
+ * create and register a durable subscriber with a message selector and then close it
+ * restart the broker
+ * send matching and non matching messages
+ * recreate and register the durable subscriber with a message selector
+ * verify only the matching messages are received
+ */
+ public void testDurSubChangedToHaveSelectorThenRestart() throws Exception
+ {
+ if (! isBrokerStorePersistent())
+ {
+ _logger.warn("Test skipped due to requirement of a persistent store");
+ return;
+ }
+
+ final String SUB_NAME=getTestQueueName();
+
+ TopicConnectionFactory factory = getConnectionFactory();
+ Topic topic = (Topic) getInitialContext().lookup(_topicName);
+
+ //create and register a durable subscriber then unsubscribe it
+ TopicConnection durConnection = factory.createTopicConnection("guest", "guest");
+ TopicSession durSession = durConnection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
+ TopicSubscriber durSub1 = durSession.createDurableSubscriber(topic, SUB_NAME);
+ durConnection.start();
+ durSub1.close();
+ durSession.unsubscribe(SUB_NAME);
+ durSession.close();
+ durConnection.close();
+
+ //create and register a durable subscriber with a message selector and then close it
+ TopicConnection durConnection2 = factory.createTopicConnection("guest", "guest");
+ TopicSession durSession2 = durConnection2.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
+ TopicSubscriber durSub2 = durSession2.createDurableSubscriber(topic, SUB_NAME, "testprop='true'", false);
+ durConnection2.start();
+ durSub2.close();
+ durSession2.close();
+ durConnection2.close();
+
+ //now restart the server
+ try
+ {
+ restartBroker();
+ }
+ catch (Exception e)
+ {
+ _logger.error("problems restarting broker: " + e);
+ throw e;
+ }
+
+ //send messages matching and not matching the selector
+ TopicConnection pubConnection = factory.createTopicConnection("guest", "guest");
+ TopicSession pubSession = pubConnection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
+ TopicPublisher publisher = pubSession.createPublisher(topic);
+ for (int i = 0; i < 5; i++)
+ {
+ Message message = pubSession.createMessage();
+ message.setStringProperty("testprop", "true");
+ publisher.publish(message);
+ message = pubSession.createMessage();
+ message.setStringProperty("testprop", "false");
+ publisher.publish(message);
+ }
+ publisher.close();
+ pubSession.close();
+
+ //now recreate the durable subscriber with selector to check there are no exceptions generated
+ //and then verify the messages are received correctly
+ TopicConnection durConnection3 = (TopicConnection) factory.createConnection("guest", "guest");
+ TopicSession durSession3 = (TopicSession) durConnection3.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ TopicSubscriber durSub3 = durSession3.createDurableSubscriber(topic, SUB_NAME, "testprop='true'", false);
+ durConnection3.start();
+
+ for (int i = 0; i < 5; i++)
+ {
+ Message message = durSub3.receive(2000);
+ if (message == null)
+ {
+ fail("testDurSubChangedToHaveSelectorThenRestart test failed. Expected message " + i + " was not returned");
+ }
+ else
+ {
+ assertTrue("testDurSubChangedToHaveSelectorThenRestart test failed. Got message not matching selector",
+ message.getStringProperty("testprop").equals("true"));
+ }
+ }
+
+ durSub3.close();
+ durSession3.unsubscribe(SUB_NAME);
+ durSession3.close();
+ durConnection3.close();
+ }
+
+
+ /**
+ * create and register a durable subscriber with a message selector and then unsubscribe it
+ * create and register a durable subscriber without a message selector and then close it
+ * restart the broker
+ * send matching and non matching messages
+ * recreate and register the durable subscriber without a message selector
+ * verify ALL the sent messages are received
+ */
+ public void testDurSubChangedToNotHaveSelectorThenRestart() throws Exception
+ {
+ if (! isBrokerStorePersistent())
+ {
+ _logger.warn("Test skipped due to requirement of a persistent store");
+ return;
+ }
+
+ final String SUB_NAME=getTestQueueName();
+
+ TopicConnectionFactory factory = getConnectionFactory();
+ Topic topic = (Topic) getInitialContext().lookup(_topicName);
+
+ //create and register a durable subscriber with selector then unsubscribe it
+ TopicConnection durConnection = factory.createTopicConnection("guest", "guest");
+ TopicSession durSession = durConnection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
+ TopicSubscriber durSub1 = durSession.createDurableSubscriber(topic, SUB_NAME, "testprop='true'", false);
+ durConnection.start();
+ durSub1.close();
+ durSession.unsubscribe(SUB_NAME);
+ durSession.close();
+ durConnection.close();
+
+ //create and register a durable subscriber without the message selector and then close it
+ TopicConnection durConnection2 = factory.createTopicConnection("guest", "guest");
+ TopicSession durSession2 = durConnection2.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
+ TopicSubscriber durSub2 = durSession2.createDurableSubscriber(topic, SUB_NAME);
+ durConnection2.start();
+ durSub2.close();
+ durSession2.close();
+ durConnection2.close();
+
+ //now restart the server
+ try
+ {
+ restartBroker();
+ }
+ catch (Exception e)
+ {
+ _logger.error("problems restarting broker: " + e);
+ throw e;
+ }
+
+ //send messages matching and not matching the original used selector
+ TopicConnection pubConnection = factory.createTopicConnection("guest", "guest");
+ TopicSession pubSession = pubConnection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
+ TopicPublisher publisher = pubSession.createPublisher(topic);
+ for (int i = 1; i <= 5; i++)
+ {
+ Message message = pubSession.createMessage();
+ message.setStringProperty("testprop", "true");
+ publisher.publish(message);
+ message = pubSession.createMessage();
+ message.setStringProperty("testprop", "false");
+ publisher.publish(message);
+ }
+ publisher.close();
+ pubSession.close();
+
+ //now recreate the durable subscriber without selector to check there are no exceptions generated
+ //then verify ALL messages sent are received
+ TopicConnection durConnection3 = (TopicConnection) factory.createConnection("guest", "guest");
+ TopicSession durSession3 = (TopicSession) durConnection3.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ TopicSubscriber durSub3 = durSession3.createDurableSubscriber(topic, SUB_NAME);
+ durConnection3.start();
+
+ for (int i = 1; i <= 10; i++)
+ {
+ Message message = durSub3.receive(2000);
+ if (message == null)
+ {
+ fail("testDurSubChangedToNotHaveSelectorThenRestart test failed. Expected message " + i + " was not received");
+ }
+ }
+
+ durSub3.close();
+ durSession3.unsubscribe(SUB_NAME);
+ durSession3.close();
+ durConnection3.close();
+ }
+
+
+ public void testResubscribeWithChangedSelectorAndRestart() throws Exception
+ {
+ if (! isBrokerStorePersistent())
+ {
+ _logger.warn("Test skipped due to requirement of a persistent store");
+ return;
+ }
+
+ Connection conn = getConnection();
+ conn.start();
+ Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ AMQTopic topic = new AMQTopic((AMQConnection) conn, "testResubscribeWithChangedSelectorAndRestart");
+ MessageProducer producer = session.createProducer(topic);
+
+ // Create durable subscriber that matches A
+ TopicSubscriber subA = session.createDurableSubscriber(topic,
+ "testResubscribeWithChangedSelectorAndRestart",
+ "Match = True", false);
+
+ // Send 1 matching message and 1 non-matching message
+ TextMessage msg = session.createTextMessage("testResubscribeWithChangedSelectorAndRestart1");
+ msg.setBooleanProperty("Match", true);
+ producer.send(msg);
+ msg = session.createTextMessage("testResubscribeWithChangedSelectorAndRestart2");
+ msg.setBooleanProperty("Match", false);
+ producer.send(msg);
+
+ Message rMsg = subA.receive(1000);
+ assertNotNull(rMsg);
+ assertEquals("Content was wrong",
+ "testResubscribeWithChangedSelectorAndRestart1",
+ ((TextMessage) rMsg).getText());
+
+ // Queue has no messages left
+ AMQQueue subQueue = new AMQQueue("amq.topic", "clientid" + ":" + "testResubscribeWithChangedSelectorAndRestart");
+ assertEquals("Msg count should be 0", 0, ((AMQSession<?, ?>) session).getQueueDepth(subQueue, true));
+
+ rMsg = subA.receive(1000);
+ assertNull(rMsg);
+
+ // Send another 1 matching message and 1 non-matching message
+ msg = session.createTextMessage("testResubscribeWithChangedSelectorAndRestart1");
+ msg.setBooleanProperty("Match", true);
+ producer.send(msg);
+ msg = session.createTextMessage("testResubscribeWithChangedSelectorAndRestart2");
+ msg.setBooleanProperty("Match", false);
+ producer.send(msg);
+
+ // Disconnect subscriber without receiving the message to
+ //leave it on the underlying queue
+ subA.close();
+
+ // Reconnect with new selector that matches B
+ TopicSubscriber subB = session.createDurableSubscriber(topic,
+ "testResubscribeWithChangedSelectorAndRestart",
+ "Match = false", false);
+
+ //verify no messages are now present on the queue as changing selector should have issued
+ //an unsubscribe and thus deleted the previous durable backing queue for the subscription.
+ //check the dur sub's underlying queue now has msg count 0
+ assertEquals("Msg count should be 0", 0, ((AMQSession<?, ?>) session).getQueueDepth(subQueue, true));
+
+ // Check that new messages are received properly
+ msg = session.createTextMessage("testResubscribeWithChangedSelectorAndRestart1");
+ msg.setBooleanProperty("Match", true);
+ producer.send(msg);
+ msg = session.createTextMessage("testResubscribeWithChangedSelectorAndRestart2");
+ msg.setBooleanProperty("Match", false);
+ producer.send(msg);
+
+ rMsg = subB.receive(1000);
+ assertNotNull(rMsg);
+ assertEquals("Content was wrong",
+ "testResubscribeWithChangedSelectorAndRestart2",
+ ((TextMessage) rMsg).getText());
+
+ rMsg = subB.receive(1000);
+ assertNull(rMsg);
+
+ //check the dur sub's underlying queue now has msg count 0
+ assertEquals("Msg count should be 0", 0, ((AMQSession<?, ?>) session).getQueueDepth(subQueue, true));
+ conn.close();
+
+ //now restart the server
+ try
+ {
+ restartBroker();
+ }
+ catch (Exception e)
+ {
+ _logger.error("problems restarting broker: " + e);
+ throw e;
+ }
+
+ // Reconnect to broker
+ Connection connection = getConnectionFactory().createConnection("guest", "guest");
+ connection.start();
+ session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ topic = new AMQTopic((AMQConnection) connection, "testResubscribeWithChangedSelectorAndRestart");
+ producer = session.createProducer(topic);
+
+ //verify no messages now present on the queue after we restart the broker
+ //check the dur sub's underlying queue now has msg count 0
+ assertEquals("Msg count should be 0", 0, ((AMQSession<?, ?>) session).getQueueDepth(subQueue, true));
+
+ // Reconnect with new selector that matches B
+ TopicSubscriber subC = session.createDurableSubscriber(topic,
+ "testResubscribeWithChangedSelectorAndRestart",
+ "Match = False", false);
+
+ // Check that new messages are still sent and recieved properly
+ msg = session.createTextMessage("testResubscribeWithChangedSelectorAndRestart1");
+ msg.setBooleanProperty("Match", true);
+ producer.send(msg);
+ msg = session.createTextMessage("testResubscribeWithChangedSelectorAndRestart2");
+ msg.setBooleanProperty("Match", false);
+ producer.send(msg);
+
+ //check the dur sub's underlying queue now has msg count 1
+ assertEquals("Msg count should be 1", 1, ((AMQSession<?, ?>) session).getQueueDepth(subQueue, true));
+
+ rMsg = subC.receive(1000);
+ assertNotNull(rMsg);
+ assertEquals("Content was wrong",
+ "testResubscribeWithChangedSelectorAndRestart2",
+ ((TextMessage) rMsg).getText());
+
+ rMsg = subC.receive(1000);
+ assertNull(rMsg);
+
+ session.unsubscribe("testResubscribeWithChangedSelectorAndRestart");
+
+ subC.close();
+ session.close();
+ connection.close();
+ }
+}
+
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/message/JMSPropertiesTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/message/JMSPropertiesTest.java
new file mode 100644
index 0000000000..3f2d6f92ab
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/message/JMSPropertiesTest.java
@@ -0,0 +1,206 @@
+/*
+ *
+ * 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.qpid.test.unit.message;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.qpid.AMQPInvalidClassException;
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.message.NonQpidObjectMessage;
+import org.apache.qpid.client.message.QpidMessageProperties;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+import javax.jms.Connection;
+import javax.jms.Destination;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageFormatException;
+import javax.jms.MessageProducer;
+import javax.jms.ObjectMessage;
+import javax.jms.Queue;
+import javax.jms.Session;
+import javax.jms.Topic;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author Apache Software Foundation
+ */
+public class JMSPropertiesTest extends QpidBrokerTestCase
+{
+
+ private static final Logger _logger = LoggerFactory.getLogger(JMSPropertiesTest.class);
+
+ public String _connectionString = "vm://:1";
+
+ public static final String JMS_CORR_ID = "QPIDID_01";
+ public static final int JMS_DELIV_MODE = 1;
+ public static final String JMS_TYPE = "test.jms.type";
+ protected static final String NULL_OBJECT_PROPERTY = "NullObject";
+ protected static final String INVALID_OBJECT_PROPERTY = "InvalidObject";
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ }
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ }
+
+ public void testJMSProperties() throws Exception
+ {
+ AMQConnection con = (AMQConnection) getConnection("guest", "guest");
+ AMQSession consumerSession = (AMQSession) con.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ Queue queue =
+ new AMQQueue(con.getDefaultQueueExchangeName(), new AMQShortString("someQ"), new AMQShortString("someQ"), false,
+ true);
+ MessageConsumer consumer = consumerSession.createConsumer(queue);
+
+ AMQConnection con2 = (AMQConnection) getConnection("guest", "guest");
+ Session producerSession = con2.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ MessageProducer producer = producerSession.createProducer(queue);
+ Destination JMS_REPLY_TO = new AMQQueue(con2, "my.replyto");
+ // create a test message to send
+ ObjectMessage sentMsg = new NonQpidObjectMessage(producerSession);
+ sentMsg.setJMSCorrelationID(JMS_CORR_ID);
+ sentMsg.setJMSDeliveryMode(JMS_DELIV_MODE);
+ sentMsg.setJMSType(JMS_TYPE);
+ sentMsg.setJMSReplyTo(JMS_REPLY_TO);
+
+ String JMSXGroupID_VALUE = "group";
+ sentMsg.setStringProperty("JMSXGroupID", JMSXGroupID_VALUE);
+
+ int JMSXGroupSeq_VALUE = 1;
+ sentMsg.setIntProperty("JMSXGroupSeq", JMSXGroupSeq_VALUE);
+
+ try
+ {
+ sentMsg.setObjectProperty(NULL_OBJECT_PROPERTY, null);
+ fail("Null Object Property value set");
+ }
+ catch (MessageFormatException mfe)
+ {
+ // Check the error message
+ assertEquals("Incorrect error message", AMQPInvalidClassException.INVALID_OBJECT_MSG + "null", mfe.getMessage());
+ }
+
+ try
+ {
+ sentMsg.setObjectProperty(INVALID_OBJECT_PROPERTY, new Exception());
+ fail("Non primitive Object Property value set");
+ }
+ catch (MessageFormatException mfe)
+ {
+ // Check the error message
+ assertEquals("Incorrect error message: " + mfe.getMessage(), AMQPInvalidClassException.INVALID_OBJECT_MSG + Exception.class, mfe.getMessage());
+ }
+
+ // send it
+ producer.send(sentMsg);
+
+ con2.close();
+
+ con.start();
+
+ // get message and check JMS properties
+ ObjectMessage rm = (ObjectMessage) consumer.receive(2000);
+ assertNotNull(rm);
+
+ assertEquals("JMS Correlation ID mismatch", sentMsg.getJMSCorrelationID(), rm.getJMSCorrelationID());
+ // TODO: Commented out as always overwritten by send delivery mode value - prob should not set in conversion
+ // assertEquals("JMS Delivery Mode mismatch",sentMsg.getJMSDeliveryMode(),rm.getJMSDeliveryMode());
+ assertEquals("JMS Type mismatch", sentMsg.getJMSType(), rm.getJMSType());
+ assertEquals("JMS Reply To mismatch", sentMsg.getJMSReplyTo(), rm.getJMSReplyTo());
+ assertTrue("JMSMessageID Does not start ID:", rm.getJMSMessageID().startsWith("ID:"));
+ assertEquals("JMS Default priority should be 4",Message.DEFAULT_PRIORITY,rm.getJMSPriority());
+
+ //Validate that the JMSX values are correct
+ assertEquals("JMSXGroupID is not as expected:", JMSXGroupID_VALUE, rm.getStringProperty("JMSXGroupID"));
+ assertEquals("JMSXGroupSeq is not as expected:", JMSXGroupSeq_VALUE, rm.getIntProperty("JMSXGroupSeq"));
+
+ boolean JMSXGroupID_Available = false;
+ boolean JMSXGroupSeq_Available = false;
+ Enumeration props = con.getMetaData().getJMSXPropertyNames();
+ while (props.hasMoreElements())
+ {
+ String name = (String) props.nextElement();
+ if (name.equals("JMSXGroupID"))
+ {
+ JMSXGroupID_Available = true;
+ }
+ if (name.equals("JMSXGroupSeq"))
+ {
+ JMSXGroupSeq_Available = true;
+ }
+ }
+
+ assertTrue("JMSXGroupID not available.",JMSXGroupID_Available);
+ assertTrue("JMSXGroupSeq not available.",JMSXGroupSeq_Available);
+
+ // Check that the NULL_OBJECT_PROPERTY was not set or transmitted.
+ assertFalse(NULL_OBJECT_PROPERTY + " was not set.", rm.propertyExists(NULL_OBJECT_PROPERTY));
+
+ con.close();
+ }
+
+ /**
+ * Test Goal : Test if custom message properties can be set and retrieved properly with out an error.
+ * Also test if unsupported properties are filtered out. See QPID-2930.
+ */
+ public void testQpidExtensionProperties() throws Exception
+ {
+ Connection con = getConnection("guest", "guest");
+ Session ssn = (AMQSession) con.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ con.start();
+
+ Topic topic = ssn.createTopic("test");
+ MessageConsumer consumer = ssn.createConsumer(topic);
+ MessageProducer prod = ssn.createProducer(topic);
+ Message m = ssn.createMessage();
+ m.setObjectProperty("foo-bar", "foobar".getBytes());
+ m.setObjectProperty(QpidMessageProperties.AMQP_0_10_APP_ID, "my-app-id");
+ prod.send(m);
+
+ Message msg = consumer.receive(1000);
+ assertNotNull(msg);
+
+ Enumeration<String> enu = msg.getPropertyNames();
+ Map<String,String> map = new HashMap<String,String>();
+ while (enu.hasMoreElements())
+ {
+ String name = enu.nextElement();
+ String value = msg.getStringProperty(name);
+ map.put(name, value);
+ }
+
+ assertFalse("Property 'foo-bar' should have been filtered out",map.containsKey("foo-bar"));
+ assertEquals("Property "+ QpidMessageProperties.AMQP_0_10_APP_ID + " should be present","my-app-id",msg.getStringProperty(QpidMessageProperties.AMQP_0_10_APP_ID));
+ assertEquals("Property "+ QpidMessageProperties.AMQP_0_10_ROUTING_KEY + " should be present","test",msg.getStringProperty(QpidMessageProperties.AMQP_0_10_ROUTING_KEY));
+
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/message/StreamMessageTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/message/StreamMessageTest.java
new file mode 100644
index 0000000000..f8ab593c88
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/message/StreamMessageTest.java
@@ -0,0 +1,165 @@
+/*
+ *
+ * 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.qpid.test.unit.message;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQHeadersExchange;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.configuration.ClientProperties;
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+import org.apache.qpid.url.AMQBindingURL;
+import org.apache.qpid.url.BindingURL;
+
+import javax.jms.Connection;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageEOFException;
+import javax.jms.MessageListener;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+import javax.jms.StreamMessage;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * @author Apache Software Foundation
+ */
+public class StreamMessageTest extends QpidBrokerTestCase
+{
+ private static final Logger _logger = LoggerFactory.getLogger(StreamMessageTest.class);
+
+ public void testStreamMessageEOF() throws Exception
+ {
+ AMQConnection con = (AMQConnection) getConnection("guest", "guest");
+ AMQSession consumerSession = (AMQSession) con.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+
+ AMQHeadersExchange queue =
+ new AMQHeadersExchange(new AMQBindingURL(
+ ExchangeDefaults.HEADERS_EXCHANGE_CLASS + "://" + ExchangeDefaults.HEADERS_EXCHANGE_NAME
+ + "/test/queue1?" + BindingURL.OPTION_ROUTING_KEY + "='F0000=1'"));
+
+ FieldTable ft = new FieldTable();
+ ft.setString("x-match", "any");
+ ft.setString("F1000", "1");
+ consumerSession.declareAndBind(queue, ft);
+ MessageConsumer consumer = consumerSession.createConsumer(queue);
+ // force synch to ensure the consumer has resulted in a bound queue
+ // ((AMQSession) consumerSession).declareExchangeSynch(ExchangeDefaults.HEADERS_EXCHANGE_NAME, ExchangeDefaults.HEADERS_EXCHANGE_CLASS);
+ // This is the default now
+
+ Connection con2 = (AMQConnection) getConnection("guest", "guest");
+
+ AMQSession producerSession = (AMQSession) con2.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+
+ // Need to start the "producer" connection in order to receive bounced messages
+ _logger.info("Starting producer connection");
+ con2.start();
+
+ MessageProducer mandatoryProducer = producerSession.createProducer(queue);
+
+ // Third test - should be routed
+ _logger.info("Sending isBound message");
+ StreamMessage msg = producerSession.createStreamMessage();
+
+ msg.setStringProperty("F1000", "1");
+
+ msg.writeByte((byte) 42);
+
+ mandatoryProducer.send(msg);
+
+ _logger.info("Starting consumer connection");
+ con.start();
+
+ StreamMessage msg2 = (StreamMessage) consumer.receive(2000);
+ assertNotNull(msg2);
+
+ msg2.readByte();
+ try
+ {
+ msg2.readByte();
+ fail("Expected exception not thrown");
+ }
+ catch (Exception e)
+ {
+ assertTrue("Expected MessageEOFException: " + e, e instanceof MessageEOFException);
+ }
+ con.close();
+ con2.close();
+ }
+
+ public void testModifyReceivedMessageExpandsBuffer() throws Exception
+ {
+ final CountDownLatch awaitMessages = new CountDownLatch(1);
+ final AtomicReference<Throwable> listenerCaughtException = new AtomicReference<Throwable>();
+
+ AMQConnection con = (AMQConnection) getConnection("guest", "guest");
+ AMQSession consumerSession = (AMQSession) con.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ AMQQueue queue = new AMQQueue(con.getDefaultQueueExchangeName(), new AMQShortString("testQ"));
+ MessageConsumer consumer = consumerSession.createConsumer(queue);
+ consumer.setMessageListener(new MessageListener()
+ {
+
+ public void onMessage(Message message)
+ {
+ final StreamMessage sm = (StreamMessage) message;
+ try
+ {
+ sm.clearBody();
+ // it is legal to extend a stream message's content
+ sm.writeString("dfgjshfslfjshflsjfdlsjfhdsljkfhdsljkfhsd");
+ }
+ catch (Throwable t)
+ {
+ listenerCaughtException.set(t);
+ }
+ finally
+ {
+ awaitMessages.countDown();
+ }
+ }
+ });
+
+ Connection con2 = (AMQConnection) getConnection("guest", "guest");
+ AMQSession producerSession = (AMQSession) con2.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ MessageProducer producer = producerSession.createProducer(queue);
+ con.start();
+ StreamMessage sm = producerSession.createStreamMessage();
+ sm.writeInt(42);
+ producer.send(sm);
+
+ // Allow up to five seconds for the message to arrive with the consumer
+ final boolean completed = awaitMessages.await(5, TimeUnit.SECONDS);
+ assertTrue("Message did not arrive with consumer within a reasonable time", completed);
+ final Throwable listenerException = listenerCaughtException.get();
+ assertNull("No exception should be caught by listener : " + listenerException, listenerException);
+
+ con.close();
+ con2.close();
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/message/UTF8Test.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/message/UTF8Test.java
new file mode 100644
index 0000000000..cc95afafa2
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/message/UTF8Test.java
@@ -0,0 +1,97 @@
+/*
+ *
+ * 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.qpid.test.unit.message;
+
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+import javax.jms.Connection;
+import javax.jms.Destination;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import javax.naming.InitialContext;
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.Properties;
+
+
+/**
+ * This test makes sure that utf8 characters can be used for
+ * specifying exchange, queue name and routing key.
+ *
+ * those tests are related to qpid-1384
+ */
+public class UTF8Test extends QpidBrokerTestCase
+{
+ public void testPlainEn() throws Exception
+ {
+ invoke("UTF8En");
+ }
+
+
+ public void testUTF8Jp() throws Exception
+ {
+ invoke("UTF8Jp");
+ }
+
+ private void invoke(String name) throws Exception
+ {
+ InputStream stream = getClass().getClassLoader().getResourceAsStream("org/apache/qpid/test/unit/message/" + name);
+
+ BufferedReader in = new BufferedReader(new InputStreamReader(stream, "UTF8"));
+ runTest(in.readLine(), in.readLine(), in.readLine(), in.readLine());
+ in.close();
+ }
+
+ private void runTest(String exchangeName, String queueName, String routingKey, String data) throws Exception
+ {
+ Connection con = getConnection();
+ Session sess = con.createSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE);
+ final Destination dest = getDestination(exchangeName, routingKey, queueName);
+
+ final MessageConsumer msgCons = sess.createConsumer(dest);
+ con.start();
+
+ // Send data
+ MessageProducer msgProd = sess.createProducer(dest);
+ TextMessage message = sess.createTextMessage(data);
+ msgProd.send(message);
+
+ // consume data
+ TextMessage m = (TextMessage) msgCons.receive(RECEIVE_TIMEOUT);
+ assertNotNull(m);
+ assertEquals(m.getText(), data);
+ }
+
+ private Destination getDestination(String exch, String routkey, String qname) throws Exception
+ {
+ Properties props = new Properties();
+ props.setProperty("destination.directUTF8Queue",
+ "direct://" + exch + "//" + qname + "?autodelete='false'&durable='false'"
+ + "&routingkey='" + routkey + "'");
+
+ // Get our connection context
+ InitialContext ctx = new InitialContext(props);
+ return (Destination) ctx.lookup("directUTF8Queue");
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java
new file mode 100644
index 0000000000..cc8bfb9433
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java
@@ -0,0 +1,1063 @@
+/*
+ *
+ * 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.qpid.test.unit.topic;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQNoRouteException;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.AMQTopic;
+import org.apache.qpid.management.common.JMXConnnectionFactory;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+import javax.jms.Connection;
+import javax.jms.ExceptionListener;
+import javax.jms.InvalidDestinationException;
+import javax.jms.InvalidSelectorException;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import javax.jms.Topic;
+import javax.jms.TopicSubscriber;
+import javax.management.MBeanServerConnection;
+import javax.management.ObjectName;
+import javax.management.remote.JMXConnector;
+import java.io.IOException;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @todo Code to check that a consumer gets only one particular method could be factored into a re-usable method (as
+ * a static on a base test helper class, e.g. TestUtils.
+ *
+ * @todo Code to create test end-points using session per connection, or all sessions on one connection, to be factored
+ * out to make creating this test variation simpler. Want to make this variation available through LocalCircuit,
+ * driven by the test model.
+ */
+public class DurableSubscriptionTest extends QpidBrokerTestCase
+{
+ private static final Logger _logger = LoggerFactory.getLogger(DurableSubscriptionTest.class);
+
+ private static final String MY_TOPIC = "MyTopic";
+
+ private static final String MY_SUBSCRIPTION = "MySubscription";
+
+ /** Timeout for receive() if we are expecting a message */
+ private static final long POSITIVE_RECEIVE_TIMEOUT = 2000;
+
+ /** Timeout for receive() if we are not expecting a message */
+ private static final long NEGATIVE_RECEIVE_TIMEOUT = 1000;
+
+ private JMXConnector _jmxc;
+ private MBeanServerConnection _mbsc;
+ private static final String USER = "admin";
+ private static final String PASSWORD = "admin";
+ private boolean _jmxConnected;
+
+ public void setUp() throws Exception
+ {
+ getBrokerConfiguration().addJmxManagementConfiguration();
+ _jmxConnected=false;
+ super.setUp();
+ }
+
+ public void tearDown() throws Exception
+ {
+ try
+ {
+ if(_jmxConnected)
+ {
+ try
+ {
+ _jmxc.close();
+ }
+ catch (IOException e)
+ {
+ _logger.error("Error closing JMX connection", e);
+ }
+ }
+ }
+ finally
+ {
+ super.tearDown();
+ }
+ }
+
+ public void testUnsubscribe() throws Exception
+ {
+ AMQConnection con = (AMQConnection) getConnection();
+ AMQTopic topic = new AMQTopic(con, "MyDurableSubscriptionTestTopic");
+ _logger.info("Create Session 1");
+ Session session1 = con.createSession(false, AMQSession.NO_ACKNOWLEDGE);
+ _logger.info("Create Consumer on Session 1");
+ MessageConsumer consumer1 = session1.createConsumer(topic);
+ _logger.info("Create Producer on Session 1");
+ MessageProducer producer = session1.createProducer(topic);
+
+ _logger.info("Create Session 2");
+ Session session2 = con.createSession(false, AMQSession.NO_ACKNOWLEDGE);
+ _logger.info("Create Durable Subscriber on Session 2");
+ TopicSubscriber consumer2 = session2.createDurableSubscriber(topic, MY_SUBSCRIPTION);
+
+ _logger.info("Starting connection");
+ con.start();
+
+ _logger.info("Producer sending message A");
+ producer.send(session1.createTextMessage("A"));
+
+ //check the dur sub's underlying queue now has msg count 1
+ AMQQueue subQueue = new AMQQueue("amq.topic", "clientid" + ":" + MY_SUBSCRIPTION);
+ assertEquals("Msg count should be 1", 1, ((AMQSession<?, ?>) session1).getQueueDepth(subQueue, true));
+
+ Message msg;
+ _logger.info("Receive message on consumer 1:expecting A");
+ msg = consumer1.receive(POSITIVE_RECEIVE_TIMEOUT);
+ assertNotNull("Message should have been received",msg);
+ assertEquals("A", ((TextMessage) msg).getText());
+ _logger.info("Receive message on consumer 1 :expecting null");
+ msg = consumer1.receive(NEGATIVE_RECEIVE_TIMEOUT);
+ assertEquals(null, msg);
+
+ _logger.info("Receive message on consumer 2:expecting A");
+ msg = consumer2.receive(POSITIVE_RECEIVE_TIMEOUT);
+ assertNotNull("Message should have been received",msg);
+ assertEquals("A", ((TextMessage) msg).getText());
+ msg = consumer2.receive(NEGATIVE_RECEIVE_TIMEOUT);
+ _logger.info("Receive message on consumer 1 :expecting null");
+ assertEquals(null, msg);
+
+ //check the dur sub's underlying queue now has msg count 0
+ assertEquals("Msg count should be 0", 0, ((AMQSession<?, ?>) session2).getQueueDepth(subQueue, true));
+
+ consumer2.close();
+ _logger.info("Unsubscribe session2/consumer2");
+ session2.unsubscribe(MY_SUBSCRIPTION);
+
+ ((AMQSession<?, ?>) session2).sync();
+
+ if(isJavaBroker())
+ {
+ //Verify that the queue was deleted by querying for its JMX MBean
+ _jmxc = JMXConnnectionFactory.getJMXConnection(5000, "127.0.0.1",
+ getManagementPort(getPort()), USER, PASSWORD);
+
+ _jmxConnected = true;
+ _mbsc = _jmxc.getMBeanServerConnection();
+
+ //must replace the occurrence of ':' in queue name with '-'
+ String queueObjectNameText = "clientid" + "-" + MY_SUBSCRIPTION;
+
+ ObjectName objName = new ObjectName("org.apache.qpid:type=VirtualHost.Queue,name="
+ + queueObjectNameText + ",*");
+
+ Set<ObjectName> objectInstances = _mbsc.queryNames(objName, null);
+
+ if(objectInstances.size() != 0)
+ {
+ fail("Queue MBean was found. Expected queue to have been deleted");
+ }
+ else
+ {
+ _logger.info("Underlying dueue for the durable subscription was confirmed deleted.");
+ }
+ }
+
+ //verify unsubscribing the durable subscriber did not affect the non-durable one
+ _logger.info("Producer sending message B");
+ producer.send(session1.createTextMessage("B"));
+
+ _logger.info("Receive message on consumer 1 :expecting B");
+ msg = consumer1.receive(POSITIVE_RECEIVE_TIMEOUT);
+ assertNotNull("Message should have been received",msg);
+ assertEquals("B", ((TextMessage) msg).getText());
+ _logger.info("Receive message on consumer 1 :expecting null");
+ msg = consumer1.receive(NEGATIVE_RECEIVE_TIMEOUT);
+ assertEquals(null, msg);
+
+ _logger.info("Close connection");
+ con.close();
+ }
+
+
+ /**
+ * Specifically uses a subscriber with a selector because QPID-4731 found that selectors
+ * can prevent queue removal.
+ */
+ public void testUnsubscribeWhenUsingSelectorMakesTopicUnreachable() throws Exception
+ {
+ setTestClientSystemProperty("qpid.default_mandatory_topic","true");
+
+ // set up subscription
+ AMQConnection connection = (AMQConnection) getConnection();
+ connection.start();
+
+ Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ Topic topic = new AMQTopic(connection, MY_TOPIC);
+ MessageProducer producer = session.createProducer(topic);
+
+ TopicSubscriber subscriber = session.createDurableSubscriber(topic, MY_SUBSCRIPTION, "1 = 1", false);
+ StoringExceptionListener exceptionListener = new StoringExceptionListener();
+ connection.setExceptionListener(exceptionListener);
+
+ // send message and verify it was consumed
+ producer.send(session.createTextMessage("message1"));
+ assertNotNull("Message should have been successfully received", subscriber.receive(POSITIVE_RECEIVE_TIMEOUT));
+ assertEquals(null, exceptionListener.getException());
+ session.unsubscribe(MY_SUBSCRIPTION);
+
+ // send another message and verify that the connection exception listener was fired.
+ StoringExceptionListener exceptionListener2 = new StoringExceptionListener();
+ connection.setExceptionListener(exceptionListener2);
+
+ producer.send(session.createTextMessage("message that should be unroutable"));
+ ((AMQSession<?, ?>) session).sync();
+
+ JMSException exception = exceptionListener2.awaitException();
+ assertNotNull("Expected exception as message should no longer be routable", exception);
+
+ Throwable linkedException = exception.getLinkedException();
+ assertNotNull("The linked exception of " + exception + " should be the 'no route' exception", linkedException);
+ assertEquals(AMQNoRouteException.class, linkedException.getClass());
+ }
+
+ private final class StoringExceptionListener implements ExceptionListener
+ {
+ private volatile JMSException _exception;
+ private CountDownLatch _latch = new CountDownLatch(1);
+
+ @Override
+ public void onException(JMSException exception)
+ {
+ _exception = exception;
+ _logger.info("Exception listener received: " + exception);
+ _latch.countDown();
+ }
+
+ public JMSException awaitException() throws InterruptedException
+ {
+ _latch.await(POSITIVE_RECEIVE_TIMEOUT, TimeUnit.MILLISECONDS);
+ return _exception;
+ }
+
+ public JMSException getException()
+ {
+ return _exception;
+ }
+ }
+
+ public void testDurabilityNOACK() throws Exception
+ {
+ durabilityImpl(AMQSession.NO_ACKNOWLEDGE, false);
+ }
+
+ public void testDurabilityAUTOACK() throws Exception
+ {
+ durabilityImpl(Session.AUTO_ACKNOWLEDGE, false);
+ }
+
+ public void testDurabilityAUTOACKwithRestartIfPersistent() throws Exception
+ {
+ if(!isBrokerStorePersistent())
+ {
+ _logger.warn("The broker store is not persistent, skipping this test");
+ return;
+ }
+
+ durabilityImpl(Session.AUTO_ACKNOWLEDGE, true);
+ }
+
+ public void testDurabilityNOACKSessionPerConnection() throws Exception
+ {
+ durabilityImplSessionPerConnection(AMQSession.NO_ACKNOWLEDGE);
+ }
+
+ public void testDurabilityAUTOACKSessionPerConnection() throws Exception
+ {
+ durabilityImplSessionPerConnection(Session.AUTO_ACKNOWLEDGE);
+ }
+
+ private void durabilityImpl(int ackMode, boolean restartBroker) throws Exception
+ {
+ AMQConnection con = (AMQConnection) getConnection();
+ AMQTopic topic = new AMQTopic(con, MY_TOPIC);
+ Session session1 = con.createSession(false, ackMode);
+ MessageConsumer consumer1 = session1.createConsumer(topic);
+
+ Session sessionProd = con.createSession(false, ackMode);
+ MessageProducer producer = sessionProd.createProducer(topic);
+
+ Session session2 = con.createSession(false, ackMode);
+ TopicSubscriber consumer2 = session2.createDurableSubscriber(topic, MY_SUBSCRIPTION);
+
+ con.start();
+
+ //send message A and check both consumers receive
+ producer.send(session1.createTextMessage("A"));
+
+ Message msg;
+ _logger.info("Receive message on consumer 1 :expecting A");
+ msg = consumer1.receive(POSITIVE_RECEIVE_TIMEOUT);
+ assertNotNull("Message should have been received",msg);
+ assertEquals("A", ((TextMessage) msg).getText());
+ msg = consumer1.receive(NEGATIVE_RECEIVE_TIMEOUT);
+ assertEquals(null, msg);
+
+ _logger.info("Receive message on consumer 2 :expecting A");
+ msg = consumer2.receive(POSITIVE_RECEIVE_TIMEOUT);
+ assertNotNull("Message should have been received",msg);
+ assertEquals("A", ((TextMessage) msg).getText());
+ msg = consumer2.receive(NEGATIVE_RECEIVE_TIMEOUT);
+ assertEquals(null, msg);
+
+ //send message B, receive with consumer 1, and disconnect consumer 2 to leave the message behind (if not NO_ACK)
+ producer.send(session1.createTextMessage("B"));
+
+ _logger.info("Receive message on consumer 1 :expecting B");
+ msg = consumer1.receive(POSITIVE_RECEIVE_TIMEOUT);
+ assertNotNull("Consumer 1 should get message 'B'.", msg);
+ assertEquals("Incorrect Message received on consumer1.", "B", ((TextMessage) msg).getText());
+ _logger.info("Receive message on consumer 1 :expecting null");
+ msg = consumer1.receive(500);
+ assertNull("There should be no more messages for consumption on consumer1.", msg);
+
+ consumer2.close();
+ session2.close();
+
+ //Send message C, then connect consumer 3 to durable subscription and get
+ //message B if not using NO_ACK, then receive C with consumer 1 and 3
+ producer.send(session1.createTextMessage("C"));
+
+ Session session3 = con.createSession(false, ackMode);
+ MessageConsumer consumer3 = session3.createDurableSubscriber(topic, MY_SUBSCRIPTION);
+
+ if(ackMode == AMQSession.NO_ACKNOWLEDGE)
+ {
+ //Do nothing if NO_ACK was used, as prefetch means the message was dropped
+ //when we didn't call receive() to get it before closing consumer 2
+ }
+ else
+ {
+ _logger.info("Receive message on consumer 3 :expecting B");
+ msg = consumer3.receive(POSITIVE_RECEIVE_TIMEOUT);
+ assertNotNull("Consumer 3 should get message 'B'.", msg);
+ assertEquals("Incorrect Message received on consumer3.", "B", ((TextMessage) msg).getText());
+ }
+
+ _logger.info("Receive message on consumer 1 :expecting C");
+ msg = consumer1.receive(POSITIVE_RECEIVE_TIMEOUT);
+ assertNotNull("Consumer 1 should get message 'C'.", msg);
+ assertEquals("Incorrect Message received on consumer1.", "C", ((TextMessage) msg).getText());
+ _logger.info("Receive message on consumer 1 :expecting null");
+ msg = consumer1.receive(500);
+ assertNull("There should be no more messages for consumption on consumer1.", msg);
+
+ _logger.info("Receive message on consumer 3 :expecting C");
+ msg = consumer3.receive(POSITIVE_RECEIVE_TIMEOUT);
+ assertNotNull("Consumer 3 should get message 'C'.", msg);
+ assertEquals("Incorrect Message received on consumer3.", "C", ((TextMessage) msg).getText());
+ _logger.info("Receive message on consumer 3 :expecting null");
+ msg = consumer3.receive(500);
+ assertNull("There should be no more messages for consumption on consumer3.", msg);
+
+ consumer1.close();
+ consumer3.close();
+
+ session3.unsubscribe(MY_SUBSCRIPTION);
+
+ con.close();
+
+ if(restartBroker)
+ {
+ try
+ {
+ restartBroker();
+ }
+ catch (Exception e)
+ {
+ fail("Error restarting the broker");
+ }
+ }
+ }
+
+ private void durabilityImplSessionPerConnection(int ackMode) throws Exception
+ {
+ Message msg;
+ // Create producer.
+ AMQConnection con0 = (AMQConnection) getConnection();
+ con0.start();
+ Session session0 = con0.createSession(false, ackMode);
+
+ AMQTopic topic = new AMQTopic(con0, MY_TOPIC);
+
+ Session sessionProd = con0.createSession(false, ackMode);
+ MessageProducer producer = sessionProd.createProducer(topic);
+
+ // Create consumer 1.
+ AMQConnection con1 = (AMQConnection) getConnection();
+ con1.start();
+ Session session1 = con1.createSession(false, ackMode);
+
+ MessageConsumer consumer1 = session1.createConsumer(topic);
+
+ // Create consumer 2.
+ AMQConnection con2 = (AMQConnection) getConnection();
+ con2.start();
+ Session session2 = con2.createSession(false, ackMode);
+
+ TopicSubscriber consumer2 = session2.createDurableSubscriber(topic, MY_SUBSCRIPTION);
+
+ // Send message and check that both consumers get it and only it.
+ producer.send(session0.createTextMessage("A"));
+
+ msg = consumer1.receive(POSITIVE_RECEIVE_TIMEOUT);
+ assertNotNull("Message should be available", msg);
+ assertEquals("Message Text doesn't match", "A", ((TextMessage) msg).getText());
+ msg = consumer1.receive(500);
+ assertNull("There should be no more messages for consumption on consumer1.", msg);
+
+ msg = consumer2.receive(POSITIVE_RECEIVE_TIMEOUT);
+ assertNotNull("Message should have been received",msg);
+ assertEquals("Consumer 2 should also received the first msg.", "A", ((TextMessage) msg).getText());
+ msg = consumer2.receive(NEGATIVE_RECEIVE_TIMEOUT);
+ assertNull("There should be no more messages for consumption on consumer2.", msg);
+
+ // Send message and receive on consumer 1.
+ producer.send(session0.createTextMessage("B"));
+
+ _logger.info("Receive message on consumer 1 :expecting B");
+ msg = consumer1.receive(POSITIVE_RECEIVE_TIMEOUT);
+ assertEquals("B", ((TextMessage) msg).getText());
+ _logger.info("Receive message on consumer 1 :expecting null");
+ msg = consumer1.receive(NEGATIVE_RECEIVE_TIMEOUT);
+ assertEquals(null, msg);
+
+ // Detach the durable subscriber.
+ consumer2.close();
+ session2.close();
+ con2.close();
+
+ // Send message C and receive on consumer 1
+ producer.send(session0.createTextMessage("C"));
+
+ _logger.info("Receive message on consumer 1 :expecting C");
+ msg = consumer1.receive(POSITIVE_RECEIVE_TIMEOUT);
+ assertEquals("C", ((TextMessage) msg).getText());
+ _logger.info("Receive message on consumer 1 :expecting null");
+ msg = consumer1.receive(NEGATIVE_RECEIVE_TIMEOUT);
+ assertEquals(null, msg);
+
+ // Re-attach a new consumer to the durable subscription, and check that it gets message B it left (if not NO_ACK)
+ // and also gets message C sent after it was disconnected.
+ AMQConnection con3 = (AMQConnection) getConnection();
+ con3.start();
+ Session session3 = con3.createSession(false, ackMode);
+
+ TopicSubscriber consumer3 = session3.createDurableSubscriber(topic, MY_SUBSCRIPTION);
+
+ if(ackMode == AMQSession.NO_ACKNOWLEDGE)
+ {
+ //Do nothing if NO_ACK was used, as prefetch means the message was dropped
+ //when we didn't call receive() to get it before closing consumer 2
+ }
+ else
+ {
+ _logger.info("Receive message on consumer 3 :expecting B");
+ msg = consumer3.receive(POSITIVE_RECEIVE_TIMEOUT);
+ assertNotNull(msg);
+ assertEquals("B", ((TextMessage) msg).getText());
+ }
+
+ _logger.info("Receive message on consumer 3 :expecting C");
+ msg = consumer3.receive(POSITIVE_RECEIVE_TIMEOUT);
+ assertNotNull("Consumer 3 should get message 'C'.", msg);
+ assertEquals("Incorrect Message recevied on consumer3.", "C", ((TextMessage) msg).getText());
+ _logger.info("Receive message on consumer 3 :expecting null");
+ msg = consumer3.receive(NEGATIVE_RECEIVE_TIMEOUT);
+ assertNull("There should be no more messages for consumption on consumer3.", msg);
+
+ consumer1.close();
+ consumer3.close();
+
+ session3.unsubscribe(MY_SUBSCRIPTION);
+
+ con0.close();
+ con1.close();
+ con3.close();
+ }
+
+ /**
+ * This tests the fix for QPID-1085
+ * Creates a durable subscriber with an invalid selector, checks that the
+ * exception is thrown correctly and that the subscription is not created.
+ * @throws Exception
+ */
+ public void testDurableWithInvalidSelector() throws Exception
+ {
+ Connection conn = getConnection();
+ conn.start();
+ Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ AMQTopic topic = new AMQTopic((AMQConnection) conn, "MyTestDurableWithInvalidSelectorTopic");
+ MessageProducer producer = session.createProducer(topic);
+ producer.send(session.createTextMessage("testDurableWithInvalidSelector1"));
+ try
+ {
+ TopicSubscriber deadSubscriber = session.createDurableSubscriber(topic, "testDurableWithInvalidSelectorSub",
+ "=TEST 'test", true);
+ assertNull("Subscriber should not have been created", deadSubscriber);
+ }
+ catch (JMSException e)
+ {
+ assertTrue("Wrong type of exception thrown", e instanceof InvalidSelectorException);
+ }
+ TopicSubscriber liveSubscriber = session.createDurableSubscriber(topic, "testDurableWithInvalidSelectorSub");
+ assertNotNull("Subscriber should have been created", liveSubscriber);
+
+ producer.send(session.createTextMessage("testDurableWithInvalidSelector2"));
+
+ Message msg = liveSubscriber.receive(POSITIVE_RECEIVE_TIMEOUT);
+ assertNotNull ("Message should have been received", msg);
+ assertEquals ("testDurableWithInvalidSelector2", ((TextMessage) msg).getText());
+ assertNull("Should not receive subsequent message", liveSubscriber.receive(200));
+ liveSubscriber.close();
+ session.unsubscribe("testDurableWithInvalidSelectorSub");
+ }
+
+ /**
+ * This tests the fix for QPID-1085
+ * Creates a durable subscriber with an invalid destination, checks that the
+ * exception is thrown correctly and that the subscription is not created.
+ * @throws Exception
+ */
+ public void testDurableWithInvalidDestination() throws Exception
+ {
+ Connection conn = getConnection();
+ conn.start();
+ Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ AMQTopic topic = new AMQTopic((AMQConnection) conn, "testDurableWithInvalidDestinationTopic");
+ try
+ {
+ TopicSubscriber deadSubscriber = session.createDurableSubscriber(null, "testDurableWithInvalidDestinationsub");
+ assertNull("Subscriber should not have been created", deadSubscriber);
+ }
+ catch (InvalidDestinationException e)
+ {
+ // This was expected
+ }
+ MessageProducer producer = session.createProducer(topic);
+ producer.send(session.createTextMessage("testDurableWithInvalidSelector1"));
+
+ TopicSubscriber liveSubscriber = session.createDurableSubscriber(topic, "testDurableWithInvalidDestinationsub");
+ assertNotNull("Subscriber should have been created", liveSubscriber);
+
+ producer.send(session.createTextMessage("testDurableWithInvalidSelector2"));
+ Message msg = liveSubscriber.receive(POSITIVE_RECEIVE_TIMEOUT);
+ assertNotNull ("Message should have been received", msg);
+ assertEquals ("testDurableWithInvalidSelector2", ((TextMessage) msg).getText());
+ assertNull("Should not receive subsequent message", liveSubscriber.receive(200));
+
+ session.unsubscribe("testDurableWithInvalidDestinationsub");
+ }
+
+ /**
+ * Creates a durable subscription with a selector, then changes that selector on resubscription
+ * <p>
+ * QPID-1202, QPID-2418
+ */
+ public void testResubscribeWithChangedSelector() throws Exception
+ {
+ Connection conn = getConnection();
+ conn.start();
+ Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ AMQTopic topic = new AMQTopic((AMQConnection) conn, "testResubscribeWithChangedSelector");
+ MessageProducer producer = session.createProducer(topic);
+
+ // Create durable subscriber that matches A
+ TopicSubscriber subA = session.createDurableSubscriber(topic,
+ "testResubscribeWithChangedSelector",
+ "Match = True", false);
+
+ // Send 1 matching message and 1 non-matching message
+ sendMatchingAndNonMatchingMessage(session, producer);
+
+ Message rMsg = subA.receive(NEGATIVE_RECEIVE_TIMEOUT);
+ assertNotNull(rMsg);
+ assertEquals("Content was wrong",
+ "testResubscribeWithChangedSelector1",
+ ((TextMessage) rMsg).getText());
+
+ rMsg = subA.receive(NEGATIVE_RECEIVE_TIMEOUT);
+ assertNull(rMsg);
+
+ // Disconnect subscriber
+ subA.close();
+
+ // Reconnect with new selector that matches B
+ TopicSubscriber subB = session.createDurableSubscriber(topic,
+ "testResubscribeWithChangedSelector","Match = False", false);
+
+ //verify no messages are now received.
+ rMsg = subB.receive(NEGATIVE_RECEIVE_TIMEOUT);
+ assertNull("Should not have received message as the selector was changed", rMsg);
+
+ // Check that new messages are received properly
+ sendMatchingAndNonMatchingMessage(session, producer);
+ rMsg = subB.receive(POSITIVE_RECEIVE_TIMEOUT);
+
+ assertNotNull("Message should have been received", rMsg);
+ assertEquals("Content was wrong",
+ "testResubscribeWithChangedSelector2",
+ ((TextMessage) rMsg).getText());
+
+
+ rMsg = subB.receive(NEGATIVE_RECEIVE_TIMEOUT);
+ assertNull("Message should not have been received",rMsg);
+ session.unsubscribe("testResubscribeWithChangedSelector");
+ }
+
+ public void testDurableSubscribeWithTemporaryTopic() throws Exception
+ {
+ Connection conn = getConnection();
+ conn.start();
+ Session ssn = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ Topic topic = ssn.createTemporaryTopic();
+ try
+ {
+ ssn.createDurableSubscriber(topic, "test");
+ fail("expected InvalidDestinationException");
+ }
+ catch (InvalidDestinationException ex)
+ {
+ // this is expected
+ }
+ try
+ {
+ ssn.createDurableSubscriber(topic, "test", null, false);
+ fail("expected InvalidDestinationException");
+ }
+ catch (InvalidDestinationException ex)
+ {
+ // this is expected
+ }
+ }
+
+ private void sendMatchingAndNonMatchingMessage(Session session, MessageProducer producer) throws JMSException
+ {
+ TextMessage msg = session.createTextMessage("testResubscribeWithChangedSelector1");
+ msg.setBooleanProperty("Match", true);
+ producer.send(msg);
+ msg = session.createTextMessage("testResubscribeWithChangedSelector2");
+ msg.setBooleanProperty("Match", false);
+ producer.send(msg);
+ }
+
+
+ /**
+ * create and register a durable subscriber with a message selector and then close it
+ * create a publisher and send 5 right messages and 5 wrong messages
+ * create another durable subscriber with the same selector and name
+ * check messages are still there
+ * <p>
+ * QPID-2418
+ */
+ public void testDurSubSameMessageSelector() throws Exception
+ {
+ Connection conn = getConnection();
+ conn.start();
+ Session session = conn.createSession(true, Session.SESSION_TRANSACTED);
+ AMQTopic topic = new AMQTopic((AMQConnection) conn, "sameMessageSelector");
+
+ //create and register a durable subscriber with a message selector and then close it
+ TopicSubscriber subOne = session.createDurableSubscriber(topic, "sameMessageSelector", "testprop = TRUE", false);
+ subOne.close();
+
+ MessageProducer producer = session.createProducer(topic);
+ for (int i = 0; i < 5; i++)
+ {
+ Message message = session.createMessage();
+ message.setBooleanProperty("testprop", true);
+ producer.send(message);
+ message = session.createMessage();
+ message.setBooleanProperty("testprop", false);
+ producer.send(message);
+ }
+ session.commit();
+ producer.close();
+
+ // should be 5 or 10 messages on queue now
+ // (5 for the java broker due to use of server side selectors, and 10 for the cpp broker due to client side selectors only)
+ AMQQueue queue = new AMQQueue("amq.topic", "clientid" + ":" + "sameMessageSelector");
+ assertEquals("Queue depth is wrong", isJavaBroker() ? 5 : 10, ((AMQSession<?, ?>) session).getQueueDepth(queue, true));
+
+ // now recreate the durable subscriber and check the received messages
+ TopicSubscriber subTwo = session.createDurableSubscriber(topic, "sameMessageSelector", "testprop = TRUE", false);
+
+ for (int i = 0; i < 5; i++)
+ {
+ Message message = subTwo.receive(1000);
+ if (message == null)
+ {
+ fail("sameMessageSelector test failed. no message was returned");
+ }
+ else
+ {
+ assertEquals("sameMessageSelector test failed. message selector not reset",
+ "true", message.getStringProperty("testprop"));
+ }
+ }
+
+ session.commit();
+
+ // Check queue has no messages
+ if (isJavaBroker())
+ {
+ assertEquals("Queue should be empty", 0, ((AMQSession<?, ?>) session).getQueueDepth(queue));
+ }
+ else
+ {
+ assertTrue("At most the queue should have only 1 message", ((AMQSession<?, ?>) session).getQueueDepth(queue) <= 1);
+ }
+
+ // Unsubscribe
+ session.unsubscribe("sameMessageSelector");
+
+ conn.close();
+ }
+
+ /**
+ * <ul>
+ * <li>create and register a durable subscriber with a message selector
+ * <li>create another durable subscriber with a different selector and same name
+ * <li>check first subscriber is now closed
+ * <li>create a publisher and send messages
+ * <li>check messages are received correctly
+ * </ul>
+ * <p>
+ * QPID-2418
+ */
+ public void testResubscribeWithChangedSelectorNoClose() throws Exception
+ {
+ Connection conn = getConnection();
+ conn.start();
+ Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ AMQTopic topic = new AMQTopic((AMQConnection) conn, "testResubscribeWithChangedSelectorNoClose");
+
+ // Create durable subscriber that matches A
+ TopicSubscriber subA = session.createDurableSubscriber(topic,
+ "testResubscribeWithChangedSelectorNoClose",
+ "Match = True", false);
+
+ // Reconnect with new selector that matches B
+ TopicSubscriber subB = session.createDurableSubscriber(topic,
+ "testResubscribeWithChangedSelectorNoClose",
+ "Match = false", false);
+
+ // First subscription has been closed
+ try
+ {
+ subA.receive(1000);
+ fail("First subscription was not closed");
+ }
+ catch (Exception e)
+ {
+ _logger.error("Receive error",e);
+ }
+
+ conn.stop();
+
+ // Send 1 matching message and 1 non-matching message
+ MessageProducer producer = session.createProducer(topic);
+ TextMessage msg = session.createTextMessage("testResubscribeWithChangedSelectorAndRestart1");
+ msg.setBooleanProperty("Match", true);
+ producer.send(msg);
+ msg = session.createTextMessage("testResubscribeWithChangedSelectorAndRestart2");
+ msg.setBooleanProperty("Match", false);
+ producer.send(msg);
+
+ // should be 1 or 2 messages on queue now
+ // (1 for the java broker due to use of server side selectors, and 2 for the cpp broker due to client side selectors only)
+ AMQQueue queue = new AMQQueue("amq.topic", "clientid" + ":" + "testResubscribeWithChangedSelectorNoClose");
+ assertEquals("Queue depth is wrong", isJavaBroker() ? 1 : 2, ((AMQSession<?, ?>) session).getQueueDepth(queue, true));
+
+ conn.start();
+
+ Message rMsg = subB.receive(POSITIVE_RECEIVE_TIMEOUT);
+ assertNotNull(rMsg);
+ assertEquals("Content was wrong",
+ "testResubscribeWithChangedSelectorAndRestart2",
+ ((TextMessage) rMsg).getText());
+
+ rMsg = subB.receive(1000);
+ assertNull(rMsg);
+
+ // Check queue has no messages
+ assertEquals("Queue should be empty", 0, ((AMQSession<?, ?>) session).getQueueDepth(queue, true));
+
+ conn.close();
+ }
+
+ /**
+ * <ul>
+ * <li>create and register a durable subscriber with no message selector
+ * <li>create another durable subscriber with a selector and same name
+ * <li>check first subscriber is now closed
+ * <li>create a publisher and send messages
+ * <li>check messages are received correctly
+ * </ul>
+ * <p>
+ * QPID-2418
+ */
+ public void testDurSubAddMessageSelectorNoClose() throws Exception
+ {
+ Connection conn = getConnection();
+ conn.start();
+ Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ AMQTopic topic = new AMQTopic((AMQConnection) conn, "subscriptionName");
+
+ // create and register a durable subscriber with no message selector
+ TopicSubscriber subOne = session.createDurableSubscriber(topic, "subscriptionName", null, false);
+
+ // now create a durable subscriber with a selector
+ TopicSubscriber subTwo = session.createDurableSubscriber(topic, "subscriptionName", "testprop = TRUE", false);
+
+ // First subscription has been closed
+ try
+ {
+ subOne.receive(1000);
+ fail("First subscription was not closed");
+ }
+ catch (Exception e)
+ {
+ _logger.error("Receive error",e);
+ }
+
+ conn.stop();
+
+ // Send 1 matching message and 1 non-matching message
+ MessageProducer producer = session.createProducer(topic);
+ TextMessage msg = session.createTextMessage("testResubscribeWithChangedSelectorAndRestart1");
+ msg.setBooleanProperty("testprop", true);
+ producer.send(msg);
+ msg = session.createTextMessage("testResubscribeWithChangedSelectorAndRestart2");
+ msg.setBooleanProperty("testprop", false);
+ producer.send(msg);
+
+ // should be 1 or 2 messages on queue now
+ // (1 for the java broker due to use of server side selectors, and 2 for the cpp broker due to client side selectors only)
+ AMQQueue queue = new AMQQueue("amq.topic", "clientid" + ":" + "subscriptionName");
+ assertEquals("Queue depth is wrong", isJavaBroker() ? 1 : 2, ((AMQSession<?, ?>) session).getQueueDepth(queue, true));
+
+ conn.start();
+
+ Message rMsg = subTwo.receive(POSITIVE_RECEIVE_TIMEOUT);
+ assertNotNull(rMsg);
+ assertEquals("Content was wrong",
+ "testResubscribeWithChangedSelectorAndRestart1",
+ ((TextMessage) rMsg).getText());
+
+ rMsg = subTwo.receive(1000);
+ assertNull(rMsg);
+
+ // Check queue has no messages
+ assertEquals("Queue should be empty", 0, ((AMQSession<?, ?>) session).getQueueDepth(queue, true));
+
+ conn.close();
+ }
+
+ /**
+ * <ul>
+ * <li>create and register a durable subscriber with no message selector
+ * <li>try to create another durable with the same name, should fail
+ * </ul>
+ * <p>
+ * QPID-2418
+ */
+ public void testDurSubNoSelectorResubscribeNoClose() throws Exception
+ {
+ Connection conn = getConnection();
+ conn.start();
+ Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ AMQTopic topic = new AMQTopic((AMQConnection) conn, "subscriptionName");
+
+ // create and register a durable subscriber with no message selector
+ session.createDurableSubscriber(topic, "subscriptionName", null, false);
+
+ // try to recreate the durable subscriber
+ try
+ {
+ session.createDurableSubscriber(topic, "subscriptionName", null, false);
+ fail("Subscription should not have been created");
+ }
+ catch (Exception e)
+ {
+ _logger.error("Error creating durable subscriber",e);
+ }
+ }
+
+ /**
+ * Tests that a subscriber created on a same <i>session</i> as producer with
+ * no local true does not receive messages.
+ */
+ public void testNoLocalOnSameSession() throws Exception
+ {
+ Connection connection = getConnection();
+ Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ Topic topic = session.createTopic(getTestQueueName());
+ MessageProducer producer = session.createProducer(topic);
+ TopicSubscriber subscriber = null;
+ try
+ {
+ subscriber = session.createDurableSubscriber(topic, getTestName(), null, true);
+ connection.start();
+
+ producer.send(createNextMessage(session, 1));
+
+ Message m = subscriber.receive(NEGATIVE_RECEIVE_TIMEOUT);
+ assertNull("Unexpected message received", m);
+ }
+ finally
+ {
+ session.unsubscribe(getTestName());
+ }
+ }
+
+
+ /**
+ * Tests that a subscriber created on a same <i>connection</i> but separate
+ * <i>sessionM</i> as producer with no local true does not receive messages.
+ */
+ public void testNoLocalOnSameConnection() throws Exception
+ {
+ Connection connection = getConnection();
+
+ Session consumerSession = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ Session producerSession = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ Topic topic = consumerSession.createTopic(getTestQueueName());
+ MessageProducer producer = producerSession.createProducer(topic);
+
+ TopicSubscriber subscriber = null;
+ try
+ {
+ subscriber = consumerSession.createDurableSubscriber(topic, getTestName(), null, true);
+ connection.start();
+
+ producer.send(createNextMessage(producerSession, 1));
+
+ Message m = subscriber.receive(NEGATIVE_RECEIVE_TIMEOUT);
+ assertNull("Unexpected message received", m);
+ }
+ finally
+ {
+ consumerSession.unsubscribe(getTestName());
+ }
+ }
+
+ /**
+ * Tests that if no-local is in use, that the messages are delivered when
+ * the client reconnects.
+ *
+ * Currently fails on the Java Broker due to QPID-3605.
+ */
+ public void testNoLocalMessagesNotDeliveredAfterReconnection() throws Exception
+ {
+ Connection connection = getConnection();
+
+ Session consumerSession = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ Session producerSession = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ Topic topic = consumerSession.createTopic(getTestQueueName());
+ MessageProducer producer = producerSession.createProducer(topic);
+
+ TopicSubscriber subscriber = null;
+ try
+ {
+ subscriber = consumerSession.createDurableSubscriber(topic, getTestName(), null, true);
+ connection.start();
+
+ producer.send(createNextMessage(producerSession, 1));
+
+ Message m = subscriber.receive(NEGATIVE_RECEIVE_TIMEOUT);
+ assertNull("Unexpected message received", m);
+
+ connection.close();
+
+ connection = getConnection();
+
+ consumerSession = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ subscriber = consumerSession.createDurableSubscriber(topic, getTestName(), null, true);
+ connection.start();
+ m = subscriber.receive(NEGATIVE_RECEIVE_TIMEOUT);
+ assertNull("Message should not be received on a new connection", m);
+ }
+ finally
+ {
+ consumerSession.unsubscribe(getTestName());
+ }
+ }
+
+ /**
+ * Tests that messages are delivered normally to a subscriber on a separate connection despite
+ * the use of durable subscriber with no-local on the first connection.
+ */
+ public void testNoLocalSubscriberAndSubscriberOnSeparateConnection() throws Exception
+ {
+ Connection noLocalConnection = getConnection();
+ Connection connection = getConnection();
+
+ String noLocalSubId1 = getTestName() + "subId1";
+ String subId = getTestName() + "subId2";
+
+ Session noLocalSession = noLocalConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ Topic noLocalTopic = noLocalSession.createTopic(getTestQueueName());
+
+ Session consumerSession = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ Topic topic = consumerSession.createTopic(getTestQueueName());
+
+ TopicSubscriber noLocalSubscriber = null;
+ TopicSubscriber subscriber = null;
+ try
+ {
+ MessageProducer producer = noLocalSession.createProducer(noLocalTopic);
+ noLocalSubscriber = noLocalSession.createDurableSubscriber(noLocalTopic, noLocalSubId1, null, true);
+ subscriber = consumerSession.createDurableSubscriber(topic, subId, null, true);
+ noLocalConnection.start();
+ connection.start();
+
+ producer.send(createNextMessage(noLocalSession, 1));
+
+ Message m1 = noLocalSubscriber.receive(NEGATIVE_RECEIVE_TIMEOUT);
+ assertNull("Subscriber on nolocal connection should not receive message", m1);
+
+ Message m2 = subscriber.receive(NEGATIVE_RECEIVE_TIMEOUT);
+ assertNotNull("Subscriber on non-nolocal connection should receive message", m2);
+ }
+ finally
+ {
+ noLocalSession.unsubscribe(noLocalSubId1);
+ consumerSession.unsubscribe(subId);
+ }
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/topic/TemporaryTopicTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/topic/TemporaryTopicTest.java
new file mode 100644
index 0000000000..a5b9ce8365
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/topic/TemporaryTopicTest.java
@@ -0,0 +1,182 @@
+/*
+ *
+ * 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.qpid.test.unit.topic;
+
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+import javax.jms.Connection;
+import javax.jms.JMSException;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+import javax.jms.TemporaryTopic;
+import javax.jms.TextMessage;
+
+
+/**
+ * Tests the behaviour of {@link TemporaryTopic}.
+ */
+public class TemporaryTopicTest extends QpidBrokerTestCase
+{
+ /**
+ * Tests the basic publish/subscribe behaviour of a temporary topic. Single
+ * message is sent to two subscribers.
+ */
+ public void testMessageDeliveryUsingTemporaryTopic() throws Exception
+ {
+ final Connection conn = getConnection();
+ final Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ final TemporaryTopic topic = session.createTemporaryTopic();
+ assertNotNull(topic);
+ final MessageProducer producer = session.createProducer(topic);
+ final MessageConsumer consumer1 = session.createConsumer(topic);
+ final MessageConsumer consumer2 = session.createConsumer(topic);
+ conn.start();
+ producer.send(session.createTextMessage("hello"));
+
+ final TextMessage tm1 = (TextMessage) consumer1.receive(2000);
+ final TextMessage tm2 = (TextMessage) consumer2.receive(2000);
+
+ assertNotNull("Message not received by subscriber1", tm1);
+ assertEquals("hello", tm1.getText());
+ assertNotNull("Message not received by subscriber2", tm2);
+ assertEquals("hello", tm2.getText());
+ }
+
+ /**
+ * Tests that the client is able to explicitly delete a temporary topic using
+ * {@link TemporaryTopic#delete()} and is prevented from deleting one that
+ * still has consumers.
+ *
+ * Note: Under < 0-10 {@link TemporaryTopic#delete()} only marks the queue as deleted
+ * on the client. 0-10 causes the topic to be deleted from the Broker.
+ */
+ public void testExplictTemporaryTopicDeletion() throws Exception
+ {
+ final Connection conn = getConnection();
+
+ final Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ final TemporaryTopic topic = session.createTemporaryTopic();
+ assertNotNull(topic);
+ final MessageConsumer consumer = session.createConsumer(topic);
+ conn.start();
+ try
+ {
+ topic.delete();
+ fail("Expected JMSException : should not be able to delete while there are active consumers");
+ }
+ catch (JMSException je)
+ {
+ //pass
+ assertEquals("Temporary Topic has consumers so cannot be deleted", je.getMessage());
+ }
+
+ consumer.close();
+
+ // Now deletion should succeed.
+ topic.delete();
+
+ try
+ {
+ session.createConsumer(topic);
+ fail("Exception not thrown");
+ }
+ catch (JMSException je)
+ {
+ //pass
+ assertEquals("Cannot consume from a deleted destination", je.getMessage());
+ }
+ }
+
+ /**
+ * Tests that a temporary topic cannot be used by another {@link Session}.
+ */
+ public void testUseFromAnotherSessionProhibited() throws Exception
+ {
+ final Connection conn = getConnection();
+ final Session session1 = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ final Session session2 = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ final TemporaryTopic topic = session1.createTemporaryTopic();
+
+ try
+ {
+ session2.createConsumer(topic);
+ fail("Expected a JMSException when subscribing to a temporary topic created on a different session");
+ }
+ catch (JMSException je)
+ {
+ // pass
+ assertEquals("Cannot consume from a temporary destination created on another session", je.getMessage());
+ }
+ }
+
+ /**
+ * Tests that the client is prohibited from creating a durable subscriber for a temporary
+ * queue.
+ */
+ public void testDurableSubscriptionProhibited() throws Exception
+ {
+ final Connection conn = getConnection();
+
+ final Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ final TemporaryTopic topic = session.createTemporaryTopic();
+ assertNotNull(topic);
+ try
+ {
+ session.createDurableSubscriber(topic, null);
+ fail("Expected JMSException : should not be able to create durable subscription from temp topic");
+ }
+ catch (JMSException je)
+ {
+ //pass
+ assertEquals("Cannot create a durable subscription with a temporary topic: " + topic.toString(), je.getMessage());
+ }
+ }
+
+ /**
+ * Tests that a temporary topic remains available for reuse even after its initial
+ * subscribers have disconnected.
+ */
+ public void testTemporaryTopicReused() throws Exception
+ {
+ final Connection conn = getConnection();
+ final Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ final TemporaryTopic topic = session.createTemporaryTopic();
+ assertNotNull(topic);
+
+ final MessageProducer producer = session.createProducer(topic);
+ final MessageConsumer consumer1 = session.createConsumer(topic);
+ conn.start();
+ producer.send(session.createTextMessage("message1"));
+ TextMessage tm = (TextMessage) consumer1.receive(2000);
+ assertNotNull("Message not received by first consumer", tm);
+ assertEquals("message1", tm.getText());
+ consumer1.close();
+
+ final MessageConsumer consumer2 = session.createConsumer(topic);
+ conn.start();
+ producer.send(session.createTextMessage("message2"));
+ tm = (TextMessage) consumer2.receive(2000);
+ assertNotNull("Message not received by second consumer", tm);
+ assertEquals("message2", tm.getText());
+ consumer2.close();
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/topic/TopicPublisherTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/topic/TopicPublisherTest.java
new file mode 100644
index 0000000000..5fbbc7f67f
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/topic/TopicPublisherTest.java
@@ -0,0 +1,76 @@
+/*
+ *
+ * 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.qpid.test.unit.topic;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.AMQTopic;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+import javax.jms.MessageConsumer;
+import javax.jms.TextMessage;
+import javax.jms.TopicPublisher;
+import javax.jms.TopicSession;
+
+/**
+ * @author Apache Software Foundation
+ */
+public class TopicPublisherTest extends QpidBrokerTestCase
+{
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ }
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ }
+
+ public void testUnidentifiedProducer() throws Exception
+ {
+
+ AMQConnection con = (AMQConnection) getConnection("guest", "guest");
+ AMQTopic topic = new AMQTopic(con,"MyTopic");
+ TopicSession session1 = con.createTopicSession(false, AMQSession.NO_ACKNOWLEDGE);
+ TopicPublisher publisher = session1.createPublisher(null);
+ MessageConsumer consumer1 = session1.createConsumer(topic);
+ con.start();
+ publisher.publish(topic, session1.createTextMessage("Hello"));
+ TextMessage m = (TextMessage) consumer1.receive(2000);
+ assertNotNull(m);
+ try
+ {
+ publisher.publish(session1.createTextMessage("Goodbye"));
+ fail("Did not throw UnsupportedOperationException");
+ }
+ catch (UnsupportedOperationException e)
+ {
+ // PASS
+ }
+ con.close();
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(TopicPublisherTest.class);
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/topic/TopicSessionTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/topic/TopicSessionTest.java
new file mode 100644
index 0000000000..c2ea3a5695
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/topic/TopicSessionTest.java
@@ -0,0 +1,388 @@
+/*
+ *
+ * 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.qpid.test.unit.topic;
+
+import javax.jms.JMSException;
+import javax.naming.NamingException;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.AMQTopic;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+import javax.jms.Connection;
+import javax.jms.InvalidDestinationException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import javax.jms.Topic;
+import javax.jms.TopicPublisher;
+import javax.jms.TopicSession;
+import javax.jms.TopicSubscriber;
+import org.apache.qpid.url.URLSyntaxException;
+
+
+/** @author Apache Software Foundation */
+public class TopicSessionTest extends QpidBrokerTestCase
+{
+ public void testTopicSubscriptionUnsubscription() throws Exception
+ {
+
+ AMQConnection con = (AMQConnection) getConnection("guest", "guest");
+ AMQTopic topic = new AMQTopic(con.getDefaultTopicExchangeName(), "MyTopic");
+ TopicSession session1 = con.createTopicSession(true, AMQSession.NO_ACKNOWLEDGE);
+ TopicSubscriber sub = session1.createDurableSubscriber(topic, "subscription0");
+ TopicPublisher publisher = session1.createPublisher(topic);
+
+ con.start();
+
+ TextMessage tm = session1.createTextMessage("Hello");
+ publisher.publish(tm);
+ session1.commit();
+
+ tm = (TextMessage) sub.receive(2000);
+ assertNotNull(tm);
+ session1.commit();
+ session1.unsubscribe("subscription0");
+
+ try
+ {
+ session1.unsubscribe("not a subscription");
+ fail("expected InvalidDestinationException when unsubscribing from unknown subscription");
+ }
+ catch (InvalidDestinationException e)
+ {
+ ; // PASS
+ }
+ catch (Exception e)
+ {
+ fail("expected InvalidDestinationException when unsubscribing from unknown subscription, got: " + e);
+ }
+
+ con.close();
+ }
+
+ public void testSubscriptionNameReuseForDifferentTopicSingleConnection() throws Exception
+ {
+ subscriptionNameReuseForDifferentTopic(false);
+ }
+
+ public void testSubscriptionNameReuseForDifferentTopicTwoConnections() throws Exception
+ {
+ subscriptionNameReuseForDifferentTopic(true);
+ }
+
+ private void subscriptionNameReuseForDifferentTopic(boolean shutdown) throws Exception
+ {
+ AMQConnection con = (AMQConnection) getConnection("guest", "guest");
+ AMQTopic topic = new AMQTopic(con, "MyTopic1" + String.valueOf(shutdown));
+ AMQTopic topic2 = new AMQTopic(con, "MyOtherTopic1" + String.valueOf(shutdown));
+
+ TopicSession session1 = con.createTopicSession(true, AMQSession.AUTO_ACKNOWLEDGE);
+ TopicSubscriber sub = session1.createDurableSubscriber(topic, "subscription0");
+ TopicPublisher publisher = session1.createPublisher(null);
+
+ con.start();
+
+ publisher.publish(topic, session1.createTextMessage("hello"));
+ session1.commit();
+ TextMessage m = (TextMessage) sub.receive(2000);
+ assertNotNull(m);
+ session1.commit();
+
+ if (shutdown)
+ {
+ session1.close();
+ con.close();
+ con = (AMQConnection) getConnection("guest", "guest");
+ con.start();
+ session1 = con.createTopicSession(true, AMQSession.NO_ACKNOWLEDGE);
+ publisher = session1.createPublisher(null);
+ }
+ sub.close();
+ TopicSubscriber sub2 = session1.createDurableSubscriber(topic2, "subscription0");
+ publisher.publish(topic, session1.createTextMessage("hello"));
+ session1.commit();
+ if (!shutdown)
+ {
+ m = (TextMessage) sub2.receive(2000);
+ assertNull(m);
+ session1.commit();
+ }
+ publisher.publish(topic2, session1.createTextMessage("goodbye"));
+ session1.commit();
+ m = (TextMessage) sub2.receive(2000);
+ assertNotNull(m);
+ assertEquals("goodbye", m.getText());
+ session1.unsubscribe("subscription0");
+ con.close();
+ }
+
+ public void testUnsubscriptionAfterConnectionClose() throws Exception
+ {
+ AMQConnection con1 = (AMQConnection) getClientConnection("guest", "guest", "clientid");
+ AMQTopic topic = new AMQTopic(con1, "MyTopic3");
+
+ TopicSession session1 = con1.createTopicSession(true, AMQSession.AUTO_ACKNOWLEDGE);
+ TopicPublisher publisher = session1.createPublisher(topic);
+
+ AMQConnection con2 = (AMQConnection) getClientConnection("guest", "guest", "clientid");
+ TopicSession session2 = con2.createTopicSession(true, AMQSession.AUTO_ACKNOWLEDGE);
+ TopicSubscriber sub = session2.createDurableSubscriber(topic, "subscription0");
+
+ con2.start();
+
+ publisher.publish(session1.createTextMessage("Hello"));
+ session1.commit();
+ TextMessage tm = (TextMessage) sub.receive(2000);
+ session2.commit();
+ assertNotNull(tm);
+ con2.close();
+ publisher.publish(session1.createTextMessage("Hello2"));
+ session1.commit();
+ con2 = (AMQConnection) getClientConnection("guest", "guest", "clientid");
+ session2 = con2.createTopicSession(true, AMQSession.NO_ACKNOWLEDGE);
+ sub = session2.createDurableSubscriber(topic, "subscription0");
+ con2.start();
+ tm = (TextMessage) sub.receive(2000);
+ session2.commit();
+ assertNotNull(tm);
+ assertEquals("Hello2", tm.getText());
+ session2.unsubscribe("subscription0");
+ con1.close();
+ con2.close();
+ }
+
+ public void testTextMessageCreation() throws Exception
+ {
+
+ AMQConnection con = (AMQConnection) getConnection("guest", "guest");
+ AMQTopic topic = new AMQTopic(con, "MyTopic4");
+ TopicSession session1 = con.createTopicSession(true, AMQSession.AUTO_ACKNOWLEDGE);
+ TopicPublisher publisher = session1.createPublisher(topic);
+ MessageConsumer consumer1 = session1.createConsumer(topic);
+ con.start();
+ TextMessage tm = session1.createTextMessage("Hello");
+ publisher.publish(tm);
+ session1.commit();
+ tm = (TextMessage) consumer1.receive(10000L);
+ assertNotNull(tm);
+ String msgText = tm.getText();
+ assertEquals("Hello", msgText);
+ tm = session1.createTextMessage();
+ msgText = tm.getText();
+ assertNull(msgText);
+ publisher.publish(tm);
+ session1.commit();
+ tm = (TextMessage) consumer1.receive(10000L);
+ assertNotNull(tm);
+ session1.commit();
+ msgText = tm.getText();
+ assertNull(msgText);
+ tm.clearBody();
+ tm.setText("Now we are not null");
+ publisher.publish(tm);
+ session1.commit();
+ tm = (TextMessage) consumer1.receive(2000);
+ assertNotNull(tm);
+ session1.commit();
+ msgText = tm.getText();
+ assertEquals("Now we are not null", msgText);
+
+ tm = session1.createTextMessage("");
+ msgText = tm.getText();
+ assertEquals("Empty string not returned", "", msgText);
+ publisher.publish(tm);
+ session1.commit();
+ tm = (TextMessage) consumer1.receive(2000);
+ session1.commit();
+ assertNotNull(tm);
+ assertEquals("Empty string not returned", "", msgText);
+ con.close();
+ }
+
+ public void testNoLocal() throws Exception
+ {
+
+ AMQConnection con = (AMQConnection) getConnection("guest", "guest");
+
+ AMQTopic topic = new AMQTopic(con, "testNoLocal");
+
+ noLocalTest(con, topic);
+
+
+ con.close();
+ }
+
+
+ public void testNoLocalDirectExchange() throws Exception
+ {
+
+ AMQConnection con = (AMQConnection) getConnection("guest", "guest");
+
+ AMQTopic topic = new AMQTopic("direct://amq.direct/testNoLocal/testNoLocal?routingkey='testNoLocal',exclusive='true',autodelete='true'");
+
+ noLocalTest(con, topic);
+
+
+ con.close();
+ }
+
+
+
+ public void testNoLocalFanoutExchange() throws Exception
+ {
+
+ AMQConnection con = (AMQConnection) getConnection("guest", "guest");
+
+ AMQTopic topic = new AMQTopic("fanout://amq.fanout/testNoLocal/testNoLocal?routingkey='testNoLocal',exclusive='true',autodelete='true'");
+
+ noLocalTest(con, topic);
+
+ con.close();
+ }
+
+
+ private void noLocalTest(AMQConnection con, AMQTopic topic)
+ throws JMSException, URLSyntaxException, AMQException, NamingException
+ {
+ TopicSession session1 = con.createTopicSession(true, AMQSession.AUTO_ACKNOWLEDGE);
+ TopicSubscriber noLocal = session1.createSubscriber(topic, "", true);
+
+ TopicSubscriber select = session1.createSubscriber(topic, "Selector = 'select'", false);
+ TopicSubscriber normal = session1.createSubscriber(topic);
+
+
+ TopicPublisher publisher = session1.createPublisher(topic);
+
+ con.start();
+ TextMessage m;
+ TextMessage message;
+
+ //send message to all consumers
+ publisher.publish(session1.createTextMessage("hello-new2"));
+ session1.commit();
+ //test normal subscriber gets message
+ m = (TextMessage) normal.receive(1000);
+ assertNotNull(m);
+ session1.commit();
+
+ //test selector subscriber doesn't message
+ m = (TextMessage) select.receive(1000);
+ assertNull(m);
+ session1.commit();
+
+ //test nolocal subscriber doesn't message
+ m = (TextMessage) noLocal.receive(1000);
+ if (m != null)
+ {
+ _logger.info("Message:" + m.getText());
+ }
+ assertNull(m);
+
+ //send message to all consumers
+ message = session1.createTextMessage("hello2");
+ message.setStringProperty("Selector", "select");
+
+ publisher.publish(message);
+ session1.commit();
+
+ //test normal subscriber gets message
+ m = (TextMessage) normal.receive(1000);
+ assertNotNull(m);
+ session1.commit();
+
+ //test selector subscriber does get message
+ m = (TextMessage) select.receive(1000);
+ assertNotNull(m);
+ session1.commit();
+
+ //test nolocal subscriber doesn't message
+ m = (TextMessage) noLocal.receive(100);
+ assertNull(m);
+
+ AMQConnection con2 = (AMQConnection) getClientConnection("guest", "guest", "foo");
+ TopicSession session2 = con2.createTopicSession(true, AMQSession.AUTO_ACKNOWLEDGE);
+ TopicPublisher publisher2 = session2.createPublisher(topic);
+
+
+ message = session2.createTextMessage("hello2");
+ message.setStringProperty("Selector", "select");
+
+ publisher2.publish(message);
+ session2.commit();
+
+ //test normal subscriber gets message
+ m = (TextMessage) normal.receive(1000);
+ assertNotNull(m);
+ session1.commit();
+
+ //test selector subscriber does get message
+ m = (TextMessage) select.receive(1000);
+ assertNotNull(m);
+ session1.commit();
+
+ //test nolocal subscriber does message
+ m = (TextMessage) noLocal.receive(1000);
+ assertNotNull(m);
+ con2.close();
+ }
+
+ /**
+ * This tests was added to demonstrate QPID-3542. The Java Client when used with the CPP Broker was failing to
+ * ack messages received that did not match the selector. This meant the messages remained indefinitely on the Broker.
+ */
+ public void testNonMatchingMessagesHandledCorrectly() throws Exception
+ {
+ final String topicName = getName();
+ final String clientId = "clientId" + topicName;
+ final Connection con1 = getConnection();
+ final Session session1 = con1.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ final Topic topic1 = session1.createTopic(topicName);
+ final AMQQueue internalNameOnBroker = new AMQQueue("amq.topic", "clientid" + ":" + clientId);
+
+ // Setup subscriber with selector
+ final TopicSubscriber subscriberWithSelector = session1.createDurableSubscriber(topic1, clientId, "Selector = 'select'", false);
+ final MessageProducer publisher = session1.createProducer(topic1);
+
+ con1.start();
+
+ // Send non-matching message
+ final Message sentMessage = session1.createTextMessage("hello");
+ sentMessage.setStringProperty("Selector", "nonMatch");
+ publisher.send(sentMessage);
+
+ // Try to consume non-message, expect this to fail.
+ final Message message1 = subscriberWithSelector.receive(1000);
+ assertNull("should not have received message", message1);
+ subscriberWithSelector.close();
+
+ session1.close();
+
+ // Now verify queue depth on broker.
+ final Session session2 = con1.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ final long depth = ((AMQSession) session2).getQueueDepth(internalNameOnBroker);
+ assertEquals("Expected queue depth of zero", 0, depth);
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/transacted/CommitRollbackTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/transacted/CommitRollbackTest.java
new file mode 100644
index 0000000000..4715831de6
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/transacted/CommitRollbackTest.java
@@ -0,0 +1,544 @@
+/*
+ * 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.qpid.test.unit.transacted;
+
+import org.apache.qpid.client.RejectBehaviour;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.configuration.ClientProperties;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageListener;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * This class tests a number of commits and roll back scenarios
+ *
+ * Assumptions; - Assumes empty Queue
+ *
+ * @see org.apache.qpid.test.client.RollbackOrderTest
+ */
+public class CommitRollbackTest extends QpidBrokerTestCase
+{
+ private static final Logger _logger = LoggerFactory.getLogger(CommitRollbackTest.class);
+ private static final int POSITIVE_TIMEOUT = 2000;
+ private static final int NEGATIVE_TIMEOUT = 250;
+
+ protected AMQConnection _conn;
+ private Session _session;
+ private MessageProducer _publisher;
+ private Session _pubSession;
+ private MessageConsumer _consumer;
+ private Queue _jmsQueue;
+
+ private void newConnection() throws Exception
+ {
+ _logger.debug("calling newConnection()");
+ _conn = (AMQConnection) getConnection();
+
+ _session = _conn.createSession(true, Session.SESSION_TRANSACTED);
+
+ final String queueName = getTestQueueName();
+ _jmsQueue = _session.createQueue(queueName);
+ _consumer = _session.createConsumer(_jmsQueue);
+
+ _pubSession = _conn.createSession(true, Session.SESSION_TRANSACTED);
+
+ _publisher = _pubSession.createProducer(_pubSession.createQueue(queueName));
+
+ _conn.start();
+ }
+
+ /**
+ * PUT a text message, disconnect before commit, confirm it is gone.
+ *
+ * @throws Exception On error
+ */
+ public void testPutThenDisconnect() throws Exception
+ {
+ newConnection();
+
+ assertTrue("session is not transacted", _session.getTransacted());
+ assertTrue("session is not transacted", _pubSession.getTransacted());
+
+ _logger.info("sending test message");
+ String MESSAGE_TEXT = "testPutThenDisconnect";
+ _publisher.send(_pubSession.createTextMessage(MESSAGE_TEXT));
+
+ _logger.info("reconnecting without commit");
+ _conn.close();
+
+ newConnection();
+
+ _logger.info("receiving result");
+ Message result = _consumer.receive(NEGATIVE_TIMEOUT);
+
+ // commit to ensure message is removed from queue
+ _session.commit();
+
+ assertNull("test message was put and disconnected before commit, but is still present", result);
+ }
+
+
+ /**
+ * PUT a text message, rollback, confirm message is gone. The consumer is on the same connection but different
+ * session as producer
+ *
+ * @throws Exception On error
+ */
+ public void testPutThenRollback() throws Exception
+ {
+ newConnection();
+
+ assertTrue("session is not transacted", _session.getTransacted());
+ assertTrue("session is not transacted", _pubSession.getTransacted());
+
+ _logger.info("sending test message");
+ String MESSAGE_TEXT = "testPutThenRollback";
+ _publisher.send(_pubSession.createTextMessage(MESSAGE_TEXT));
+
+ _logger.info("rolling back");
+ _pubSession.rollback();
+
+ _logger.info("receiving result");
+ Message result = _consumer.receive(NEGATIVE_TIMEOUT);
+
+ assertNull("test message was put and rolled back, but is still present", result);
+ }
+
+ /**
+ * GET a text message, disconnect before commit, confirm it is still there. The consumer is on a new connection
+ *
+ * @throws Exception On error
+ */
+ public void testGetThenDisconnect() throws Exception
+ {
+ newConnection();
+
+ assertTrue("session is not transacted", _session.getTransacted());
+ assertTrue("session is not transacted", _pubSession.getTransacted());
+
+ _logger.info("sending test message");
+ String MESSAGE_TEXT = "testGetThenDisconnect";
+ _publisher.send(_pubSession.createTextMessage(MESSAGE_TEXT));
+
+ _pubSession.commit();
+
+ _logger.info("getting test message");
+
+ Message msg = _consumer.receive(POSITIVE_TIMEOUT);
+ assertNotNull("retrieved message is null", msg);
+
+ _logger.info("closing connection");
+ _conn.close();
+
+ newConnection();
+
+ _logger.info("receiving result");
+ Message result = _consumer.receive(NEGATIVE_TIMEOUT);
+
+ _session.commit();
+
+ assertNotNull("test message was consumed and disconnected before commit, but is gone", result);
+ assertEquals("test message was correct message", MESSAGE_TEXT, ((TextMessage) result).getText());
+ }
+
+ /**
+ * GET a text message, close consumer, disconnect before commit, confirm it is still there. The consumer is on the
+ * same connection but different session as producer
+ *
+ * @throws Exception On error
+ */
+ public void testGetThenCloseDisconnect() throws Exception
+ {
+ newConnection();
+
+ assertTrue("session is not transacted", _session.getTransacted());
+ assertTrue("session is not transacted", _pubSession.getTransacted());
+
+ _logger.info("sending test message");
+ String MESSAGE_TEXT = "testGetThenCloseDisconnect";
+ _publisher.send(_pubSession.createTextMessage(MESSAGE_TEXT));
+
+ _pubSession.commit();
+
+ _logger.info("getting test message");
+
+ Message msg = _consumer.receive(POSITIVE_TIMEOUT);
+ assertNotNull("retrieved message is null", msg);
+ assertEquals("test message was correct message", MESSAGE_TEXT, ((TextMessage) msg).getText());
+
+ _logger.info("reconnecting without commit");
+ _consumer.close();
+ _conn.close();
+
+ newConnection();
+
+ _logger.info("receiving result");
+ Message result = _consumer.receive(POSITIVE_TIMEOUT);
+
+ _session.commit();
+
+ assertNotNull("test message was consumed and disconnected before commit, but is gone", result);
+ assertEquals("test message was correct message", MESSAGE_TEXT, ((TextMessage) result).getText());
+ }
+
+ /**
+ * GET a text message, rollback, confirm it is still there. The consumer is on the same connection but differnt
+ * session to the producer
+ *
+ * @throws Exception On error
+ */
+ public void testGetThenRollback() throws Exception
+ {
+ newConnection();
+
+ assertTrue("session is not transacted", _session.getTransacted());
+ assertTrue("session is not transacted", _pubSession.getTransacted());
+
+ _logger.info("sending test message");
+ String MESSAGE_TEXT = "testGetThenRollback";
+ _publisher.send(_pubSession.createTextMessage(MESSAGE_TEXT));
+
+ _pubSession.commit();
+
+ _logger.info("getting test message");
+
+ Message msg = _consumer.receive(POSITIVE_TIMEOUT);
+
+ assertNotNull("retrieved message is null", msg);
+ assertEquals("test message was correct message", MESSAGE_TEXT, ((TextMessage) msg).getText());
+
+ _logger.info("rolling back");
+
+ _session.rollback();
+
+ _logger.info("receiving result");
+
+ Message result = _consumer.receive(POSITIVE_TIMEOUT);
+
+ _session.commit();
+ assertNotNull("test message was consumed and rolled back, but is gone", result);
+ assertEquals("test message was correct message", MESSAGE_TEXT, ((TextMessage) result).getText());
+ assertTrue("Message is not marked as redelivered", result.getJMSRedelivered());
+ }
+
+ /**
+ * GET a text message, close message producer, rollback, confirm it is still there. The consumer is on the same
+ * connection but different session as producer
+ *
+ * @throws Exception On error
+ */
+ public void testGetThenCloseRollback() throws Exception
+ {
+ newConnection();
+
+ assertTrue("session is not transacted", _session.getTransacted());
+ assertTrue("session is not transacted", _pubSession.getTransacted());
+
+ _logger.info("sending test message");
+ String MESSAGE_TEXT = "testGetThenCloseRollback";
+ _publisher.send(_pubSession.createTextMessage(MESSAGE_TEXT));
+
+ _pubSession.commit();
+
+ _logger.info("getting test message");
+
+ Message msg = _consumer.receive(POSITIVE_TIMEOUT);
+
+ assertNotNull("retrieved message is null", msg);
+ assertEquals("test message was correct message", MESSAGE_TEXT, ((TextMessage) msg).getText());
+
+ _logger.info("Closing consumer");
+ _consumer.close();
+
+ _logger.info("rolling back");
+ _session.rollback();
+
+ _logger.info("receiving result");
+
+ _consumer = _session.createConsumer(_jmsQueue);
+
+ Message result = _consumer.receive(POSITIVE_TIMEOUT);
+
+ _session.commit();
+ assertNotNull("test message was consumed and rolled back, but is gone", result);
+ assertEquals("test message was correct message", MESSAGE_TEXT, ((TextMessage) result).getText());
+ assertTrue("Message is not marked as redelivered", result.getJMSRedelivered());
+ }
+
+ /**
+ * This test sends two messages receives one of them but doesn't ack it.
+ * The consumer is then closed
+ * the first message should be returned as redelivered.
+ * the second message should be delivered normally.
+ * @throws Exception
+ */
+ public void testSend2ThenCloseAfter1andTryAgain() throws Exception
+ {
+ newConnection();
+
+ assertTrue("session is not transacted", _session.getTransacted());
+ assertTrue("session is not transacted", _pubSession.getTransacted());
+
+ _logger.info("sending two test messages");
+ _publisher.send(_pubSession.createTextMessage("1"));
+ _publisher.send(_pubSession.createTextMessage("2"));
+ _pubSession.commit();
+
+ _logger.info("getting test message");
+ Message result = _consumer.receive(POSITIVE_TIMEOUT);
+
+ assertNotNull("Message received should not be null", result);
+ assertEquals("1", ((TextMessage) result).getText());
+ assertTrue("Message is marked as redelivered" + result, !result.getJMSRedelivered());
+
+ _logger.info("Closing Consumer");
+
+ _consumer.close();
+
+ _logger.info("Creating New consumer");
+ _consumer = _session.createConsumer(_jmsQueue);
+
+ _logger.info("receiving result");
+
+
+ // Message 2 may be marked as redelivered if it was prefetched.
+ result = _consumer.receive(POSITIVE_TIMEOUT);
+ assertNotNull("Second message was not consumed, but is gone", result);
+
+ // The first message back will be 2, message 1 has been received but not committed
+ // Closing the consumer does not commit the session.
+
+ // if this is message 1 then it should be marked as redelivered
+ if("1".equals(((TextMessage) result).getText()))
+ {
+ fail("First message was received again");
+ }
+
+ result = _consumer.receive(NEGATIVE_TIMEOUT);
+ assertNull("test message should be null:" + result, result);
+
+ _session.commit();
+ }
+
+ public void testPutThenRollbackThenGet() throws Exception
+ {
+ newConnection();
+
+ assertTrue("session is not transacted", _session.getTransacted());
+ assertTrue("session is not transacted", _pubSession.getTransacted());
+
+ _logger.info("sending test message");
+ String MESSAGE_TEXT = "testPutThenRollbackThenGet";
+
+ _publisher.send(_pubSession.createTextMessage(MESSAGE_TEXT));
+ _pubSession.commit();
+
+ assertNotNull(_consumer.receive(POSITIVE_TIMEOUT));
+
+ _publisher.send(_pubSession.createTextMessage(MESSAGE_TEXT));
+
+ _logger.info("rolling back");
+ _pubSession.rollback();
+
+ _logger.info("receiving result");
+ Message result = _consumer.receive(NEGATIVE_TIMEOUT);
+ assertNull("test message was put and rolled back, but is still present", result);
+
+ _publisher.send(_pubSession.createTextMessage(MESSAGE_TEXT));
+
+ _pubSession.commit();
+
+ assertNotNull(_consumer.receive(POSITIVE_TIMEOUT));
+
+ _session.commit();
+ }
+
+ /**
+ * Qpid-1163
+ * Check that when commit is called inside onMessage then
+ * the last message is nor redelivered.
+ *
+ * @throws Exception
+ */
+ public void testCommitWithinOnMessage() throws Exception
+ {
+ newConnection();
+
+ Queue queue = (Queue) getInitialContext().lookup("queue");
+ // create a consumer
+ MessageConsumer cons = _session.createConsumer(queue);
+ MessageProducer prod = _session.createProducer(queue);
+ Message message = _session.createTextMessage("Message");
+ message.setJMSCorrelationID("m1");
+ prod.send(message);
+ _session.commit();
+ _logger.info("Sent message to queue");
+ CountDownLatch cd = new CountDownLatch(1);
+ cons.setMessageListener(new CommitWithinOnMessageListener(cd));
+ _conn.start();
+ cd.await(30, TimeUnit.SECONDS);
+ if( cd.getCount() > 0 )
+ {
+ fail("Did not received message");
+ }
+ // Check that the message has been dequeued
+ _session.close();
+ _conn.close();
+ _conn = (AMQConnection) getConnection();
+ _conn.start();
+ Session session = _conn.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ cons = session.createConsumer(queue);
+ message = cons.receiveNoWait();
+ if(message != null)
+ {
+ if(message.getJMSCorrelationID().equals("m1"))
+ {
+ fail("received message twice");
+ }
+ else
+ {
+ fail("queue should have been empty, received message: " + message);
+ }
+ }
+ }
+
+ /**
+ * This test ensures that after exhausting credit (prefetch), a {@link Session#rollback()} successfully
+ * restores credit and allows the same messages to be re-received.
+ */
+ public void testRollbackSessionAfterCreditExhausted() throws Exception
+ {
+ final int maxPrefetch= 5;
+
+ // We send more messages than prefetch size. This ensure that if the 0-10 client were to
+ // complete the message commands before the rollback command is sent, the broker would
+ // send additional messages utilising the release credit. This problem would manifest itself
+ // as an incorrect message (or no message at all) being received at the end of the test.
+
+ final int numMessages = maxPrefetch * 2;
+
+ setTestClientSystemProperty(ClientProperties.MAX_PREFETCH_PROP_NAME, String.valueOf(maxPrefetch));
+
+ newConnection();
+
+ assertEquals("Prefetch not reset", maxPrefetch, ((AMQSession<?, ?>)_session).getDefaultPrefetch());
+
+ assertTrue("session is not transacted", _session.getTransacted());
+ assertTrue("session is not transacted", _pubSession.getTransacted());
+
+ sendMessage(_pubSession, _publisher.getDestination(), numMessages);
+ _pubSession.commit();
+
+ for (int i=0 ;i< maxPrefetch; i++)
+ {
+ final Message message = _consumer.receive(POSITIVE_TIMEOUT);
+ assertNotNull("Received:" + i, message);
+ assertEquals("Unexpected message received", i, message.getIntProperty(INDEX));
+ }
+
+ _logger.info("Rolling back");
+ _session.rollback();
+
+ _logger.info("Receiving messages");
+
+ Message result = _consumer.receive(POSITIVE_TIMEOUT);;
+ assertNotNull("Message expected", result);
+ // Expect the first message
+ assertEquals("Unexpected message received", 0, result.getIntProperty(INDEX));
+ }
+
+ private class CommitWithinOnMessageListener implements MessageListener
+ {
+ private CountDownLatch _cd;
+ private CommitWithinOnMessageListener(CountDownLatch cd)
+ {
+ _cd = cd;
+ }
+ public void onMessage(Message message)
+ {
+ try
+ {
+ _logger.info("received message " + message);
+ assertEquals("Wrong message received", message.getJMSCorrelationID(), "m1");
+ _logger.info("commit session");
+ _session.commit();
+ _cd.countDown();
+ }
+ catch (JMSException e)
+ {
+ _logger.error("OnMessage error",e);
+ }
+ }
+ }
+
+
+ public void testResendUnseenMessagesAfterRollback() throws Exception
+ {
+ resendAfterRollback();
+ }
+
+ public void testResendUnseenMessagesAfterRollbackWithServerReject() throws Exception
+ {
+ setTestSystemProperty(ClientProperties.REJECT_BEHAVIOUR_PROP_NAME, RejectBehaviour.SERVER.toString());
+ resendAfterRollback();
+ }
+
+ private void resendAfterRollback() throws Exception
+ {
+ newConnection();
+
+ assertTrue("session is not transacted", _session.getTransacted());
+ assertTrue("session is not transacted", _pubSession.getTransacted());
+
+ _logger.info("sending test message");
+ String MESSAGE_TEXT = "message text";
+
+ _publisher.send(_pubSession.createTextMessage(MESSAGE_TEXT));
+ _publisher.send(_pubSession.createTextMessage(MESSAGE_TEXT));
+
+ _pubSession.commit();
+
+ assertNotNull("two messages were sent, but none has been received", _consumer.receive(POSITIVE_TIMEOUT));
+
+ _session.rollback();
+
+ _logger.info("receiving result");
+
+ assertNotNull("two messages were sent, but none has been received", _consumer.receive(POSITIVE_TIMEOUT));
+ assertNotNull("two messages were sent, but only one has been received", _consumer.receive(POSITIVE_TIMEOUT));
+ assertNull("Only two messages were sent, but more have been received", _consumer.receive(NEGATIVE_TIMEOUT));
+
+ _session.commit();
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/transacted/TransactedTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/transacted/TransactedTest.java
new file mode 100644
index 0000000000..78c76602c5
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/transacted/TransactedTest.java
@@ -0,0 +1,389 @@
+/*
+ *
+ * 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.qpid.test.unit.transacted;
+
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.jms.Session;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+import javax.jms.Connection;
+import javax.jms.IllegalStateException;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.TextMessage;
+
+public class TransactedTest extends QpidBrokerTestCase
+{
+ private AMQQueue queue1;
+
+ private AMQConnection con;
+ private Session session;
+ private MessageConsumer consumer1;
+ private MessageProducer producer2;
+
+ private AMQConnection prepCon;
+ private Session prepSession;
+ private MessageProducer prepProducer1;
+
+ private AMQConnection testCon;
+ private Session testSession;
+ private MessageConsumer testConsumer1;
+ private MessageConsumer testConsumer2;
+ private static final Logger _logger = LoggerFactory.getLogger(TransactedTest.class);
+
+ protected void setUp() throws Exception
+ {
+ try
+ {
+ super.setUp();
+ _logger.info("Create Connection");
+ con = (AMQConnection) getConnection("guest", "guest");
+ _logger.info("Create Session");
+ session = con.createSession(true, Session.SESSION_TRANSACTED);
+ _logger.info("Create Q1");
+ queue1 = new AMQQueue(session.getDefaultQueueExchangeName(), new AMQShortString("Q1"),
+ new AMQShortString("Q1"), false, true);
+ _logger.info("Create Q2");
+ AMQQueue queue2 = new AMQQueue(session.getDefaultQueueExchangeName(), new AMQShortString("Q2"), false);
+
+ _logger.info("Create Consumer of Q1");
+ consumer1 = session.createConsumer(queue1);
+ // Dummy just to create the queue.
+ _logger.info("Create Consumer of Q2");
+ MessageConsumer consumer2 = session.createConsumer(queue2);
+ _logger.info("Close Consumer of Q2");
+ consumer2.close();
+
+ _logger.info("Create producer to Q2");
+ producer2 = session.createProducer(queue2);
+
+ _logger.info("Start Connection");
+ con.start();
+
+ _logger.info("Create prep connection");
+ prepCon = (AMQConnection) getConnection("guest", "guest");
+
+ _logger.info("Create prep session");
+ prepSession = prepCon.createSession(false, AMQSession.AUTO_ACKNOWLEDGE);
+
+ _logger.info("Create prep producer to Q1");
+ prepProducer1 = prepSession.createProducer(queue1);
+
+ _logger.info("Create prep connection start");
+ prepCon.start();
+
+ _logger.info("Create test connection");
+ testCon = (AMQConnection) getConnection("guest", "guest");
+ _logger.info("Create test session");
+ testSession = testCon.createSession(false, AMQSession.AUTO_ACKNOWLEDGE);
+ _logger.info("Create test consumer of q2");
+ testConsumer2 = testSession.createConsumer(queue2);
+ }
+ catch (Exception e)
+ {
+ _logger.error("setup error",e);
+ stopBroker();
+ throw e;
+ }
+ }
+
+ protected void tearDown() throws Exception
+ {
+ try
+ {
+ _logger.info("Close connection");
+ con.close();
+ _logger.info("Close test connection");
+ testCon.close();
+ _logger.info("Close prep connection");
+ prepCon.close();
+ }
+ catch (Exception e)
+ {
+ _logger.error("tear down error",e);
+ }
+ finally
+ {
+ super.tearDown();
+ }
+ }
+
+ public void testCommit() throws Exception
+ {
+ _logger.info("Send prep A");
+ prepProducer1.send(prepSession.createTextMessage("A"));
+ _logger.info("Send prep B");
+ prepProducer1.send(prepSession.createTextMessage("B"));
+ _logger.info("Send prep C");
+ prepProducer1.send(prepSession.createTextMessage("C"));
+
+ // send and receive some messages
+ _logger.info("Send X to Q2");
+ producer2.send(session.createTextMessage("X"));
+ _logger.info("Send Y to Q2");
+ producer2.send(session.createTextMessage("Y"));
+ _logger.info("Send Z to Q2");
+ producer2.send(session.createTextMessage("Z"));
+
+ _logger.info("Read A from Q1");
+ expect("A", consumer1.receive(1000));
+ _logger.info("Read B from Q1");
+ expect("B", consumer1.receive(1000));
+ _logger.info("Read C from Q1");
+ expect("C", consumer1.receive(1000));
+
+ // commit
+ _logger.info("session commit");
+ session.commit();
+ _logger.info("Start test Connection");
+ testCon.start();
+
+ // ensure sent messages can be received and received messages are gone
+ _logger.info("Read X from Q2");
+ expect("X", testConsumer2.receive(1000));
+ _logger.info("Read Y from Q2");
+ expect("Y", testConsumer2.receive(1000));
+ _logger.info("Read Z from Q2");
+ expect("Z", testConsumer2.receive(1000));
+
+ _logger.info("create test session on Q1");
+ testConsumer1 = testSession.createConsumer(queue1);
+ _logger.info("Read null from Q1");
+ assertTrue(null == testConsumer1.receive(1000));
+ _logger.info("Read null from Q2");
+ assertTrue(null == testConsumer2.receive(1000));
+ }
+
+ public void testRollback() throws Exception
+ {
+ // add some messages
+ _logger.info("Send prep RB_A");
+ prepProducer1.send(prepSession.createTextMessage("RB_A"));
+ _logger.info("Send prep RB_B");
+ prepProducer1.send(prepSession.createTextMessage("RB_B"));
+ _logger.info("Send prep RB_C");
+ prepProducer1.send(prepSession.createTextMessage("RB_C"));
+
+ _logger.info("Sending RB_X RB_Y RB_Z");
+ producer2.send(session.createTextMessage("RB_X"));
+ producer2.send(session.createTextMessage("RB_Y"));
+ producer2.send(session.createTextMessage("RB_Z"));
+ _logger.info("Receiving RB_A RB_B");
+ expect("RB_A", consumer1.receive(1000));
+ expect("RB_B", consumer1.receive(1000));
+ // Don't consume 'RB_C' leave it in the prefetch cache to ensure rollback removes it.
+ // Quick sleep to ensure 'RB_C' gets pre-fetched
+ Thread.sleep(500);
+
+ // rollback
+ _logger.info("rollback");
+ session.rollback();
+
+ _logger.info("Receiving RB_A RB_B RB_C");
+ // ensure sent messages are not visible and received messages are requeued
+ expect("RB_A", consumer1.receive(1000), true);
+ expect("RB_B", consumer1.receive(1000), true);
+ expect("RB_C", consumer1.receive(1000), isBroker010()?false:true);
+ _logger.info("Starting new connection");
+ testCon.start();
+ testConsumer1 = testSession.createConsumer(queue1);
+ _logger.info("Testing we have no messages left");
+ assertTrue(null == testConsumer1.receive(1000));
+ assertTrue(null == testConsumer2.receive(1000));
+
+ session.commit();
+
+ _logger.info("Testing we have no messages left after commit");
+ assertTrue(null == testConsumer1.receive(1000));
+ assertTrue(null == testConsumer2.receive(1000));
+ }
+
+ public void testResendsMsgsAfterSessionClose() throws Exception
+ {
+ AMQConnection con = (AMQConnection) getConnection("guest", "guest");
+
+ Session consumerSession = con.createSession(true, Session.SESSION_TRANSACTED);
+ AMQQueue queue3 = new AMQQueue(consumerSession.getDefaultQueueExchangeName(), new AMQShortString("Q3"), false);
+ MessageConsumer consumer = consumerSession.createConsumer(queue3);
+
+ AMQConnection con2 = (AMQConnection) getConnection("guest", "guest");
+ Session producerSession = con2.createSession(true, Session.SESSION_TRANSACTED);
+ MessageProducer producer = producerSession.createProducer(queue3);
+
+ _logger.info("Sending four messages");
+ producer.send(producerSession.createTextMessage("msg1"));
+ producer.send(producerSession.createTextMessage("msg2"));
+ producer.send(producerSession.createTextMessage("msg3"));
+ producer.send(producerSession.createTextMessage("msg4"));
+
+ producerSession.commit();
+
+ _logger.info("Starting connection");
+ con.start();
+ TextMessage tm = (TextMessage) consumer.receive();
+ assertNotNull(tm);
+ assertEquals("msg1", tm.getText());
+
+ consumerSession.commit();
+
+ _logger.info("Received and committed first message");
+ tm = (TextMessage) consumer.receive(1000);
+ assertNotNull(tm);
+ assertEquals("msg2", tm.getText());
+
+ tm = (TextMessage) consumer.receive(1000);
+ assertNotNull(tm);
+ assertEquals("msg3", tm.getText());
+
+ tm = (TextMessage) consumer.receive(1000);
+ assertNotNull(tm);
+ assertEquals("msg4", tm.getText());
+
+ _logger.info("Received all four messages. Closing connection with three outstanding messages");
+
+ consumerSession.close();
+
+ consumerSession = con.createSession(true, Session.SESSION_TRANSACTED);
+
+ consumer = consumerSession.createConsumer(queue3);
+
+ // no ack for last three messages so when I call recover I expect to get three messages back
+ tm = (TextMessage) consumer.receive(3000);
+ assertNotNull(tm);
+ assertEquals("msg2", tm.getText());
+ assertTrue("Message is not redelivered", tm.getJMSRedelivered());
+
+ tm = (TextMessage) consumer.receive(3000);
+ assertNotNull(tm);
+ assertEquals("msg3", tm.getText());
+ assertTrue("Message is not redelivered", tm.getJMSRedelivered());
+
+ tm = (TextMessage) consumer.receive(3000);
+ assertNotNull(tm);
+ assertEquals("msg4", tm.getText());
+ assertTrue("Message is not redelivered", tm.getJMSRedelivered());
+
+ _logger.info("Received redelivery of three messages. Committing");
+
+ consumerSession.commit();
+
+ _logger.info("Called commit");
+
+ tm = (TextMessage) consumer.receive(1000);
+ assertNull(tm);
+
+ _logger.info("No messages redelivered as is expected");
+
+ con.close();
+ con2.close();
+ }
+
+ public void testCommitOnClosedConnection() throws Exception
+ {
+ Connection connnection = getConnection();
+ javax.jms.Session transactedSession = connnection.createSession(true, Session.SESSION_TRANSACTED);
+ connnection.close();
+ try
+ {
+ transactedSession.commit();
+ fail("Commit on closed connection should throw IllegalStateException!");
+ }
+ catch(IllegalStateException e)
+ {
+ // passed
+ }
+ }
+
+ public void testCommitOnClosedSession() throws Exception
+ {
+ Connection connnection = getConnection();
+ javax.jms.Session transactedSession = connnection.createSession(true, Session.SESSION_TRANSACTED);
+ transactedSession.close();
+ try
+ {
+ transactedSession.commit();
+ fail("Commit on closed session should throw IllegalStateException!");
+ }
+ catch(IllegalStateException e)
+ {
+ // passed
+ }
+ }
+
+ public void testRollbackOnClosedSession() throws Exception
+ {
+ Connection connnection = getConnection();
+ javax.jms.Session transactedSession = connnection.createSession(true, Session.SESSION_TRANSACTED);
+ transactedSession.close();
+ try
+ {
+ transactedSession.rollback();
+ fail("Rollback on closed session should throw IllegalStateException!");
+ }
+ catch(IllegalStateException e)
+ {
+ // passed
+ }
+ }
+
+ public void testGetTransactedOnClosedSession() throws Exception
+ {
+ Connection connnection = getConnection();
+ javax.jms.Session transactedSession = connnection.createSession(true, Session.SESSION_TRANSACTED);
+ transactedSession.close();
+ try
+ {
+ transactedSession.getTransacted();
+ fail("According to Sun TCK invocation of Session#getTransacted on closed session should throw IllegalStateException!");
+ }
+ catch(IllegalStateException e)
+ {
+ // passed
+ }
+ }
+
+ private void expect(String text, Message msg) throws JMSException
+ {
+ expect(text, msg, false);
+ }
+
+ private void expect(String text, Message msg, boolean requeued) throws JMSException
+ {
+ assertNotNull("Message should not be null", msg);
+ assertTrue("Message should be a text message", msg instanceof TextMessage);
+ assertEquals("Message content does not match expected", text, ((TextMessage) msg).getText());
+ assertEquals("Message should " + (requeued ? "" : "not") + " be requeued", requeued, msg.getJMSRedelivered());
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(TransactedTest.class);
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/transacted/TransactionTimeoutDisabledTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/transacted/TransactionTimeoutDisabledTest.java
new file mode 100644
index 0000000000..e37c6cf54b
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/transacted/TransactionTimeoutDisabledTest.java
@@ -0,0 +1,77 @@
+/*
+ *
+ * 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.qpid.test.unit.transacted;
+
+import org.apache.qpid.test.utils.TestBrokerConfiguration;
+
+/**
+ * This verifies that the default behaviour is not to time out transactions.
+ */
+public class TransactionTimeoutDisabledTest extends TransactionTimeoutTestCase
+{
+ @Override
+ protected void configure() throws Exception
+ {
+ // Setup housekeeping every second
+ TestBrokerConfiguration brokerConfiguration = getBrokerConfiguration();
+ setTestSystemProperty("virtualhost.housekeepingCheckPeriod", "100");
+
+ // No transaction timeout configuration.
+ }
+
+ public void testProducerIdleCommit() throws Exception
+ {
+ try
+ {
+ send(5, 0);
+
+ sleep(2.0f);
+
+ _psession.commit();
+ }
+ catch (Exception e)
+ {
+ fail("Should have succeeded");
+ }
+
+ assertEquals("Listener should not have received exception", 0, getNumberOfDeliveredExceptions());
+
+ monitor(0, 0);
+ }
+
+ public void testProducerOpenCommit() throws Exception
+ {
+ try
+ {
+ send(5, 0.3f);
+
+ _psession.commit();
+ }
+ catch (Exception e)
+ {
+ fail("Should have succeeded");
+ }
+
+ assertEquals("Listener should not have received exception", 0, getNumberOfDeliveredExceptions());
+
+ monitor(0, 0);
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/transacted/TransactionTimeoutTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/transacted/TransactionTimeoutTest.java
new file mode 100644
index 0000000000..b84e03972d
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/transacted/TransactionTimeoutTest.java
@@ -0,0 +1,350 @@
+/*
+ *
+ * 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.qpid.test.unit.transacted;
+
+import javax.jms.DeliveryMode;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+
+import org.apache.qpid.server.model.Broker;
+import org.apache.qpid.test.utils.TestBrokerConfiguration;
+
+/**
+ * This tests the behaviour of transactional sessions when the {@code transactionTimeout} configuration
+ * is set for a virtual host.
+ *
+ * A producer that is idle for too long or open for too long will have its connection/session(0-10) closed and
+ * any further operations will fail with a 408 resource timeout exception. Consumers will not
+ * be affected by the transaction timeout configuration.
+ */
+public class TransactionTimeoutTest extends TransactionTimeoutTestCase
+{
+
+ protected void configure() throws Exception
+ {
+ // switch off connection close in order to test timeout on publishing of unroutable messages
+ getBrokerConfiguration().setBrokerAttribute(Broker.CONNECTION_CLOSE_WHEN_NO_ROUTE, false);
+
+ // Setup housekeeping every 100ms
+ TestBrokerConfiguration brokerConfiguration = getBrokerConfiguration();
+ setTestSystemProperty("virtualhost.housekeepingCheckPeriod","100");
+
+ if (getName().contains("ProducerIdle"))
+ {
+ setTestSystemProperty("virtualhost.storeTransactionOpenTimeoutWarn", "0");
+ setTestSystemProperty("virtualhost.storeTransactionOpenTimeoutClose", "0");
+ setTestSystemProperty("virtualhost.storeTransactionIdleTimeoutWarn", "500");
+ setTestSystemProperty("virtualhost.storeTransactionIdleTimeoutClose", "1500");
+ }
+ else if (getName().contains("ProducerOpen"))
+ {
+ setTestSystemProperty("virtualhost.storeTransactionOpenTimeoutWarn", "1000");
+ setTestSystemProperty("virtualhost.storeTransactionOpenTimeoutClose", "2000");
+ setTestSystemProperty("virtualhost.storeTransactionIdleTimeoutWarn", "0");
+ setTestSystemProperty("virtualhost.storeTransactionIdleTimeoutClose", "0");
+ }
+ else
+ {
+ setTestSystemProperty("virtualhost.storeTransactionOpenTimeoutWarn", "1000");
+ setTestSystemProperty("virtualhost.storeTransactionOpenTimeoutClose", "2000");
+ setTestSystemProperty("virtualhost.storeTransactionIdleTimeoutWarn", "500");
+ setTestSystemProperty("virtualhost.storeTransactionIdleTimeoutClose", "1500");
+ }
+ }
+
+ public void testProducerIdle() throws Exception
+ {
+ sleep(2.0f);
+
+ _psession.commit();
+
+ assertEquals("Listener should not have received exception", 0, getNumberOfDeliveredExceptions());
+
+ monitor(0, 0);
+ }
+
+ public void testProducerIdleCommit() throws Exception
+ {
+ send(5, 0);
+ // Idle for more than idleClose to generate idle-warns and cause a close.
+ sleep(2.0f);
+
+ try
+ {
+ _psession.commit();
+ fail("Exception not thrown");
+ }
+ catch (Exception e)
+ {
+ _exception = e;
+ }
+
+ monitor(10, 0);
+
+ check(IDLE);
+ }
+
+ public void testProducerIdleCommitTwice() throws Exception
+ {
+ send(5, 0);
+ // Idle for less than idleClose to generate idle-warns
+ sleep(1.0f);
+
+ _psession.commit();
+
+ send(5, 0);
+ // Now idle for more than idleClose to generate more idle-warns and cause a close.
+ sleep(2.0f);
+
+ try
+ {
+ _psession.commit();
+ fail("Exception not thrown");
+ }
+ catch (Exception e)
+ {
+ _exception = e;
+ }
+
+ monitor(15, 0);
+
+ check(IDLE);
+ }
+
+ public void testProducerIdleRollback() throws Exception
+ {
+ send(5, 0);
+ // Now idle for more than idleClose to generate more idle-warns and cause a close.
+ sleep(2.0f);
+ try
+ {
+ _psession.rollback();
+ fail("Exception not thrown");
+ }
+ catch (Exception e)
+ {
+ _exception = e;
+ }
+
+ monitor(10, 0);
+
+ check(IDLE);
+ }
+
+ public void testProducerIdleRollbackTwice() throws Exception
+ {
+ send(5, 0);
+ // Idle for less than idleClose to generate idle-warns
+ sleep(1.0f);
+ _psession.rollback();
+ send(5, 0);
+ // Now idle for more than idleClose to generate more idle-warns and cause a close.
+ sleep(2.0f);
+ try
+ {
+ _psession.rollback();
+ fail("should fail");
+ }
+ catch (Exception e)
+ {
+ _exception = e;
+ }
+
+ monitor(15, 0);
+
+ check(IDLE);
+ }
+
+ public void testProducerOpenCommit() throws Exception
+ {
+ try
+ {
+ // Sleep between sends to cause open warns and then cause a close.
+ send(6, 0.5f);
+ _psession.commit();
+ fail("Exception not thrown");
+ }
+ catch (Exception e)
+ {
+ _exception = e;
+ }
+
+ monitor(0, 10);
+
+ check(OPEN);
+ }
+
+ public void testProducerOpenCommitTwice() throws Exception
+ {
+ send(5, 0);
+ sleep(1.0f);
+ _psession.commit();
+
+ try
+ {
+ // Now sleep between sends to cause open warns and then cause a close.
+ send(6, 0.5f);
+ _psession.commit();
+ fail("Exception not thrown");
+ }
+ catch (Exception e)
+ {
+ _exception = e;
+ }
+
+ monitor(0, 10);
+
+ check(OPEN);
+ }
+
+ public void testConsumerCommitClose() throws Exception
+ {
+ send(1, 0);
+
+ _psession.commit();
+
+ expect(1, 0);
+
+ _csession.commit();
+
+ sleep(3.0f);
+
+ _csession.close();
+
+ assertEquals("Listener should not have received exception", 0, getNumberOfDeliveredExceptions());
+
+ monitor(0, 0);
+ }
+
+ public void testConsumerIdleReceiveCommit() throws Exception
+ {
+ send(1, 0);
+
+ _psession.commit();
+
+ sleep(2.0f);
+
+ expect(1, 0);
+
+ sleep(2.0f);
+
+ _csession.commit();
+
+ assertEquals("Listener should not have received exception", 0, getNumberOfDeliveredExceptions());
+
+ monitor(0, 0);
+ }
+
+ public void testConsumerIdleCommit() throws Exception
+ {
+ send(1, 0);
+
+ _psession.commit();
+
+ expect(1, 0);
+
+ sleep(2.0f);
+
+ _csession.commit();
+
+ assertEquals("Listener should not have received exception", 0, getNumberOfDeliveredExceptions());
+
+ monitor(0, 0);
+ }
+
+ public void testConsumerIdleRollback() throws Exception
+ {
+ send(1, 0);
+
+ _psession.commit();
+
+ expect(1, 0);
+
+ sleep(2.0f);
+
+ _csession.rollback();
+
+ assertEquals("Listener should not have received exception", 0, getNumberOfDeliveredExceptions());
+
+ monitor(0, 0);
+ }
+
+ public void testConsumerOpenCommit() throws Exception
+ {
+ send(1, 0);
+
+ _psession.commit();
+
+ sleep(3.0f);
+
+ _csession.commit();
+
+ assertEquals("Listener should not have received exception", 0, getNumberOfDeliveredExceptions());
+
+ monitor(0, 0);
+ }
+
+ public void testConsumerOpenRollback() throws Exception
+ {
+ send(1, 0);
+
+ _psession.commit();
+
+ sleep(3.0f);
+
+ _csession.rollback();
+
+ assertEquals("Listener should not have received exception", 0, getNumberOfDeliveredExceptions());
+
+ monitor(0, 0);
+ }
+
+ /**
+ * Tests that sending an unroutable persistent message does not result in a long running store transaction [warning].
+ */
+ public void testTransactionCommittedOnNonRoutableQueuePersistentMessage() throws Exception
+ {
+ checkTransactionCommittedOnNonRoutableQueueMessage(DeliveryMode.PERSISTENT);
+ }
+
+ /**
+ * Tests that sending an unroutable transient message does not result in a long running store transaction [warning].
+ */
+ public void testTransactionCommittedOnNonRoutableQueueTransientMessage() throws Exception
+ {
+ checkTransactionCommittedOnNonRoutableQueueMessage(DeliveryMode.NON_PERSISTENT);
+ }
+
+ private void checkTransactionCommittedOnNonRoutableQueueMessage(int deliveryMode) throws JMSException, Exception
+ {
+ Queue nonExisting = _psession.createQueue(getTestQueueName() + System.currentTimeMillis());
+ MessageProducer producer = _psession.createProducer(nonExisting);
+ Message message = _psession.createMessage();
+ producer.send(message, deliveryMode, Message.DEFAULT_PRIORITY, Message.DEFAULT_TIME_TO_LIVE);
+ _psession.commit();
+
+ // give time to house keeping thread to log messages
+ sleep(3f);
+ monitor(0, 0);
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/transacted/TransactionTimeoutTestCase.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/transacted/TransactionTimeoutTestCase.java
new file mode 100644
index 0000000000..98fe29f826
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/transacted/TransactionTimeoutTestCase.java
@@ -0,0 +1,244 @@
+/*
+ *
+ * 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.qpid.test.unit.transacted;
+
+import junit.framework.TestCase;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.configuration.ClientProperties;
+import org.apache.qpid.protocol.AMQConstant;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+import org.apache.qpid.util.LogMonitor;
+
+import javax.jms.Connection;
+import javax.jms.DeliveryMode;
+import javax.jms.ExceptionListener;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * The {@link TestCase} for transaction timeout testing.
+ */
+public abstract class TransactionTimeoutTestCase extends QpidBrokerTestCase implements ExceptionListener
+{
+ private static final int ALERT_MESSAGE_TOLERANCE = 6;
+ public static final String VIRTUALHOST = "test";
+ public static final String TEXT = "0123456789abcdefghiforgettherest";
+ public static final String CHN_OPEN_TXN = "CHN-1007";
+ public static final String CHN_IDLE_TXN = "CHN-1008";
+ public static final String IDLE = "Idle";
+ public static final String OPEN = "Open";
+
+ protected LogMonitor _monitor;
+ protected Connection _con;
+ protected Session _psession, _csession;
+ protected Queue _queue;
+ protected MessageConsumer _consumer;
+ protected MessageProducer _producer;
+ protected Exception _exception;
+
+ private final CountDownLatch _exceptionListenerLatch = new CountDownLatch(1);
+ private final AtomicInteger _exceptionCount = new AtomicInteger(0);
+ private volatile AMQConstant _linkedExceptionCode;
+ private volatile String _linkedExceptionMessage;
+
+ /**
+ * Subclasses must implement this to configure transaction timeout parameters.
+ */
+ protected abstract void configure() throws Exception;
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ // Configure timeouts
+ configure();
+
+ // Monitor log file
+ _monitor = new LogMonitor(_outputFile);
+
+ // Start broker
+ super.setUp();
+
+ // Connect to broker
+ setTestClientSystemProperty(ClientProperties.MAX_PREFETCH_PROP_NAME, String.valueOf(1));
+ _con = getConnection();
+ _con.setExceptionListener(this);
+ _con.start();
+
+ // Create queue
+ Session qsession = _con.createSession(true, Session.SESSION_TRANSACTED);
+ _queue = qsession.createQueue(getTestQueueName());
+ qsession.close();
+
+ // Create producer and consumer
+ producer();
+ consumer();
+ }
+
+ /**
+ * Create a transacted persistent message producer session.
+ */
+ protected void producer() throws Exception
+ {
+ _psession = _con.createSession(true, Session.SESSION_TRANSACTED);
+ _producer = _psession.createProducer(_queue);
+ _producer.setDeliveryMode(DeliveryMode.PERSISTENT);
+ }
+
+ /**
+ * Create a transacted message consumer session.
+ */
+ protected void consumer() throws Exception
+ {
+ _csession = _con.createSession(true, Session.SESSION_TRANSACTED);
+ _consumer = _csession.createConsumer(_queue);
+ }
+
+ /**
+ * Send a number of messages to the queue, optionally pausing after each.
+ *
+ * Need to sync to ensure that the Broker has received the message(s) in order
+ * the test and broker start timing the idle transaction from the same point in time.
+ */
+ protected void send(int count, float delay) throws Exception
+ {
+ for (int i = 0; i < count; i++)
+ {
+ sleep(delay);
+ Message msg = _psession.createTextMessage(TEXT);
+ msg.setIntProperty("i", i);
+ _producer.send(msg);
+ }
+
+ ((AMQSession<?, ?>)_psession).sync();
+ }
+
+ /**
+ * Sleep for a number of seconds.
+ */
+ protected void sleep(float seconds) throws Exception
+ {
+ try
+ {
+ Thread.sleep((long) (seconds * 1000.0f));
+ }
+ catch (InterruptedException ie)
+ {
+ throw new RuntimeException("Interrupted");
+ }
+ }
+
+ /**
+ * Check for idle and open messages.
+ *
+ * Either exactly zero messages, or +-2 error accepted around the specified number.
+ */
+ protected void monitor(int idle, int open) throws Exception
+ {
+ List<String> idleMsgs = _monitor.findMatches(CHN_IDLE_TXN);
+ List<String> openMsgs = _monitor.findMatches(CHN_OPEN_TXN);
+
+ String idleErr = "Expected " + idle + " but found " + idleMsgs.size() + " txn idle messages";
+ String openErr = "Expected " + open + " but found " + openMsgs.size() + " txn open messages";
+
+ if (idle == 0)
+ {
+ assertTrue(idleErr, idleMsgs.isEmpty());
+ }
+ else
+ {
+ assertTrue(idleErr, idleMsgs.size() >= idle - ALERT_MESSAGE_TOLERANCE && idleMsgs.size() <= idle + ALERT_MESSAGE_TOLERANCE);
+ }
+
+ if (open == 0)
+ {
+ assertTrue(openErr, openMsgs.isEmpty());
+ }
+ else
+ {
+ assertTrue(openErr, openMsgs.size() >= open - ALERT_MESSAGE_TOLERANCE && openMsgs.size() <= open + ALERT_MESSAGE_TOLERANCE);
+ }
+ }
+
+ /**
+ * Receive a number of messages, optionally pausing after each.
+ */
+ protected void expect(int count, float delay) throws Exception
+ {
+ for (int i = 0; i < count; i++)
+ {
+ sleep(delay);
+ Message msg = _consumer.receive(1000);
+ assertNotNull("Message should not be null", msg);
+ assertTrue("Message should be a text message", msg instanceof TextMessage);
+ assertEquals("Message content does not match expected", TEXT, ((TextMessage) msg).getText());
+ assertEquals("Message order is incorrect", i, msg.getIntProperty("i"));
+ }
+ }
+
+ /**
+ * Checks that the correct exception was thrown and was received
+ * by the listener with a 506 error code.
+ */
+ protected void check(String reason) throws InterruptedException
+ {
+ assertNotNull("Should have thrown exception to client", _exception);
+
+ assertTrue("Should have caught exception in listener", _exceptionListenerLatch.await(1, TimeUnit.SECONDS));
+ assertNotNull("Linked exception message should not be null", _linkedExceptionMessage);
+ assertTrue("Linked exception message '" + _linkedExceptionMessage + "' should contain '" + reason + "'",
+ _linkedExceptionMessage.contains(reason + " transaction timed out"));
+ assertNotNull("Linked exception should have an error code", _linkedExceptionCode);
+ assertEquals("Linked exception error code should be 506", AMQConstant.RESOURCE_ERROR, _linkedExceptionCode);
+ }
+
+ /** @see javax.jms.ExceptionListener#onException(javax.jms.JMSException) */
+ @Override
+ public void onException(JMSException jmse)
+ {
+ if (jmse.getLinkedException() != null)
+ {
+ _linkedExceptionMessage = jmse.getLinkedException().getMessage();
+ }
+
+ if (jmse.getLinkedException() instanceof AMQException)
+ {
+ _linkedExceptionCode = ((AMQException) jmse.getLinkedException()).getErrorCode();
+ }
+ _exceptionCount.incrementAndGet();
+ _exceptionListenerLatch.countDown();
+ }
+
+ protected int getNumberOfDeliveredExceptions()
+ {
+ return _exceptionCount.get();
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/xa/AbstractXATestCase.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/xa/AbstractXATestCase.java
new file mode 100644
index 0000000000..92df1bd331
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/xa/AbstractXATestCase.java
@@ -0,0 +1,138 @@
+/* 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.qpid.test.unit.xa;
+
+import org.apache.qpid.dtx.XidImpl;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+import javax.jms.DeliveryMode;
+import javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.TextMessage;
+import javax.jms.XASession;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+import java.util.Random;
+
+/**
+ *
+ *
+ */
+public abstract class AbstractXATestCase extends QpidBrokerTestCase
+{
+ protected static final String _sequenceNumberPropertyName = "seqNumber";
+
+ /**
+ * the xaResource associated with the standard session
+ */
+ protected static XAResource _xaResource = null;
+
+ /**
+ * producer registered with the standard session
+ */
+ protected static MessageProducer _producer = null;
+
+ /**
+ * consumer registered with the standard session
+ */
+ protected static MessageConsumer _consumer = null;
+
+ /**
+ * a standard message
+ */
+ protected static TextMessage _message = null;
+
+ /**
+ * xid counter
+ */
+ private static int _xidCounter = (new Random()).nextInt(1000000);
+
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ init();
+ }
+
+ public abstract void init() throws Exception;
+
+
+
+ /**
+ * construct a new Xid
+ *
+ * @return a new Xid
+ */
+ protected Xid getNewXid()
+ {
+ byte[] branchQualifier;
+ byte[] globalTransactionID;
+ int format = _xidCounter;
+ String branchQualifierSt = "branchQualifier" + _xidCounter;
+ String globalTransactionIDSt = "globalTransactionID" + _xidCounter;
+ branchQualifier = branchQualifierSt.getBytes();
+ globalTransactionID = globalTransactionIDSt.getBytes();
+ _xidCounter++;
+ return new XidImpl(branchQualifier, format, globalTransactionID);
+ }
+
+ public void init(XASession session, Destination destination)
+ {
+ // get the xaResource
+ try
+ {
+ _xaResource = session.getXAResource();
+ }
+ catch (Exception e)
+ {
+ fail("cannot access the xa resource: " + e.getMessage());
+ }
+ // create standard producer
+ try
+ {
+ _producer = session.createProducer(destination);
+ _producer.setDeliveryMode(DeliveryMode.PERSISTENT);
+ }
+ catch (JMSException e)
+ {
+ _logger.error("Producer error",e);
+ fail("cannot create message producer: " + e.getMessage());
+ }
+ // create standard consumer
+ try
+ {
+ _consumer = session.createConsumer(destination);
+ }
+ catch (JMSException e)
+ {
+ fail("cannot create message consumer: " + e.getMessage());
+ }
+ // create a standard message
+ try
+ {
+ _message = session.createTextMessage();
+ _message.setText("test XA");
+ }
+ catch (JMSException e)
+ {
+ fail("cannot create standard message: " + e.getMessage());
+ }
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/xa/FaultTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/xa/FaultTest.java
new file mode 100644
index 0000000000..c5fa217aa9
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/xa/FaultTest.java
@@ -0,0 +1,414 @@
+/*
+ *
+ * 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.qpid.test.unit.xa;
+
+
+import junit.framework.TestSuite;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.jms.XAQueueConnection;
+import javax.jms.XAQueueConnectionFactory;
+import javax.jms.XAQueueSession;
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+
+
+public class FaultTest extends AbstractXATestCase
+{
+ private static final Logger _logger = LoggerFactory.getLogger(FaultTest.class);
+
+ /**
+ * the queue use by all the tests
+ */
+ private static Queue _queue = null;
+ /**
+ * the queue connection factory used by all tests
+ */
+ private static XAQueueConnectionFactory _queueFactory = null;
+
+ /**
+ * standard xa queue connection
+ */
+ private static XAQueueConnection _xaqueueConnection = null;
+
+ /**
+ * standard xa queue connection
+ */
+ private static QueueConnection _queueConnection = null;
+
+
+ /**
+ * standard queue session created from the standard connection
+ */
+ private static QueueSession _nonXASession = null;
+
+ /**
+ * the queue name
+ */
+ private static final String QUEUENAME = "xaQueue";
+
+ /** ----------------------------------------------------------------------------------- **/
+ /**
+ * ----------------------------- JUnit support ----------------------------------------- *
+ */
+
+ /**
+ * Gets the test suite tests
+ *
+ * @return the test suite tests
+ */
+ public static TestSuite getSuite()
+ {
+ return new TestSuite(QueueTest.class);
+ }
+
+ /**
+ * Run the test suite.
+ *
+ * @param args Any command line arguments specified to this class.
+ */
+ public static void main(String args[])
+ {
+ junit.textui.TestRunner.run(getSuite());
+ }
+
+ public void tearDown() throws Exception
+ {
+ if (!isBroker08())
+ {
+ _xaqueueConnection.close();
+ _queueConnection.close();
+ }
+ super.tearDown();
+ }
+
+ /**
+ * Initialize standard actors
+ */
+ public void init() throws Exception
+ {
+ if (!isBroker08())
+ {
+ _queue = (Queue) getInitialContext().lookup(QUEUENAME);
+ _queueFactory = getConnectionFactory();
+ _xaqueueConnection = _queueFactory.createXAQueueConnection("guest", "guest");
+ XAQueueSession session = _xaqueueConnection.createXAQueueSession();
+ _queueConnection = _queueFactory.createQueueConnection("guest","guest");
+ _nonXASession = _queueConnection.createQueueSession(true, Session.AUTO_ACKNOWLEDGE);
+ init(session, _queue);
+ }
+ }
+
+ /** -------------------------------------------------------------------------------------- **/
+ /** ----------------------------- Test Suite -------------------------------------------- **/
+ /** -------------------------------------------------------------------------------------- **/
+
+ /**
+ * Strategy:
+ * Invoke start twice with the same xid on an XA resource.
+ * Check that the second
+ * invocation is throwing the expected XA exception.
+ */
+ public void testSameXID() throws Exception
+ {
+ Xid xid = getNewXid();
+ _xaResource.start(xid, XAResource.TMNOFLAGS);
+ // we now exepct this operation to fail
+ try
+ {
+ _xaResource.start(xid, XAResource.TMNOFLAGS);
+ fail("We managed to start a transaction with the same xid");
+ }
+ catch (XAException e)
+ {
+ assertEquals("Wrong error code: ", XAException.XAER_DUPID, e.errorCode);
+ }
+ }
+
+ /**
+ * Strategy:
+ * Invoke start on a XA resource with flag other than TMNOFLAGS, TMJOIN, or TMRESUME.
+ * Check that a XA Exception is thrown.
+ */
+ public void testWrongStartFlag()
+ {
+ Xid xid = getNewXid();
+ try
+ {
+ _xaResource.start(xid, XAResource.TMONEPHASE);
+ fail("We managed to start a transaction with a wrong flag");
+ }
+ catch (XAException e)
+ {
+ assertEquals("Wrong error code: ", XAException.XAER_INVAL, e.errorCode);
+ }
+ }
+
+ /**
+ * Strategy:
+ * Check that a XA exception is thrown when:
+ * A non started xid is ended
+ */
+ public void testEnd()
+ {
+ Xid xid = getNewXid();
+ try
+ {
+ _xaResource.end(xid, XAResource.TMSUCCESS);
+ fail("We managed to end a transaction before it is started");
+ }
+ catch (XAException e)
+ {
+ assertEquals("Wrong error code: ", XAException.XAER_PROTO, e.errorCode);
+ }
+ }
+
+
+ /**
+ * Strategy:
+ * Check that a XA exception is thrown when:
+ * Call forget on an unknown xid
+ * call forget on a started xid
+ * A non started xid is prepared
+ * A non ended xis is prepared
+ */
+ public void testForget()
+ {
+ Xid xid = getNewXid();
+ try
+ {
+ _xaResource.forget(xid);
+ fail("We managed to forget an unknown xid");
+ }
+ catch (XAException e)
+ {
+ // assertEquals("Wrong error code: ", XAException.XAER_NOTA, e.errorCode);
+ }
+ xid = getNewXid();
+ try
+ {
+ _xaResource.start(xid, XAResource.TMNOFLAGS);
+ _xaResource.forget(xid);
+ fail("We managed to forget a started xid");
+ }
+ catch (XAException e)
+ {
+ assertEquals("Wrong error code: ", XAException.XAER_PROTO, e.errorCode);
+ }
+ }
+
+ /**
+ * Strategy:
+ * Check that a XA exception is thrown when:
+ * A non started xid is prepared
+ * A non ended xid is prepared
+ */
+ public void testPrepare()
+ {
+ Xid xid = getNewXid();
+ try
+ {
+ _xaResource.prepare(xid);
+ fail("We managed to prepare an unknown xid");
+ }
+ catch (XAException e)
+ {
+ assertEquals("Wrong error code: ", XAException.XAER_NOTA, e.errorCode);
+ }
+ xid = getNewXid();
+ try
+ {
+ _xaResource.start(xid, XAResource.TMNOFLAGS);
+ _xaResource.prepare(xid);
+ fail("We managed to prepare a started xid");
+ }
+ catch (XAException e)
+ {
+ assertEquals("Wrong error code: ", XAException.XAER_PROTO, e.errorCode);
+ }
+ }
+
+ /**
+ * Strategy:
+ * Check that the expected XA exception is thrown when:
+ * A non started xid is committed
+ * A non ended xid is committed
+ * A non prepared xid is committed with one phase set to false.
+ * A prepared xid is committed with one phase set to true.
+ */
+ public void testCommit() throws Exception
+ {
+ Xid xid = getNewXid();
+ try
+ {
+ _xaResource.commit(xid, true);
+ fail("We managed to commit an unknown xid");
+ }
+ catch (XAException e)
+ {
+ assertEquals("Wrong error code: ", XAException.XAER_NOTA, e.errorCode);
+ }
+ xid = getNewXid();
+ try
+ {
+ _xaResource.start(xid, XAResource.TMNOFLAGS);
+ _xaResource.commit(xid, true);
+ fail("We managed to commit a not ended xid");
+ }
+ catch (XAException e)
+ {
+ assertEquals("Wrong error code: ", XAException.XAER_PROTO, e.errorCode);
+ }
+ xid = getNewXid();
+ try
+ {
+ _xaResource.start(xid, XAResource.TMNOFLAGS);
+ _xaResource.end(xid, XAResource.TMSUCCESS);
+ _xaResource.commit(xid, false);
+ fail("We managed to commit a not prepared xid");
+ }
+ catch (XAException e)
+ {
+ assertEquals("Wrong error code: ", XAException.XAER_PROTO, e.errorCode);
+ }
+ xid = getNewXid();
+ try
+ {
+ _xaResource.start(xid, XAResource.TMNOFLAGS);
+ _xaResource.end(xid, XAResource.TMSUCCESS);
+ _xaResource.prepare(xid);
+ _xaResource.commit(xid, true);
+ fail("We managed to commit a prepared xid");
+ }
+ catch (XAException e)
+ {
+ assertEquals("Wrong error code: ", XAException.XAER_PROTO, e.errorCode);
+ }
+ finally
+ {
+ _xaResource.commit(xid, false);
+ }
+ }
+
+ /**
+ * Strategy:
+ * Check that the expected XA exception is thrown when:
+ * A non started xid is rolled back
+ * A non ended xid is rolled back
+ */
+ public void testRollback()
+ {
+ Xid xid = getNewXid();
+ try
+ {
+ _xaResource.rollback(xid);
+ fail("We managed to rollback an unknown xid");
+ }
+ catch (XAException e)
+ {
+ assertEquals("Wrong error code: ", XAException.XAER_NOTA, e.errorCode);
+ }
+ xid = getNewXid();
+ try
+ {
+ _xaResource.start(xid, XAResource.TMNOFLAGS);
+ _xaResource.rollback(xid);
+ fail("We managed to rollback a not ended xid");
+ }
+ catch (XAException e)
+ {
+ assertEquals("Wrong error code: ", XAException.XAER_PROTO, e.errorCode);
+ }
+ }
+
+ /**
+ * Strategy:
+ * Check that the timeout is set correctly
+ */
+ public void testTransactionTimeoutvalue() throws Exception
+ {
+ Xid xid = getNewXid();
+ _xaResource.start(xid, XAResource.TMNOFLAGS);
+ assertEquals("Wrong timeout", _xaResource.getTransactionTimeout(), 0);
+ _xaResource.setTransactionTimeout(1000);
+ assertEquals("Wrong timeout", _xaResource.getTransactionTimeout(), 1000);
+ _xaResource.end(xid, XAResource.TMSUCCESS);
+ xid = getNewXid();
+ _xaResource.start(xid, XAResource.TMNOFLAGS);
+ assertEquals("Wrong timeout", _xaResource.getTransactionTimeout(), 1000);
+ }
+
+ /**
+ * Strategy:
+ * Check that a transaction timeout as expected
+ * - set timeout to 1s
+ * - sleep 1500ms
+ * - call end and check that the expected exception is thrown
+ */
+ public void testTransactionTimeout() throws Exception
+ {
+ _xaResource.setTransactionTimeout(1);
+
+ Xid xid = getNewXid();
+ try
+ {
+ _xaResource.start(xid, XAResource.TMNOFLAGS);
+ Thread.sleep(1500);
+ _xaResource.end(xid, XAResource.TMSUCCESS);
+ fail("Timeout expected ");
+ }
+ catch (XAException e)
+ {
+ assertEquals("Wrong error code: ", XAException.XA_RBTIMEOUT, e.errorCode);
+ }
+ }
+
+ /**
+ * Strategy:
+ * Set the transaction timeout to 1000
+ */
+ public void testTransactionTimeoutAfterCommit() throws Exception
+ {
+ Xid xid = getNewXid();
+
+ _xaResource.start(xid, XAResource.TMNOFLAGS);
+ _xaResource.setTransactionTimeout(1000);
+ assertEquals("Wrong timeout", 1000,_xaResource.getTransactionTimeout());
+
+ //_xaResource.prepare(xid);
+ _xaResource.end(xid, XAResource.TMSUCCESS);
+ _xaResource.commit(xid, true);
+
+ _xaResource.setTransactionTimeout(2000);
+ assertEquals("Wrong timeout", 2000,_xaResource.getTransactionTimeout());
+
+ xid = getNewXid();
+ _xaResource.start(xid, XAResource.TMNOFLAGS);
+ assertEquals("Wrong timeout", 2000, _xaResource.getTransactionTimeout());
+ }
+
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/xa/QueueTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/xa/QueueTest.java
new file mode 100644
index 0000000000..350781e970
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/xa/QueueTest.java
@@ -0,0 +1,669 @@
+/* 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.qpid.test.unit.xa;
+
+import junit.framework.TestSuite;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.DeliveryMode;
+import javax.jms.JMSException;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import javax.jms.XAQueueConnection;
+import javax.jms.XAQueueConnectionFactory;
+import javax.jms.XAQueueSession;
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+
+public class QueueTest extends AbstractXATestCase
+{
+ private static final Logger _logger = LoggerFactory.getLogger(QueueTest.class);
+
+ /**
+ * the queue use by all the tests
+ */
+ private static Queue _queue = null;
+ /**
+ * the queue connection factory used by all tests
+ */
+ private static XAQueueConnectionFactory _queueFactory = null;
+
+ /**
+ * standard xa queue connection
+ */
+ private static XAQueueConnection _xaqueueConnection= null;
+
+ /**
+ * standard xa queue connection
+ */
+ private static QueueConnection _queueConnection=null;
+
+
+ /**
+ * standard queue session created from the standard connection
+ */
+ private static QueueSession _nonXASession = null;
+
+ /**
+ * the queue name
+ */
+ private static final String QUEUENAME = "xaQueue";
+
+ /** ----------------------------------------------------------------------------------- **/
+ /**
+ * ----------------------------- JUnit support ----------------------------------------- *
+ */
+
+ /**
+ * Gets the test suite tests
+ *
+ * @return the test suite tests
+ */
+ public static TestSuite getSuite()
+ {
+ return new TestSuite(QueueTest.class);
+ }
+
+ /**
+ * Run the test suite.
+ *
+ * @param args Any command line arguments specified to this class.
+ */
+ public static void main(String args[])
+ {
+ junit.textui.TestRunner.run(getSuite());
+ }
+
+ public void tearDown() throws Exception
+ {
+ if (!isBroker08())
+ {
+ try
+ {
+ _xaqueueConnection.close();
+ _queueConnection.close();
+ }
+ catch (Exception e)
+ {
+ fail("Exception thrown when cleaning standard connection: " + e.getStackTrace());
+ }
+ }
+ super.tearDown();
+ }
+
+ /**
+ * Initialize standard actors
+ */
+ public void init()
+ {
+ if (!isBroker08())
+ {
+ // lookup test queue
+ try
+ {
+ _queue = (Queue) getInitialContext().lookup(QUEUENAME);
+ }
+ catch (Exception e)
+ {
+ fail("cannot lookup test queue " + e.getMessage());
+ }
+
+ // lookup connection factory
+ try
+ {
+ _queueFactory = getConnectionFactory();
+ }
+ catch (Exception e)
+ {
+ fail("enable to lookup connection factory ");
+ }
+ // create standard connection
+ try
+ {
+ _xaqueueConnection= getNewQueueXAConnection();
+ }
+ catch (JMSException e)
+ {
+ fail("cannot create queue connection: " + e.getMessage());
+ }
+ // create xa session
+ XAQueueSession session = null;
+ try
+ {
+ session = _xaqueueConnection.createXAQueueSession();
+ }
+ catch (JMSException e)
+ {
+ fail("cannot create queue session: " + e.getMessage());
+ }
+ // create a standard session
+ try
+ {
+ _queueConnection = _queueFactory.createQueueConnection("guest", "guest");
+ _nonXASession = _queueConnection.createQueueSession(true, Session.AUTO_ACKNOWLEDGE);
+ }
+ catch (JMSException e)
+ {
+ _logger.error("cannot create queue session",e);
+ fail("cannot create queue session: " + e.getMessage());
+ }
+ init(session, _queue);
+ }
+ }
+
+ /** -------------------------------------------------------------------------------------- **/
+ /** ----------------------------- Test Suite -------------------------------------------- **/
+ /** -------------------------------------------------------------------------------------- **/
+
+ /**
+ * Uses two transactions respectively with xid1 and xid2 that are used to send a message
+ * within xid1 and xid2. xid2 is committed and xid1 is used to receive the message that was sent within xid2.
+ * Xid is then committed and a standard transaction is used to receive the message that was sent within xid1.
+ */
+ public void testProducer()
+ {
+ if (!isBroker08())
+ {
+ _logger.debug("running testProducer");
+ Xid xid1 = getNewXid();
+ Xid xid2 = getNewXid();
+ // start the xaResource for xid1
+ try
+ {
+ _xaResource.start(xid1, XAResource.TMNOFLAGS);
+ }
+ catch (XAException e)
+ {
+ _logger.error("cannot start the transaction with xid1", e);
+ fail("cannot start the transaction with xid1: " + e.getMessage());
+ }
+ try
+ {
+ // start the connection
+ _xaqueueConnection.start();
+ // produce a message with sequence number 1
+ _message.setLongProperty(_sequenceNumberPropertyName, 1);
+ _producer.send(_message);
+ }
+ catch (JMSException e)
+ {
+ fail(" cannot send persistent message: " + e.getMessage());
+ }
+ // suspend the transaction
+ try
+ {
+ _xaResource.end(xid1, XAResource.TMSUSPEND);
+ }
+ catch (XAException e)
+ {
+ fail("Cannot end the transaction with xid1: " + e.getMessage());
+ }
+ // start the xaResource for xid2
+ try
+ {
+ _xaResource.start(xid2, XAResource.TMNOFLAGS);
+ }
+ catch (XAException e)
+ {
+ fail("cannot start the transaction with xid2: " + e.getMessage());
+ }
+ try
+ {
+ // produce a message
+ _message.setLongProperty(_sequenceNumberPropertyName, 2);
+ _producer.send(_message);
+ }
+ catch (JMSException e)
+ {
+ fail(" cannot send second persistent message: " + e.getMessage());
+ }
+ // end xid2 and start xid1
+ try
+ {
+ _xaResource.end(xid2, XAResource.TMSUCCESS);
+ _xaResource.start(xid1, XAResource.TMRESUME);
+ }
+ catch (XAException e)
+ {
+ fail("Exception when ending and starting transactions: " + e.getMessage());
+ }
+ // two phases commit transaction with xid2
+ try
+ {
+ int resPrepare = _xaResource.prepare(xid2);
+ if (resPrepare != XAResource.XA_OK)
+ {
+ fail("prepare returned: " + resPrepare);
+ }
+ _xaResource.commit(xid2, false);
+ }
+ catch (XAException e)
+ {
+ fail("Exception thrown when preparing transaction with xid2: " + e.getMessage());
+ }
+ // receive a message from queue test we expect it to be the second one
+ try
+ {
+ TextMessage message = (TextMessage) _consumer.receive(1000);
+ if (message == null)
+ {
+ fail("did not receive second message as expected ");
+ }
+ else
+ {
+ if (message.getLongProperty(_sequenceNumberPropertyName) != 2)
+ {
+ fail("receive wrong message its sequence number is: " + message
+ .getLongProperty(_sequenceNumberPropertyName));
+ }
+ }
+ }
+ catch (JMSException e)
+ {
+ fail("Exception when receiving second message: " + e.getMessage());
+ }
+ // end and one phase commit the first transaction
+ try
+ {
+ _xaResource.end(xid1, XAResource.TMSUCCESS);
+ _xaResource.commit(xid1, true);
+ }
+ catch (XAException e)
+ {
+ fail("Exception thrown when commiting transaction with xid1");
+ }
+ // We should now be able to receive the first message
+ try
+ {
+ _xaqueueConnection.close();
+ Session nonXASession = _nonXASession;
+ MessageConsumer nonXAConsumer = nonXASession.createConsumer(_queue);
+ _queueConnection.start();
+ TextMessage message1 = (TextMessage) nonXAConsumer.receive(1000);
+ if (message1 == null)
+ {
+ fail("did not receive first message as expected ");
+ }
+ else
+ {
+ if (message1.getLongProperty(_sequenceNumberPropertyName) != 1)
+ {
+ fail("receive wrong message its sequence number is: " + message1
+ .getLongProperty(_sequenceNumberPropertyName));
+ }
+ }
+ // commit that transacted session
+ nonXASession.commit();
+ // the queue should be now empty
+ message1 = (TextMessage) nonXAConsumer.receive(1000);
+ if (message1 != null)
+ {
+ fail("receive an unexpected message ");
+ }
+ }
+ catch (JMSException e)
+ {
+ fail("Exception thrown when emptying the queue: " + e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * strategy: Produce a message within Tx1 and prepare tx1. crash the server then commit tx1 and consume the message
+ */
+ public void testSendAndRecover()
+ {
+ if (!isBroker08())
+ {
+ _logger.debug("running testSendAndRecover");
+ Xid xid1 = getNewXid();
+ // start the xaResource for xid1
+ try
+ {
+ _xaResource.start(xid1, XAResource.TMNOFLAGS);
+ }
+ catch (XAException e)
+ {
+ fail("cannot start the transaction with xid1: " + e.getMessage());
+ }
+ try
+ {
+ // start the connection
+ _xaqueueConnection.start();
+ // produce a message with sequence number 1
+ _message.setLongProperty(_sequenceNumberPropertyName, 1);
+ _producer.send(_message);
+ }
+ catch (JMSException e)
+ {
+ fail(" cannot send persistent message: " + e.getMessage());
+ }
+ // suspend the transaction
+ try
+ {
+ _xaResource.end(xid1, XAResource.TMSUCCESS);
+ }
+ catch (XAException e)
+ {
+ fail("Cannot end the transaction with xid1: " + e.getMessage());
+ }
+ // prepare the transaction with xid1
+ try
+ {
+ _xaResource.prepare(xid1);
+ }
+ catch (XAException e)
+ {
+ fail("Exception when preparing xid1: " + e.getMessage());
+ }
+
+ /////// stop the server now !!
+ try
+ {
+ _logger.debug("stopping broker");
+ restartBroker();
+ init();
+ }
+ catch (Exception e)
+ {
+ fail("Exception when stopping and restarting the server");
+ }
+
+ // get the list of in doubt transactions
+ try
+ {
+ Xid[] inDoubt = _xaResource.recover(XAResource.TMSTARTRSCAN);
+ if (inDoubt == null)
+ {
+ fail("the array of in doubt transactions should not be null ");
+ }
+ // At that point we expect only two indoubt transactions:
+ if (inDoubt.length != 1)
+ {
+ fail("in doubt transaction size is diffenrent thatn 2, there are " + inDoubt.length + "in doubt transactions");
+ }
+
+ // commit them
+ for (Xid anInDoubt : inDoubt)
+ {
+ if (anInDoubt.equals(xid1))
+ {
+ _logger.info("commit xid1 ");
+ try
+ {
+ _xaResource.commit(anInDoubt, false);
+ }
+ catch (Exception e)
+ {
+ _logger.error("PB when aborted xid1", e);
+ }
+ }
+ else
+ {
+ fail("did not receive right xid ");
+ }
+ }
+ }
+ catch (XAException e)
+ {
+ _logger.error("exception thrown when recovering transactions", e);
+ fail("exception thrown when recovering transactions " + e.getMessage());
+ }
+ // the queue should contain the first message!
+ try
+ {
+ _xaqueueConnection.close();
+ Session nonXASession = _nonXASession;
+ MessageConsumer nonXAConsumer = nonXASession.createConsumer(_queue);
+ _queueConnection.start();
+ TextMessage message1 = (TextMessage) nonXAConsumer.receive(1000);
+
+ if (message1 == null)
+ {
+ fail("queue does not contain any message!");
+ }
+ if (message1.getLongProperty(_sequenceNumberPropertyName) != 1)
+ {
+ fail("Wrong message returned! Sequence number is " + message1
+ .getLongProperty(_sequenceNumberPropertyName));
+ }
+ nonXASession.commit();
+ }
+ catch (JMSException e)
+ {
+ fail("Exception thrown when testin that queue test is not empty: " + e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * strategy: Produce a message within Tx1 and prepare tx1. Produce a standard message and consume
+ * it within tx2 and prepare tx2. Shutdown the server and get the list of in doubt transactions:
+ * we expect tx1 and tx2! Then, Tx1 is aborted and tx2 is committed so we expect the test's queue to be empty!
+ */
+ public void testRecover()
+ {
+ if (!isBroker08())
+ {
+ _logger.debug("running testRecover");
+ Xid xid1 = getNewXid();
+ Xid xid2 = getNewXid();
+ // start the xaResource for xid1
+ try
+ {
+ _xaResource.start(xid1, XAResource.TMNOFLAGS);
+ }
+ catch (XAException e)
+ {
+ fail("cannot start the transaction with xid1: " + e.getMessage());
+ }
+ try
+ {
+ // start the connection
+ _xaqueueConnection.start();
+ // produce a message with sequence number 1
+ _message.setLongProperty(_sequenceNumberPropertyName, 1);
+ _producer.send(_message);
+ }
+ catch (JMSException e)
+ {
+ fail(" cannot send persistent message: " + e.getMessage());
+ }
+ // suspend the transaction
+ try
+ {
+ _xaResource.end(xid1, XAResource.TMSUCCESS);
+ }
+ catch (XAException e)
+ {
+ fail("Cannot end the transaction with xid1: " + e.getMessage());
+ }
+ // prepare the transaction with xid1
+ try
+ {
+ _xaResource.prepare(xid1);
+ }
+ catch (XAException e)
+ {
+ fail("Exception when preparing xid1: " + e.getMessage());
+ }
+
+ // send a message using the standard session
+ try
+ {
+ Session nonXASession = _nonXASession;
+ MessageProducer nonXAProducer = nonXASession.createProducer(_queue);
+ TextMessage message2 = nonXASession.createTextMessage();
+ message2.setText("non XA ");
+ message2.setLongProperty(_sequenceNumberPropertyName, 2);
+ nonXAProducer.setDeliveryMode(DeliveryMode.PERSISTENT);
+ nonXAProducer.send(message2);
+ // commit that transacted session
+ nonXASession.commit();
+ }
+ catch (Exception e)
+ {
+ fail("Exception thrown when emptying the queue: " + e.getMessage());
+ }
+ // start the xaResource for xid2
+ try
+ {
+ _xaResource.start(xid2, XAResource.TMNOFLAGS);
+ }
+ catch (XAException e)
+ {
+ fail("cannot start the transaction with xid1: " + e.getMessage());
+ }
+ // receive a message from queue test we expect it to be the second one
+ try
+ {
+ TextMessage message = (TextMessage) _consumer.receive(1000);
+ if (message == null || message.getLongProperty(_sequenceNumberPropertyName) != 2)
+ {
+ fail("did not receive second message as expected ");
+ }
+ }
+ catch (JMSException e)
+ {
+ fail("Exception when receiving second message: " + e.getMessage());
+ }
+ // suspend the transaction
+ try
+ {
+ _xaResource.end(xid2, XAResource.TMSUCCESS);
+ }
+ catch (XAException e)
+ {
+ fail("Cannot end the transaction with xid2: " + e.getMessage());
+ }
+ // prepare the transaction with xid1
+ try
+ {
+ _xaResource.prepare(xid2);
+ }
+ catch (XAException e)
+ {
+ fail("Exception when preparing xid2: " + e.getMessage());
+ }
+
+ /////// stop the server now !!
+ try
+ {
+ _logger.debug("stopping broker");
+ restartBroker();
+ init();
+ }
+ catch (Exception e)
+ {
+ fail("Exception when stopping and restarting the server");
+ }
+
+ // get the list of in doubt transactions
+ try
+ {
+ Xid[] inDoubt = _xaResource.recover(XAResource.TMSTARTRSCAN);
+ if (inDoubt == null)
+ {
+ fail("the array of in doubt transactions should not be null ");
+ }
+ // At that point we expect only two indoubt transactions:
+ if (inDoubt.length != 2)
+ {
+ fail("in doubt transaction size is diffenrent thatn 2, there are " + inDoubt.length + "in doubt transactions");
+ }
+
+ // commit them
+ for (Xid anInDoubt : inDoubt)
+ {
+ if (anInDoubt.equals(xid1))
+ {
+ _logger.debug("rollback xid1 ");
+ try
+ {
+ _xaResource.rollback(anInDoubt);
+ }
+ catch (Exception e)
+ {
+ _logger.error("PB when aborted xid1", e);
+ }
+ }
+ else if (anInDoubt.equals(xid2))
+ {
+ _logger.debug("commit xid2 ");
+ try
+ {
+ _xaResource.commit(anInDoubt, false);
+ }
+ catch (Exception e)
+ {
+ _logger.error("PB when commiting xid2", e);
+ }
+ }
+ }
+ }
+ catch (XAException e)
+ {
+ _logger.error("exception thrown when recovering transactions", e);
+ fail("exception thrown when recovering transactions " + e.getMessage());
+ }
+ // the queue should be empty
+ try
+ {
+ _xaqueueConnection.close();
+ Session nonXASession = _nonXASession;
+ MessageConsumer nonXAConsumer = nonXASession.createConsumer(_queue);
+ _queueConnection.start();
+ TextMessage message1 = (TextMessage) nonXAConsumer.receive(1000);
+ if (message1 != null)
+ {
+
+ fail("The queue is not empty! " + message1.getLongProperty(_sequenceNumberPropertyName));
+ }
+ }
+ catch (JMSException e)
+ {
+ fail("Exception thrown when testin that queue test is empty: " + e.getMessage());
+ }
+ }
+ }
+
+ /** -------------------------------------------------------------------------------------- **/
+ /** ----------------------------- Utility methods --------------------------------------- **/
+ /** -------------------------------------------------------------------------------------- **/
+
+ /**
+ * get a new queue connection
+ *
+ * @return a new queue connection
+ * @throws JMSException If the JMS provider fails to create the queue connection
+ * due to some internal error or in case of authentication failure
+ */
+ private XAQueueConnection getNewQueueXAConnection() throws JMSException
+ {
+ return _queueFactory.createXAQueueConnection("guest", "guest");
+ }
+
+
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/xa/TopicTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/xa/TopicTest.java
new file mode 100644
index 0000000000..4d9242b8b3
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/unit/xa/TopicTest.java
@@ -0,0 +1,1742 @@
+/* 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.qpid.test.unit.xa;
+
+import junit.framework.TestSuite;
+import org.apache.qpid.configuration.ClientProperties;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.*;
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ *
+ *
+ */
+public class TopicTest extends AbstractXATestCase
+{
+ /* this class logger */
+ private static final Logger _logger = LoggerFactory.getLogger(TopicTest.class);
+
+ /**
+ * the topic use by all the tests
+ */
+ private static Topic _topic = null;
+
+ /**
+ * the topic connection factory used by all tests
+ */
+ private static XATopicConnectionFactory _topicFactory = null;
+
+ /**
+ * standard topic connection
+ */
+ private static XATopicConnection _topicConnection = null;
+
+ /**
+ * standard topic session created from the standard connection
+ */
+ private static XATopicSession _session = null;
+
+ private static TopicSession _nonXASession = null;
+
+ /**
+ * the topic name
+ */
+ private static final String TOPICNAME = "xaTopic";
+
+ /**
+ * Indicate that a listenere has failed
+ */
+ private static boolean _failure = false;
+
+ /** -------------------------------------------------------------------------------------- **/
+ /** ----------------------------- JUnit support ----------------------------------------- **/
+ /** -------------------------------------------------------------------------------------- **/
+
+ /**
+ * Gets the test suite tests
+ *
+ * @return the test suite tests
+ */
+ public static TestSuite getSuite()
+ {
+ return new TestSuite(TopicTest.class);
+ }
+
+ /**
+ * Run the test suite.
+ *
+ * @param args Any command line arguments specified to this class.
+ */
+ public static void main(String args[])
+ {
+ junit.textui.TestRunner.run(getSuite());
+ }
+
+ public void tearDown() throws Exception
+ {
+ if (!isBroker08())
+ {
+ try
+ {
+ _topicConnection.stop();
+ _topicConnection.close();
+ }
+ catch (Exception e)
+ {
+ fail("Exception thrown when cleaning standard connection: " + e);
+ }
+ }
+ super.tearDown();
+ }
+
+ /**
+ * Initialize standard actors
+ */
+ public void init()
+ {
+ if (!isBroker08())
+ {
+ setTestClientSystemProperty(ClientProperties.MAX_PREFETCH_PROP_NAME, "1");
+ // lookup test queue
+ try
+ {
+ _topic = (Topic) getInitialContext().lookup(TOPICNAME);
+ }
+ catch (Exception e)
+ {
+ fail("cannot lookup test topic " + e.getMessage());
+ }
+ // lookup connection factory
+ try
+ {
+ _topicFactory = getConnectionFactory();
+ }
+ catch (Exception e)
+ {
+ fail("enable to lookup connection factory ");
+ }
+ // create standard connection
+ try
+ {
+ _topicConnection = getNewTopicXAConnection();
+ }
+ catch (JMSException e)
+ {
+ fail("cannot create queue connection: " + e.getMessage());
+ }
+ // create standard session
+ try
+ {
+ _session = _topicConnection.createXATopicSession();
+ }
+ catch (JMSException e)
+ {
+ fail("cannot create queue session: " + e.getMessage());
+ }
+ // create a standard session
+ try
+ {
+ _nonXASession = _topicConnection.createTopicSession(true, Session.AUTO_ACKNOWLEDGE);
+ }
+ catch (JMSException e)
+ {
+ _logger.error("Error creating topic session", e);
+ }
+ init(_session, _topic);
+ }
+ }
+
+ /** -------------------------------------------------------------------------------------- **/
+ /** ----------------------------- Test Suite -------------------------------------------- **/
+ /** -------------------------------------------------------------------------------------- **/
+
+
+ /**
+ * Uses two transactions respectively with xid1 and xid2 that are use to send a message
+ * within xid1 and xid2. xid2 is committed and xid1 is used to receive the message that was sent within xid2.
+ * Xid is then committed and a standard transaction is used to receive the message that was sent within xid1.
+ */
+ public void testProducer()
+ {
+ if (!isBroker08())
+ {
+ _logger.debug("testProducer");
+ Xid xid1 = getNewXid();
+ Xid xid2 = getNewXid();
+ try
+ {
+ Session nonXASession = _nonXASession;
+ MessageConsumer nonXAConsumer = nonXASession.createConsumer(_topic);
+ _producer.setDeliveryMode(DeliveryMode.PERSISTENT);
+ // start the xaResource for xid1
+ try
+ {
+ _logger.debug("starting tx branch xid1");
+ _xaResource.start(xid1, XAResource.TMNOFLAGS);
+ }
+ catch (XAException e)
+ {
+ _logger.error("cannot start the transaction with xid1", e);
+ fail("cannot start the transaction with xid1: " + e.getMessage());
+ }
+ try
+ {
+ // start the connection
+ _topicConnection.start();
+ _logger.debug("produce a message with sequence number 1");
+ _message.setLongProperty(_sequenceNumberPropertyName, 1);
+ _producer.send(_message);
+ }
+ catch (JMSException e)
+ {
+ fail(" cannot send persistent message: " + e.getMessage());
+ }
+ _logger.debug("suspend the transaction branch xid1");
+ try
+ {
+ _xaResource.end(xid1, XAResource.TMSUSPEND);
+ }
+ catch (XAException e)
+ {
+ fail("Cannot end the transaction with xid1: " + e.getMessage());
+ }
+ _logger.debug("start the xaResource for xid2");
+ try
+ {
+ _xaResource.start(xid2, XAResource.TMNOFLAGS);
+ }
+ catch (XAException e)
+ {
+ fail("cannot start the transaction with xid2: " + e.getMessage());
+ }
+ try
+ {
+ _logger.debug("produce a message");
+ _message.setLongProperty(_sequenceNumberPropertyName, 2);
+ _producer.send(_message);
+ }
+ catch (JMSException e)
+ {
+ fail(" cannot send second persistent message: " + e.getMessage());
+ }
+ _logger.debug("end xid2 and start xid1");
+ try
+ {
+ _xaResource.end(xid2, XAResource.TMSUCCESS);
+ _xaResource.start(xid1, XAResource.TMRESUME);
+ }
+ catch (XAException e)
+ {
+ fail("Exception when ending and starting transactions: " + e.getMessage());
+ }
+ _logger.debug("two phases commit transaction with xid2");
+ try
+ {
+ int resPrepare = _xaResource.prepare(xid2);
+ if (resPrepare != XAResource.XA_OK)
+ {
+ fail("prepare returned: " + resPrepare);
+ }
+ _xaResource.commit(xid2, false);
+ }
+ catch (XAException e)
+ {
+ fail("Exception thrown when preparing transaction with xid2: " + e.getMessage());
+ }
+ _logger.debug("receiving a message from topic test we expect it to be the second one");
+ try
+ {
+ TextMessage message = (TextMessage) _consumer.receive(1000);
+ if (message == null)
+ {
+ fail("did not receive second message as expected ");
+ }
+ else
+ {
+ if (message.getLongProperty(_sequenceNumberPropertyName) != 2)
+ {
+ fail("receive wrong message its sequence number is: " + message
+ .getLongProperty(_sequenceNumberPropertyName));
+ }
+ }
+ }
+ catch (JMSException e)
+ {
+ fail("Exception when receiving second message: " + e.getMessage());
+ }
+ _logger.debug("end and one phase commit the first transaction");
+ try
+ {
+ _xaResource.end(xid1, XAResource.TMSUCCESS);
+ _xaResource.commit(xid1, true);
+ }
+ catch (XAException e)
+ {
+ fail("Exception thrown when commiting transaction with xid1");
+ }
+ _logger.debug("We should now be able to receive the first and second message");
+ try
+ {
+ TextMessage message1 = (TextMessage) nonXAConsumer.receive(1000);
+ if (message1 == null)
+ {
+ fail("did not receive first message as expected ");
+ }
+ else
+ {
+ if (message1.getLongProperty(_sequenceNumberPropertyName) != 2)
+ {
+ fail("receive wrong message its sequence number is: " + message1
+ .getLongProperty(_sequenceNumberPropertyName));
+ }
+ }
+ message1 = (TextMessage) nonXAConsumer.receive(1000);
+ if (message1 == null)
+ {
+ fail("did not receive first message as expected ");
+ }
+ else
+ {
+ if (message1.getLongProperty(_sequenceNumberPropertyName) != 1)
+ {
+ fail("receive wrong message its sequence number is: " + message1
+ .getLongProperty(_sequenceNumberPropertyName));
+ }
+ }
+ _logger.debug("commit transacted session");
+ nonXASession.commit();
+ _logger.debug("Test that the topic is now empty");
+ message1 = (TextMessage) nonXAConsumer.receive(1000);
+ if (message1 != null)
+ {
+ fail("receive an unexpected message ");
+ }
+ }
+ catch (JMSException e)
+ {
+ fail("Exception thrown when emptying the queue: " + e.getMessage());
+ }
+ }
+ catch (JMSException e)
+ {
+ fail("cannot create standard consumer: " + e.getMessage());
+ }
+ }
+ }
+
+
+ /**
+ * strategy: Produce a message within Tx1 and commit tx1. consume this message within tx2 and abort tx2.
+ * Consume the same message within tx3 and commit it. Check that no more message is available.
+ */
+ public void testDurSub()
+ {
+ if (!isBroker08())
+ {
+ Xid xid1 = getNewXid();
+ Xid xid2 = getNewXid();
+ Xid xid3 = getNewXid();
+ Xid xid4 = getNewXid();
+ String durSubName = "xaSubDurable";
+ try
+ {
+ TopicSubscriber xaDurSub = _session.createDurableSubscriber(_topic, durSubName);
+ try
+ {
+ _topicConnection.start();
+ _logger.debug("start xid1");
+ _xaResource.start(xid1, XAResource.TMNOFLAGS);
+ // start the connection
+ _topicConnection.start();
+ _logger.debug("produce a message with sequence number 1");
+ _message.setLongProperty(_sequenceNumberPropertyName, 1);
+ _producer.send(_message);
+ _logger.debug("2 phases commit xid1");
+ _xaResource.end(xid1, XAResource.TMSUCCESS);
+ if (_xaResource.prepare(xid1) != XAResource.XA_OK)
+ {
+ fail("Problem when preparing tx1 ");
+ }
+ _xaResource.commit(xid1, false);
+ }
+ catch (Exception e)
+ {
+ _logger.error("Exception when working with xid1", e);
+ fail("Exception when working with xid1: " + e.getMessage());
+ }
+ try
+ {
+ _logger.debug("start xid2");
+ _xaResource.start(xid2, XAResource.TMNOFLAGS);
+ _logger.debug("receive the previously produced message");
+ TextMessage message = (TextMessage) xaDurSub.receive(1000);
+ if (message == null)
+ {
+ fail("no message received ");
+ }
+ else if (message.getLongProperty(_sequenceNumberPropertyName) != 1)
+ {
+ fail("wrong sequence number: " + message.getLongProperty(_sequenceNumberPropertyName));
+ }
+ _logger.debug("rollback xid2");
+ boolean rollbackOnFailure = false;
+ try
+ {
+ _xaResource.end(xid2, XAResource.TMFAIL);
+ }
+ catch (XAException e)
+ {
+ if (e.errorCode != XAException.XA_RBROLLBACK)
+ {
+ fail("Exception when working with xid2: " + e.getMessage());
+ }
+ rollbackOnFailure = true;
+ }
+ if (!rollbackOnFailure)
+ {
+ if (_xaResource.prepare(xid2) != XAResource.XA_OK)
+ {
+ fail("Problem when preparing tx2 ");
+ }
+ _xaResource.rollback(xid2);
+ }
+ }
+ catch (Exception e)
+ {
+ _logger.error("Exception when working with xid2", e);
+ fail("Exception when working with xid2: " + e.getMessage());
+ }
+ try
+ {
+ _logger.debug("start xid3");
+ _xaResource.start(xid3, XAResource.TMNOFLAGS);
+ _logger.debug(" receive the previously aborted consumed message");
+ TextMessage message = (TextMessage) xaDurSub.receive(1000);
+ if (message == null)
+ {
+ fail("no message received ");
+ }
+ else if (message.getLongProperty(_sequenceNumberPropertyName) != 1)
+ {
+ fail("wrong sequence number: " + message.getLongProperty(_sequenceNumberPropertyName));
+ }
+ _logger.debug("commit xid3");
+ _xaResource.end(xid3, XAResource.TMSUCCESS);
+ if (_xaResource.prepare(xid3) != XAResource.XA_OK)
+ {
+ fail("Problem when preparing tx3 ");
+ }
+ _xaResource.commit(xid3, false);
+ }
+ catch (Exception e)
+ {
+ _logger.error("Exception when working with xid3", e);
+ fail("Exception when working with xid3: " + e.getMessage());
+ }
+ try
+ {
+ _logger.debug("start xid4");
+ _xaResource.start(xid4, XAResource.TMNOFLAGS);
+ _logger.debug("check that topic is empty");
+ TextMessage message = (TextMessage) xaDurSub.receive(1000);
+ if (message != null)
+ {
+ fail("An unexpected message was received ");
+ }
+ _logger.debug("commit xid4");
+ _xaResource.end(xid4, XAResource.TMSUCCESS);
+ _xaResource.commit(xid4, true);
+ }
+ catch (Exception e)
+ {
+ _logger.error("Exception when working with xid4", e);
+ fail("Exception when working with xid4: " + e.getMessage());
+ }
+ }
+ catch (Exception e)
+ {
+ _logger.error("problem when creating dur sub", e);
+ fail("problem when creating dur sub: " + e.getMessage());
+ }
+ finally
+ {
+ try
+ {
+ _session.unsubscribe(durSubName);
+ }
+ catch (JMSException e)
+ {
+ _logger.error("problem when unsubscribing dur sub", e);
+ fail("problem when unsubscribing dur sub: " + e.getMessage());
+ }
+ }
+ }
+ }
+
+ /**
+ * strategy: create a XA durable subscriber dusSub, produce 7 messages with the standard session,
+ * consume 2 messages respectively with tx1, tx2 and tx3
+ * abort tx2, we now expect to receive messages 3 and 4 first! Receive 3 messages within tx1 i.e. 34 and 7!
+ * commit tx3
+ * abort tx1: we now expect that only messages 5 and 6 are definitly consumed!
+ * start tx4 and consume messages 1 - 4 and 7
+ * commit tx4
+ * Now the topic should be empty!
+ */
+ public void testMultiMessagesDurSub()
+ {
+ if (!isBroker08())
+ {
+ Xid xid1 = getNewXid();
+ Xid xid2 = getNewXid();
+ Xid xid3 = getNewXid();
+ Xid xid4 = getNewXid();
+ Xid xid6 = getNewXid();
+ String durSubName = "xaSubDurable";
+ TextMessage message;
+ try
+ {
+ TopicSubscriber xaDurSub = _session.createDurableSubscriber(_topic, durSubName);
+ try
+ {
+ Session txSession = _nonXASession;
+ MessageProducer txProducer = txSession.createProducer(_topic);
+ _logger.debug("produce 10 persistent messages");
+ txProducer.setDeliveryMode(DeliveryMode.PERSISTENT);
+ _topicConnection.start();
+ for (int i = 1; i <= 7; i++)
+ {
+ _message.setLongProperty(_sequenceNumberPropertyName, i);
+ txProducer.send(_message);
+ }
+ // commit txSession
+ txSession.commit();
+ }
+ catch (JMSException e)
+ {
+ _logger.error("Exception thrown when producing messages", e);
+ fail("Exception thrown when producing messages: " + e.getMessage());
+ }
+
+ try
+ {
+ _logger.debug(" consume 2 messages respectively with tx1, tx2 and tx3");
+ //----- start xid1
+ _xaResource.start(xid1, XAResource.TMNOFLAGS);
+ // receive the 2 first messages
+ for (int i = 1; i <= 2; i++)
+ {
+ message = (TextMessage) xaDurSub.receive(1000);
+ if (message == null)
+ {
+ fail("no message received! expected: " + i);
+ }
+ else if (message.getLongProperty(_sequenceNumberPropertyName) != i)
+ {
+ fail("wrong sequence number: " + message.getLongProperty(_sequenceNumberPropertyName));
+ }
+ }
+ _xaResource.end(xid1, XAResource.TMSUSPEND);
+ //----- start xid2
+ _xaResource.start(xid2, XAResource.TMNOFLAGS);
+ // receive the 2 first messages
+ for (int i = 3; i <= 4; i++)
+ {
+ message = (TextMessage) xaDurSub.receive(1000);
+ if (message == null)
+ {
+ fail("no message received! expected: " + i);
+ }
+ else if (message.getLongProperty(_sequenceNumberPropertyName) != i)
+ {
+ fail("wrong sequence number: " + message.getLongProperty(_sequenceNumberPropertyName));
+ }
+ }
+ _xaResource.end(xid2, XAResource.TMSUSPEND);
+ //----- start xid3
+ _xaResource.start(xid3, XAResource.TMNOFLAGS);
+ // receive the 2 first messages
+ for (int i = 5; i <= 6; i++)
+ {
+ message = (TextMessage) xaDurSub.receive(1000);
+ if (message == null)
+ {
+ fail("no message received! expected: " + i);
+ }
+ else if (message.getLongProperty(_sequenceNumberPropertyName) != i)
+ {
+ fail("wrong sequence number: " + message.getLongProperty(_sequenceNumberPropertyName));
+ }
+ }
+ _xaResource.end(xid3, XAResource.TMSUCCESS);
+ }
+ catch (Exception e)
+ {
+ _logger.error("Exception thrown when consumming 6 first messages", e);
+ fail("Exception thrown when consumming 6 first messages: " + e.getMessage());
+ }
+ try
+ {
+ _logger.debug("abort tx2, we now expect to receive messages 3, 4 and 7");
+ _xaResource.start(xid2, XAResource.TMRESUME);
+ _xaResource.end(xid2, XAResource.TMSUCCESS);
+ _xaResource.prepare(xid2);
+ _xaResource.rollback(xid2);
+ // receive 3 message within tx1: 3, 4 and 7
+ _xaResource.start(xid1, XAResource.TMRESUME);
+ _logger.debug(" 3, 4 and 7");
+ for (int i = 1; i <= 3; i++)
+ {
+ message = (TextMessage) xaDurSub.receive(1000);
+ if (message == null)
+ {
+ fail("no message received! expected: " + 3);
+ }
+ else if (message.getLongProperty(_sequenceNumberPropertyName) <= 2 || 5 == message
+ .getLongProperty(_sequenceNumberPropertyName) || message
+ .getLongProperty(_sequenceNumberPropertyName) == 6)
+ {
+ fail("wrong sequence number: " + message
+ .getLongProperty(_sequenceNumberPropertyName));
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ _logger.error("Exception thrown when consumming message: 3, 4 and 7", e);
+ fail("Exception thrown when consumming message: 3, 4 and 7: " + e.getMessage());
+ }
+
+ try
+ {
+ _xaResource.end(xid1, XAResource.TMSUCCESS);
+ _logger.debug(" commit tx3");
+ _xaResource.commit(xid3, true);
+ _logger.debug("abort tx1");
+ _xaResource.prepare(xid1);
+ _xaResource.rollback(xid1);
+ }
+ catch (XAException e)
+ {
+ _logger.error("XAException thrown when committing tx3 or aborting tx1", e);
+ fail("XAException thrown when committing tx3 or aborting tx1: " + e.getMessage());
+ }
+
+ try
+ {
+ // consume messages 1 - 4 + 7
+ //----- start xid1
+ _xaResource.start(xid4, XAResource.TMNOFLAGS);
+ for (int i = 1; i <= 5; i++)
+ {
+
+ message = (TextMessage) xaDurSub.receive(1000);
+
+ if(message != null)
+ {
+ _logger.debug(" received message: " + message.getLongProperty(_sequenceNumberPropertyName));
+ }
+
+ if (message == null)
+ {
+ fail("no message received! expected: " + i);
+ }
+ else if (message.getLongProperty(_sequenceNumberPropertyName) == 5 || message
+ .getLongProperty(_sequenceNumberPropertyName) == 6)
+ {
+ fail("wrong sequence number: " + message.getLongProperty(_sequenceNumberPropertyName));
+ }
+ }
+ _xaResource.end(xid4, XAResource.TMSUCCESS);
+ _xaResource.prepare(xid4);
+ _xaResource.commit(xid4, false);
+ }
+ catch (Exception e)
+ {
+ _logger.error("Exception thrown in last phase", e);
+ fail("Exception thrown in last phase: " + e.getMessage());
+ }
+ // now the topic should be empty!!
+ try
+ {
+ // start xid6
+ _xaResource.start(xid6, XAResource.TMNOFLAGS);
+ // should now be empty
+ message = (TextMessage) xaDurSub.receive(1000);
+ if (message != null)
+ {
+ fail("An unexpected message was received " + message
+ .getLongProperty(_sequenceNumberPropertyName));
+ }
+ // commit xid6
+ _xaResource.end(xid6, XAResource.TMSUCCESS);
+ _xaResource.commit(xid6, true);
+ }
+ catch (Exception e)
+ {
+ _logger.error("Exception when working with xid6", e);
+ fail("Exception when working with xid6: " + e.getMessage());
+ }
+ }
+ catch (Exception e)
+ {
+ _logger.error("problem when creating dur sub", e);
+ fail("problem when creating dur sub: " + e.getMessage());
+ }
+ finally
+ {
+ try
+ {
+ _session.unsubscribe(durSubName);
+ }
+ catch (JMSException e)
+ {
+ _logger.error("problem when unsubscribing dur sub", e);
+ fail("problem when unsubscribing dur sub: " + e.getMessage());
+ }
+ }
+ }
+ }
+
+ /**
+ * strategy: create a XA durable subscriber dusSub, produce 10 messages with the standard session,
+ * consume 2 messages respectively with tx1, tx2 and tx3
+ * prepare xid2 and xid3
+ * crash the server
+ * Redo the job for xid1 that has been aborted by server crash
+ * abort tx2, we now expect to receive messages 3 and 4 first! Receive 3 messages within tx1 i.e. 34 and 7!
+ * commit tx3
+ * abort tx1: we now expect that only messages 5 and 6 are definitly consumed!
+ * start tx4 and consume messages 1 - 4
+ * start tx5 and consume messages 7 - 10
+ * abort tx4
+ * consume messages 1-4 with tx5
+ * commit tx5
+ * Now the topic should be empty!
+ */
+ public void testMultiMessagesDurSubCrash()
+ {
+ if (!isBroker08())
+ {
+ Xid xid1 = getNewXid();
+ Xid xid2 = getNewXid();
+ Xid xid3 = getNewXid();
+ Xid xid4 = getNewXid();
+ Xid xid5 = getNewXid();
+ Xid xid6 = getNewXid();
+ String durSubName = "xaSubDurable";
+ TextMessage message;
+ try
+ {
+ TopicSubscriber xaDurSub = _session.createDurableSubscriber(_topic, durSubName);
+ try
+ {
+ Session txSession = _nonXASession;
+ MessageProducer txProducer = txSession.createProducer(_topic);
+ // produce 10 persistent messages
+ txProducer.setDeliveryMode(DeliveryMode.PERSISTENT);
+ _topicConnection.start();
+ for (int i = 1; i <= 10; i++)
+ {
+ _message.setLongProperty(_sequenceNumberPropertyName, i);
+ txProducer.send(_message);
+ }
+ // commit txSession
+ txSession.commit();
+ }
+ catch (JMSException e)
+ {
+ _logger.error("Exception thrown when producing messages", e);
+ fail("Exception thrown when producing messages: " + e.getMessage());
+ }
+ try
+ {
+ // consume 2 messages respectively with tx1, tx2 and tx3
+ //----- start xid1
+ _xaResource.start(xid1, XAResource.TMNOFLAGS);
+ // receive the 2 first messages
+ for (int i = 1; i <= 2; i++)
+ {
+ message = (TextMessage) xaDurSub.receive(1000);
+ if (message == null)
+ {
+ fail("no message received! expected: " + i);
+ }
+ else if (message.getLongProperty(_sequenceNumberPropertyName) != i)
+ {
+ fail("wrong sequence number: " + message.getLongProperty(_sequenceNumberPropertyName));
+ }
+ }
+ _xaResource.end(xid1, XAResource.TMSUCCESS);
+ //----- start xid2
+ _xaResource.start(xid2, XAResource.TMNOFLAGS);
+ // receive the 2 first messages
+ for (int i = 3; i <= 4; i++)
+ {
+ message = (TextMessage) xaDurSub.receive(1000);
+ if (message == null)
+ {
+ fail("no message received! expected: " + i);
+ }
+ else if (message.getLongProperty(_sequenceNumberPropertyName) != i)
+ {
+ fail("wrong sequence number: " + message.getLongProperty(_sequenceNumberPropertyName));
+ }
+ }
+ _xaResource.end(xid2, XAResource.TMSUCCESS);
+ //----- start xid3
+ _xaResource.start(xid3, XAResource.TMNOFLAGS);
+ // receive the 2 first messages
+ for (int i = 5; i <= 6; i++)
+ {
+ message = (TextMessage) xaDurSub.receive(1000);
+ if (message == null)
+ {
+ fail("no message received! expected: " + i);
+ }
+ else if (message.getLongProperty(_sequenceNumberPropertyName) != i)
+ {
+ fail("wrong sequence number: " + message.getLongProperty(_sequenceNumberPropertyName));
+ }
+ }
+ _xaResource.end(xid3, XAResource.TMSUCCESS);
+ // prepare tx2 and tx3
+
+ _xaResource.prepare(xid2);
+ _xaResource.prepare(xid3);
+ }
+ catch (Exception e)
+ {
+ _logger.error("Exception thrown when consumming 6 first messages", e);
+ fail("Exception thrown when consumming 6 first messages: " + e.getMessage());
+ }
+ /////// stop the broker now !!
+ try
+ {
+ restartBroker();
+ init();
+ }
+ catch (Exception e)
+ {
+ fail("Exception when stopping and restarting the server");
+ }
+ // get the list of in doubt transactions
+ try
+ {
+ _topicConnection.start();
+ // reconnect to dursub!
+ xaDurSub = _session.createDurableSubscriber(_topic, durSubName);
+ Xid[] inDoubt = _xaResource.recover(XAResource.TMSTARTRSCAN);
+ if (inDoubt == null)
+ {
+ fail("the array of in doubt transactions should not be null ");
+ }
+ // At that point we expect only two indoubt transactions:
+ if (inDoubt.length != 2)
+ {
+ fail("in doubt transaction size is diffenrent than 2, there are " + inDoubt.length + "in doubt transactions");
+ }
+ }
+ catch (XAException e)
+ {
+ _logger.error("exception thrown when recovering transactions", e);
+ fail("exception thrown when recovering transactions " + e.getMessage());
+ }
+ try
+ {
+ // xid1 has been aborted redo the job!
+ // consume 2 messages with tx1
+ //----- start xid1
+ _xaResource.start(xid1, XAResource.TMNOFLAGS);
+ // receive the 2 first messages
+ for (int i = 1; i <= 2; i++)
+ {
+ message = (TextMessage) xaDurSub.receive(1000);
+ if (message == null)
+ {
+ fail("no message received! expected: " + i);
+ }
+ else if (message.getLongProperty(_sequenceNumberPropertyName) != i)
+ {
+ fail("wrong sequence number: " + message.getLongProperty(_sequenceNumberPropertyName));
+ }
+ }
+ _xaResource.end(xid1, XAResource.TMSUSPEND);
+ // abort tx2, we now expect to receive messages 3 and 4 first!
+ _xaResource.rollback(xid2);
+
+ // receive 3 message within tx1: 3, 4 and 7
+ _xaResource.start(xid1, XAResource.TMRESUME);
+ // receive messages 3, 4 and 7
+ Set<Long> expected = new HashSet<Long>();
+ expected.add(3L);
+ expected.add(4L);
+ expected.add(7L);
+ message = (TextMessage) xaDurSub.receive(1000);
+ if (message == null)
+ {
+ fail("no message received! expected one of: " + expected);
+ }
+ else if (!expected.remove(message.getLongProperty(_sequenceNumberPropertyName)))
+ {
+ fail("wrong sequence number: " + message
+ .getLongProperty(_sequenceNumberPropertyName) + " expected one from " + expected);
+ }
+ message = (TextMessage) xaDurSub.receive(1000);
+ if (message == null)
+ {
+ fail("no message received! expected one of: " + expected);
+ }
+ else if (!expected.remove(message.getLongProperty(_sequenceNumberPropertyName)))
+ {
+
+ fail("wrong sequence number: " + message
+ .getLongProperty(_sequenceNumberPropertyName) + " expected one from " + expected);
+ }
+ message = (TextMessage) xaDurSub.receive(1000);
+ if (message == null)
+ {
+ fail("no message received! expected one of: " + expected);
+ }
+ else if (!expected.remove(message.getLongProperty(_sequenceNumberPropertyName)))
+ {
+ fail("wrong sequence number: " + message
+ .getLongProperty(_sequenceNumberPropertyName) + " expected one from " + expected);
+ }
+ }
+ catch (Exception e)
+ {
+ _logger.error("Exception thrown when consumming message: 3, 4 and 7", e);
+ fail("Exception thrown when consumming message: 3, 4 and 7: " + e.getMessage());
+ }
+
+ try
+ {
+ _xaResource.end(xid1, XAResource.TMSUSPEND);
+ // commit tx3
+ _xaResource.commit(xid3, false);
+ // abort tx1
+ _xaResource.prepare(xid1);
+ _xaResource.rollback(xid1);
+ }
+ catch (XAException e)
+ {
+ _logger.error("XAException thrown when committing tx3 or aborting tx1", e);
+ fail("XAException thrown when committing tx3 or aborting tx1: " + e.getMessage());
+ }
+
+ try
+ {
+ // consume messages: could be any from (1 - 4, 7-10)
+ //----- start xid4
+ Set<Long> expected = new HashSet<Long>();
+ Set<Long> xid4msgs = new HashSet<Long>();
+ for(long l = 1; l <= 4l; l++)
+ {
+ expected.add(l);
+ }
+ for(long l = 7; l <= 10l; l++)
+ {
+ expected.add(l);
+ }
+ _xaResource.start(xid4, XAResource.TMNOFLAGS);
+ for (int i = 1; i <= 4; i++)
+ {
+ message = (TextMessage) xaDurSub.receive(1000);
+ if (message == null)
+ {
+ fail("no message received! expected: " + i);
+ }
+
+ long seqNo = message.getLongProperty(_sequenceNumberPropertyName);
+ xid4msgs.add(seqNo);
+
+ if (!expected.remove(seqNo))
+ {
+ fail("wrong sequence number: " + seqNo +
+ " expected one from " + expected);
+ }
+ }
+ _xaResource.end(xid4, XAResource.TMSUSPEND);
+ // consume messages 8 - 10
+ _xaResource.start(xid5, XAResource.TMNOFLAGS);
+ for (int i = 7; i <= 10; i++)
+ {
+ message = (TextMessage) xaDurSub.receive(1000);
+ if (message == null)
+ {
+ fail("no message received! expected: " + i);
+ }
+ else if (!expected.remove(message.getLongProperty(_sequenceNumberPropertyName)))
+ {
+ fail("wrong sequence number: " + message.getLongProperty(_sequenceNumberPropertyName)
+ + " expected one from " + expected);
+ }
+ }
+ _xaResource.end(xid5, XAResource.TMSUSPEND);
+ // abort tx4
+ _xaResource.prepare(xid4);
+ _xaResource.rollback(xid4);
+ expected.addAll(xid4msgs);
+ // consume messages 1-4 with tx5
+ _xaResource.start(xid5, XAResource.TMRESUME);
+ for (int i = 1; i <= 4; i++)
+ {
+ message = (TextMessage) xaDurSub.receive(1000);
+ if (message == null)
+ {
+ fail("no message received! expected: " + i);
+ }
+ else if (!expected.remove(message.getLongProperty(_sequenceNumberPropertyName)))
+ {
+ fail("wrong sequence number: " + message.getLongProperty(_sequenceNumberPropertyName)
+ + " expected one from " + expected);
+ }
+ }
+ _xaResource.end(xid5, XAResource.TMSUSPEND);
+ // commit tx5
+
+ _xaResource.prepare(xid5);
+ _xaResource.commit(xid5, false);
+ }
+ catch (Exception e)
+ {
+ _logger.error("Exception thrown in last phase", e);
+ fail("Exception thrown in last phase: " + e.getMessage());
+ }
+ // now the topic should be empty!!
+ try
+ {
+ // start xid6
+ _xaResource.start(xid6, XAResource.TMNOFLAGS);
+ // should now be empty
+ message = (TextMessage) xaDurSub.receive(1000);
+ if (message != null)
+ {
+ fail("An unexpected message was received " + message
+ .getLongProperty(_sequenceNumberPropertyName));
+ }
+ // commit xid6
+ _xaResource.end(xid6, XAResource.TMSUSPEND);
+ _xaResource.commit(xid6, true);
+ }
+ catch (Exception e)
+ {
+ _logger.error("Exception when working with xid6", e);
+ fail("Exception when working with xid6: " + e.getMessage());
+ }
+ }
+ catch (Exception e)
+ {
+ _logger.error("problem when creating dur sub", e);
+ fail("problem when creating dur sub: " + e.getMessage());
+ }
+ finally
+ {
+ try
+ {
+ _session.unsubscribe(durSubName);
+ }
+ catch (JMSException e)
+ {
+ _logger.error("problem when unsubscribing dur sub", e);
+ fail("problem when unsubscribing dur sub: " + e.getMessage());
+ }
+ }
+ }
+ }
+
+
+ /**
+ * strategy: Produce a message within Tx1 and commit tx1. a durable subscriber then receives that message within tx2
+ * that is then prepared.
+ * Shutdown the server and get the list of in doubt transactions:
+ * we expect tx2, Tx2 is aborted and the message consumed within tx3 that is committed we then check that the topic is empty.
+ */
+ public void testDurSubCrash()
+ {
+ if (!isBroker08())
+ {
+ Xid xid1 = getNewXid();
+ Xid xid2 = getNewXid();
+ Xid xid3 = getNewXid();
+ Xid xid4 = getNewXid();
+ String durSubName = "xaSubDurable";
+ try
+ {
+ TopicSubscriber xaDurSub = _session.createDurableSubscriber(_topic, durSubName);
+ try
+ {
+ _topicConnection.start();
+ //----- start xid1
+ _xaResource.start(xid1, XAResource.TMNOFLAGS);
+ // start the connection
+ _topicConnection.start();
+ // produce a message with sequence number 1
+ _message.setLongProperty(_sequenceNumberPropertyName, 1);
+ _producer.send(_message);
+ // commit
+ _xaResource.end(xid1, XAResource.TMSUCCESS);
+ if (_xaResource.prepare(xid1) != XAResource.XA_OK)
+ {
+ fail("Problem when preparing tx1 ");
+ }
+ _xaResource.commit(xid1, false);
+ }
+ catch (Exception e)
+ {
+ _logger.error("Exception when working with xid1", e);
+ fail("Exception when working with xid1: " + e.getMessage());
+ }
+ try
+ {
+ // start xid2
+ _xaResource.start(xid2, XAResource.TMNOFLAGS);
+ // receive the previously produced message
+ TextMessage message = (TextMessage) xaDurSub.receive(1000);
+ if (message == null)
+ {
+ fail("no message received ");
+ }
+ else if (message.getLongProperty(_sequenceNumberPropertyName) != 1)
+ {
+ fail("wrong sequence number: " + message.getLongProperty(_sequenceNumberPropertyName));
+ }
+ // prepare xid2
+ _xaResource.end(xid2, XAResource.TMSUCCESS);
+ if (_xaResource.prepare(xid2) != XAResource.XA_OK)
+ {
+ fail("Problem when preparing tx2 ");
+ }
+ }
+ catch (Exception e)
+ {
+ _logger.error("Exception when working with xid2", e);
+ fail("Exception when working with xid2: " + e.getMessage());
+ }
+
+ /////// stop the server now !!
+ try
+ {
+ restartBroker();
+ init();
+ }
+ catch (Exception e)
+ {
+ fail("Exception when stopping and restarting the server");
+ }
+
+ // get the list of in doubt transactions
+ try
+ {
+ _topicConnection.start();
+ // reconnect to dursub!
+ xaDurSub = _session.createDurableSubscriber(_topic, durSubName);
+ Xid[] inDoubt = _xaResource.recover(XAResource.TMSTARTRSCAN);
+ if (inDoubt == null)
+ {
+ fail("the array of in doubt transactions should not be null ");
+ }
+ // At that point we expect only two indoubt transactions:
+ if (inDoubt.length != 1)
+ {
+ fail("in doubt transaction size is diffenrent than 2, there are " + inDoubt.length + "in doubt transactions");
+ }
+
+ // commit them
+ for (Xid anInDoubt : inDoubt)
+ {
+ if (anInDoubt.equals(xid2))
+ {
+ _logger.info("aborting xid2 ");
+ try
+ {
+ _xaResource.rollback(anInDoubt);
+ }
+ catch (Exception e)
+ {
+ _logger.error("exception when aborting xid2 ", e);
+ fail("exception when aborting xid2 ");
+ }
+ }
+ else
+ {
+ _logger.info("XID2 is not in doubt ");
+ }
+ }
+ }
+ catch (XAException e)
+ {
+ _logger.error("exception thrown when recovering transactions", e);
+ fail("exception thrown when recovering transactions " + e.getMessage());
+ }
+
+ try
+ {
+ // start xid3
+ _xaResource.start(xid3, XAResource.TMNOFLAGS);
+ // receive the previously produced message and aborted
+ TextMessage message = (TextMessage) xaDurSub.receive(1000);
+ if (message == null)
+ {
+ fail("no message received ");
+ }
+ else if (message.getLongProperty(_sequenceNumberPropertyName) != 1)
+ {
+ fail("wrong sequence number: " + message.getLongProperty(_sequenceNumberPropertyName));
+ }
+ // commit xid3
+ _xaResource.end(xid3, XAResource.TMSUCCESS);
+ if (_xaResource.prepare(xid3) != XAResource.XA_OK)
+ {
+ fail("Problem when preparing tx3 ");
+ }
+ _xaResource.commit(xid3, false);
+ }
+ catch (Exception e)
+ {
+ _logger.error("Exception when working with xid3", e);
+ fail("Exception when working with xid3: " + e.getMessage());
+ }
+ try
+ {
+ // start xid4
+ _xaResource.start(xid4, XAResource.TMNOFLAGS);
+ // should now be empty
+ TextMessage message = (TextMessage) xaDurSub.receive(1000);
+ if (message != null)
+ {
+ fail("An unexpected message was received " + message
+ .getLongProperty(_sequenceNumberPropertyName));
+ }
+ // commit xid4
+ _xaResource.end(xid4, XAResource.TMSUCCESS);
+ _xaResource.commit(xid4, true);
+ }
+ catch (Exception e)
+ {
+ _logger.error("Exception when working with xid4", e);
+ fail("Exception when working with xid4: " + e.getMessage());
+ }
+ }
+ catch (Exception e)
+ {
+ _logger.error("problem when creating dur sub", e);
+ fail("problem when creating dur sub: " + e.getMessage());
+ }
+ finally
+ {
+ try
+ {
+ _session.unsubscribe(durSubName);
+ }
+ catch (JMSException e)
+ {
+ _logger.error("problem when unsubscribing dur sub", e);
+ fail("problem when unsubscribing dur sub: " + e.getMessage());
+ }
+ }
+ }
+ }
+
+ /**
+ * strategy: Produce a message within Tx1 and prepare tx1. Shutdown the server and get the list of indoubt transactions:
+ * we expect tx1, Tx1 is committed so we expect the test topic not to be empty!
+ */
+ public void testRecover()
+ {
+ if (!isBroker08())
+ {
+ Xid xid1 = getNewXid();
+ String durSubName = "test1";
+ try
+ {
+ // create a dummy durable subscriber to be sure that messages are persisted!
+ _nonXASession.createDurableSubscriber(_topic, durSubName);
+ // start the xaResource for xid1
+ try
+ {
+ _xaResource.start(xid1, XAResource.TMNOFLAGS);
+ }
+ catch (XAException e)
+ {
+ fail("cannot start the transaction with xid1: " + e.getMessage());
+ }
+ try
+ {
+ // start the connection
+ _topicConnection.start();
+ // produce a message with sequence number 1
+ _message.setLongProperty(_sequenceNumberPropertyName, 1);
+ _producer.send(_message);
+ }
+ catch (JMSException e)
+ {
+ fail(" cannot send persistent message: " + e.getMessage());
+ }
+ // suspend the transaction
+ try
+ {
+ _xaResource.end(xid1, XAResource.TMSUCCESS);
+ }
+ catch (XAException e)
+ {
+ fail("Cannot end the transaction with xid1: " + e.getMessage());
+ }
+ // prepare the transaction with xid1
+ try
+ {
+ _xaResource.prepare(xid1);
+ }
+ catch (XAException e)
+ {
+ fail("Exception when preparing xid1: " + e.getMessage());
+ }
+
+ /////// stop the server now !!
+ try
+ {
+ restartBroker();
+ init();
+ }
+ catch (Exception e)
+ {
+ fail("Exception when stopping and restarting the server");
+ }
+
+ try
+ {
+ MessageConsumer nonXAConsumer = _nonXASession.createDurableSubscriber(_topic, durSubName);
+ _topicConnection.start();
+ // get the list of in doubt transactions
+ try
+ {
+ Xid[] inDoubt = _xaResource.recover(XAResource.TMSTARTRSCAN);
+ if (inDoubt == null)
+ {
+ fail("the array of in doubt transactions should not be null ");
+ }
+ // At that point we expect only two indoubt transactions:
+ if (inDoubt.length != 1)
+ {
+ fail("in doubt transaction size is diffenrent thatn 2, there are " + inDoubt.length + "in doubt transactions");
+ }
+ // commit them
+ for (Xid anInDoubt : inDoubt)
+ {
+ if (anInDoubt.equals(xid1))
+ {
+ _logger.debug("committing xid1 ");
+ try
+ {
+ _xaResource.commit(anInDoubt, false);
+ }
+ catch (Exception e)
+ {
+ _logger.error("PB when aborted xid1");
+ fail("exception when committing xid1 ");
+ }
+ }
+ else
+ {
+ _logger.debug("XID1 is not in doubt ");
+ }
+ }
+ }
+ catch (XAException e)
+ {
+ _logger.error("exception thrown when recovering transactions ", e);
+ fail("exception thrown when recovering transactions " + e.getMessage());
+ }
+ _logger.debug("the topic should not be empty");
+ TextMessage message1 = (TextMessage) nonXAConsumer.receive(1000);
+ if (message1 == null)
+ {
+ fail("The topic is empty! ");
+ }
+ }
+ catch (Exception e)
+ {
+ _logger.error("Exception thrown when testin that queue test is empty", e);
+ fail("Exception thrown when testin that queue test is empty: " + e.getMessage());
+ }
+ }
+ catch (JMSException e)
+ {
+ _logger.error("cannot create dummy durable subscriber", e);
+ fail("cannot create dummy durable subscriber: " + e.getMessage());
+ }
+ finally
+ {
+ try
+ {
+ // unsubscribe the dummy durable subscriber
+ TopicSession nonXASession = _nonXASession;
+ nonXASession.unsubscribe(durSubName);
+ }
+ catch (JMSException e)
+ {
+ fail("cannot unsubscribe durable subscriber: " + e.getMessage());
+ }
+ }
+ }
+ }
+
+ /**
+ * strategy:
+ * create a standard durable subscriber
+ * produce 3 messages
+ * consume the first message with that durable subscriber
+ * close the standard session that deactivates the durable subscriber
+ * migrate the durable subscriber to an xa one
+ * consume the second message with that xa durable subscriber
+ * close the xa session that deactivates the durable subscriber
+ * reconnect to the durable subscriber with a standard session
+ * consume the two remaining messages and check that the topic is empty!
+ */
+ public void testMigrateDurableSubscriber()
+ {
+ if (!isBroker08())
+ {
+ Xid xid1 = getNewXid();
+ Xid xid2 = getNewXid();
+ String durSubName = "DurableSubscriberMigrate";
+ try
+ {
+ Session stSession = _nonXASession;
+ MessageProducer producer = stSession.createProducer(_topic);
+ _logger.debug("Create a standard durable subscriber!");
+ TopicSubscriber durSub = stSession.createDurableSubscriber(_topic, durSubName);
+ TopicSubscriber durSub1 = stSession.createDurableSubscriber(_topic, durSubName + "_second");
+ TextMessage message;
+ producer.setDeliveryMode(DeliveryMode.PERSISTENT);
+ _topicConnection.start();
+ _logger.debug("produce 3 messages");
+ for (int i = 1; i <= 3; i++)
+ {
+ _message.setLongProperty(_sequenceNumberPropertyName, i);
+ //producer.send( _message );
+ producer.send(_message, DeliveryMode.PERSISTENT, 9 - i, 0);
+ stSession.commit();
+ }
+ _logger.debug("consume the first message with that durable subscriber");
+ message = (TextMessage) durSub.receive(1000);
+ if (message == null)
+ {
+ fail("no message received ");
+ }
+ else if (message.getLongProperty(_sequenceNumberPropertyName) != 1)
+ {
+ fail("wrong sequence number: " + message.getLongProperty(_sequenceNumberPropertyName));
+ }
+ // commit the standard session
+ stSession.commit();
+ _logger.debug("first message consumed ");
+ // close the session that deactivates the durable subscriber
+ stSession.close();
+ _logger.debug("migrate the durable subscriber to an xa one");
+ _xaResource.start(xid1, XAResource.TMNOFLAGS);
+ durSub = _session.createDurableSubscriber(_topic, durSubName);
+ _logger.debug(" consume the second message with that xa durable subscriber and abort it");
+ message = (TextMessage) durSub.receive(1000);
+ if (message == null)
+ {
+ fail("no message received ");
+ }
+ else if (message.getLongProperty(_sequenceNumberPropertyName) != 2)
+ {
+ _logger.info("wrong sequence number, 2 expected, received: " + message
+ .getLongProperty(_sequenceNumberPropertyName));
+ }
+ _xaResource.end(xid1, XAResource.TMSUCCESS);
+ _xaResource.prepare(xid1);
+ _xaResource.rollback(xid1);
+ _logger.debug("close the session that deactivates the durable subscriber");
+ _session.close();
+ _logger.debug("create a new standard session");
+ stSession = _topicConnection.createTopicSession(true, 1);
+ _logger.debug("reconnect to the durable subscriber");
+ durSub = stSession.createDurableSubscriber(_topic, durSubName);
+ durSub1 = stSession.createDurableSubscriber(_topic, durSubName + "_second");
+ _logger.debug("Reconnected to durablse subscribers");
+ _logger.debug(" consume the 2 remaining messages");
+ message = (TextMessage) durSub.receive(1000);
+ if (message == null)
+ {
+ fail("no message received ");
+ }
+ else if (message.getLongProperty(_sequenceNumberPropertyName) != 2)
+ {
+ _logger.info("wrong sequence number, 2 expected, received: " + message
+ .getLongProperty(_sequenceNumberPropertyName));
+ }
+ // consume the third message with that xa durable subscriber
+ message = (TextMessage) durSub.receive(1000);
+ if (message == null)
+ {
+ fail("no message received ");
+ }
+ else if (message.getLongProperty(_sequenceNumberPropertyName) != 3)
+ {
+ _logger.info("wrong sequence number, 3 expected, received: " + message
+ .getLongProperty(_sequenceNumberPropertyName));
+ }
+ stSession.commit();
+ _logger.debug("the topic should be empty now");
+ message = (TextMessage) durSub.receive(1000);
+ if (message != null)
+ {
+ fail("Received unexpected message ");
+ }
+ stSession.commit();
+ _logger.debug(" use dursub1 to receive all the 3 messages");
+ for (int i = 1; i <= 3; i++)
+ {
+ message = (TextMessage) durSub1.receive(1000);
+ if (message == null)
+ {
+ _logger.debug("no message received ");
+ }
+ else if (message.getLongProperty(_sequenceNumberPropertyName) != i)
+ {
+ fail("wrong sequence number, " + i + " expected, received: " + message
+ .getLongProperty(_sequenceNumberPropertyName));
+ }
+ }
+ stSession.commit();
+ // send a non persistent message to check that all persistent messages are deleted
+ producer = stSession.createProducer(_topic);
+ producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
+ producer.send(_message);
+ stSession.commit();
+ message = (TextMessage) durSub.receive(1000);
+ if (message == null)
+ {
+ fail("message not received ");
+ }
+ message = (TextMessage) durSub1.receive(1000);
+ if (message == null)
+ {
+ fail("message not received ");
+ }
+ stSession.commit();
+ stSession.close();
+ _logger.debug(" now create a standard non transacted session and reconnect to the durable xubscriber");
+ TopicConnection stConnection =
+ _topicConnection; //_topicFactory.createTopicConnection("guest", "guest");
+ TopicSession autoAclSession = stConnection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
+ TopicPublisher publisher = autoAclSession.createPublisher(_topic);
+ durSub = autoAclSession.createDurableSubscriber(_topic, durSubName);
+ stConnection.start();
+ // produce 3 persistent messages
+ for (int i = 1; i <= 3; i++)
+ {
+ _message.setLongProperty(_sequenceNumberPropertyName, i);
+ //producer.send( _message );
+ publisher.send(_message, DeliveryMode.PERSISTENT, 9 - i, 0);
+ }
+ _logger.debug(" use dursub to receive all the 3 messages");
+ for (int i = 1; i <= 3; i++)
+ {
+ message = (TextMessage) durSub.receive(1000);
+ if (message == null)
+ {
+ _logger.info("no message received ");
+ }
+ else if (message.getLongProperty(_sequenceNumberPropertyName) != i)
+ {
+ _logger.info("wrong sequence number, " + i + " expected, received: " + message
+ .getLongProperty(_sequenceNumberPropertyName));
+ }
+ }
+
+ _logger.debug("now set a message listener");
+ AtomicBoolean lock = new AtomicBoolean(true);
+ reset();
+ stConnection.stop();
+ durSub.setMessageListener(new TopicListener(1, 3, lock));
+ _logger.debug(" produce 3 persistent messages");
+ for (int i = 1; i <= 3; i++)
+ {
+ _message.setLongProperty(_sequenceNumberPropertyName, i);
+ //producer.send( _message );
+ publisher.send(_message, DeliveryMode.PERSISTENT, 9 - i, 0);
+ }
+ // start the connection
+ stConnection.start();
+ while (lock.get())
+ {
+ synchronized (lock)
+ {
+ lock.wait();
+ }
+ }
+ if (getFailureStatus())
+ {
+ fail("problem with message listener");
+ }
+ stConnection.stop();
+ durSub.setMessageListener(null);
+ _logger.debug(" do the same with an xa session");
+ // produce 3 persistent messages
+ for (int i = 1; i <= 3; i++)
+ {
+ _message.setLongProperty(_sequenceNumberPropertyName, i);
+ //producer.send( _message );
+ publisher.send(_message, DeliveryMode.PERSISTENT, 9 - i, 0);
+ }
+ //stConnection.close();
+ autoAclSession.close();
+ _logger.debug(" migrate the durable subscriber to an xa one");
+ _session = _topicConnection.createXATopicSession();
+ _xaResource = _session.getXAResource();
+ _xaResource.start(xid2, XAResource.TMNOFLAGS);
+ durSub = _session.createDurableSubscriber(_topic, durSubName);
+ lock = new AtomicBoolean();
+ reset();
+ _topicConnection.stop();
+ durSub.setMessageListener(new TopicListener(1, 3, lock));
+ // start the connection
+ _topicConnection.start();
+ while (lock.get())
+ {
+ synchronized (lock)
+ {
+ lock.wait();
+ }
+ }
+ if (getFailureStatus())
+ {
+ fail("problem with XA message listener");
+ }
+ _xaResource.end(xid2, XAResource.TMSUCCESS);
+ _xaResource.commit(xid2, true);
+ _session.close();
+ }
+ catch (Exception e)
+ {
+ _logger.error("Exception thrown", e);
+ fail("Exception thrown: " + e.getMessage());
+ }
+ finally
+ {
+ try
+ {
+ _topicConnection.createXASession().unsubscribe(durSubName);
+ _topicConnection.createXASession().unsubscribe(durSubName + "_second");
+ }
+ catch (JMSException e)
+ {
+ fail("Exception thrown when unsubscribing durable subscriber " + e.getMessage());
+ }
+ }
+ }
+ }
+
+ /** -------------------------------------------------------------------------------------- **/
+ /** ----------------------------- Utility methods --------------------------------------- **/
+ /** -------------------------------------------------------------------------------------- **/
+
+ /**
+ * get a new queue connection
+ *
+ * @return a new queue connection
+ * @throws javax.jms.JMSException If the JMS provider fails to create the queue connection
+ * due to some internal error or in case of authentication failure
+ */
+ private XATopicConnection getNewTopicXAConnection() throws JMSException
+ {
+ return _topicFactory.createXATopicConnection("guest", "guest");
+ }
+
+ public static void failure()
+ {
+ _failure = true;
+ }
+
+ public static void reset()
+ {
+ _failure = false;
+ }
+
+ public static boolean getFailureStatus()
+ {
+ return _failure;
+ }
+
+ private class TopicListener implements MessageListener
+ {
+ private long _counter;
+ private long _end;
+ private final AtomicBoolean _lock;
+
+ public TopicListener(long init, long end, AtomicBoolean lock)
+ {
+ _counter = init;
+ _end = end;
+ _lock = lock;
+ }
+
+ public void onMessage(Message message)
+ {
+ long seq = 0;
+ try
+ {
+ seq = message.getLongProperty(TopicTest._sequenceNumberPropertyName);
+ }
+ catch (JMSException e)
+ {
+ _logger.error("Error getting long property: " + TopicTest._sequenceNumberPropertyName , e);
+ TopicTest.failure();
+ _lock.set(false);
+ synchronized (_lock)
+ {
+ _lock.notifyAll();
+ }
+ }
+ if (seq != _counter)
+ {
+ _logger.info("received message " + seq + " expected " + _counter);
+ TopicTest.failure();
+ _lock.set(false);
+ synchronized (_lock)
+ {
+ _lock.notifyAll();
+ }
+ }
+ _counter++;
+ if (_counter > _end)
+ {
+ _lock.set(false);
+ synchronized (_lock)
+ {
+ _lock.notifyAll();
+ }
+ }
+ }
+ }
+
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/utils/BrokerCommandHelperTest.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/utils/BrokerCommandHelperTest.java
new file mode 100644
index 0000000000..83c2f1e58d
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/utils/BrokerCommandHelperTest.java
@@ -0,0 +1,79 @@
+/* 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.qpid.test.utils;
+
+import static org.mockito.Mockito.*;
+
+import java.io.File;
+
+public class BrokerCommandHelperTest extends QpidTestCase
+{
+ private static final String PATH_TO_QPID_EXECUTABLE = "/path / to (/qpid";
+ private static final String ARGUMENT_WITH_SPACES = " blah / blah /blah";
+ private static final String ARGUMENT_PORT = "-p";
+ private static final String ARGUMENT_PORT_VALUE = "@PORT";
+ private static final String ARGUMENT_STORE_PATH = "-sp";
+ private static final String ARGUMENT_STORE_PATH_VALUE = "@STORE_PATH";
+ private static final String ARGUMENT_STORE_TYPE = "-st";
+ private static final String ARGUMENT_STORE_TYPE_VALUE = "@STORE_TYPE";
+ private static final String ARGUMENT_LOG = "-l";
+ private static final String ARGUMENT_LOG_VALUE = "@LOG_CONFIG_FILE";
+
+ private BrokerCommandHelper _brokerCommandHelper;
+
+ private File _logConfigFile = mock(File.class);
+
+ @Override
+ public void setUp()
+ {
+ when(_logConfigFile.getAbsolutePath()).thenReturn("log Config File");
+ _brokerCommandHelper = new BrokerCommandHelper("\"" + PATH_TO_QPID_EXECUTABLE + "\" " + ARGUMENT_PORT + " "
+ + ARGUMENT_PORT_VALUE + " " + ARGUMENT_STORE_PATH + " " + ARGUMENT_STORE_PATH_VALUE + " " + ARGUMENT_STORE_TYPE
+ + " " + ARGUMENT_STORE_TYPE_VALUE + " " + ARGUMENT_LOG + " " + ARGUMENT_LOG_VALUE + " '" + ARGUMENT_WITH_SPACES
+ + "'");
+ }
+
+ public void testGetBrokerCommand()
+ {
+ String[] brokerCommand = _brokerCommandHelper.getBrokerCommand(1, "path to config file", "json", _logConfigFile);
+
+ String[] expected = { PATH_TO_QPID_EXECUTABLE, ARGUMENT_PORT, "1", ARGUMENT_STORE_PATH, "path to config file",
+ ARGUMENT_STORE_TYPE, "json", ARGUMENT_LOG, "\"log Config File\"", ARGUMENT_WITH_SPACES };
+ assertEquals("Unexpected broker command", expected.length, brokerCommand.length);
+ for (int i = 0; i < expected.length; i++)
+ {
+ assertEquals("Unexpected command part value at " + i,expected[i], brokerCommand[i] );
+ }
+ }
+
+ public void testRemoveBrokerCommandLog4JFile()
+ {
+ _brokerCommandHelper.removeBrokerCommandLog4JFile();
+ String[] brokerCommand = _brokerCommandHelper.getBrokerCommand(1, "configFile", "json", _logConfigFile);
+
+ String[] expected = { PATH_TO_QPID_EXECUTABLE, ARGUMENT_PORT, "1", ARGUMENT_STORE_PATH, "configFile",
+ ARGUMENT_STORE_TYPE, "json", ARGUMENT_WITH_SPACES };
+
+ assertEquals("Unexpected broker command", expected.length, brokerCommand.length);
+ for (int i = 0; i < expected.length; i++)
+ {
+ assertEquals("Unexpected command part value at " + i,expected[i], brokerCommand[i] );
+ }
+ }
+
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/utils/ConversationFactory.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/utils/ConversationFactory.java
new file mode 100644
index 0000000000..3a9354d822
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/utils/ConversationFactory.java
@@ -0,0 +1,484 @@
+/*
+ *
+ * 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.qpid.test.utils;
+
+import org.apache.log4j.Logger;
+
+import javax.jms.Connection;
+import javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageListener;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * A conversation helper, uses a message correlation id pattern to match up sent and received messages as a conversation
+ * over JMS messaging. Incoming message traffic is divided up by correlation id. Each id has a queue (behaviour dependant
+ * on the queue implementation). Clients of this de-multiplexer can wait on messages, defined by message correlation ids.
+ *
+ * <p/>One use of this is as a conversation synchronizer where multiple threads are carrying out conversations over a
+ * multiplexed messaging route. This can be usefull, as JMS sessions are not multi-threaded. Setting up the conversation
+ * with synchronous queues will allow these threads to be written in a synchronous style, but with their execution order
+ * governed by the asynchronous message flow. For example, something like the following code could run a multi-threaded
+ * conversation (the conversation methods can be called many times in parallel):
+ *
+ * <p/><pre>
+ * class Initiator
+ * {
+ * ConversationHelper conversation = new ConversationHelper(connection, null,
+ * java.util.concurrent.LinkedBlockingQueue.class);
+ *
+ * initiateConversation()
+ * {
+ * try {
+ * // Exchange greetings.
+ * conversation.send(sendDestination, conversation.getSession().createTextMessage("Hello."));
+ * Message greeting = conversation.receive();
+ *
+ * // Exchange goodbyes.
+ * conversation.send(conversation.getSession().createTextMessage("Goodbye."));
+ * Message goodbye = conversation.receive();
+ * } finally {
+ * conversation.end();
+ * }
+ * }
+ * }
+ *
+ * class Responder
+ * {
+ * ConversationHelper conversation = new ConversationHelper(connection, receiveDestination,
+ * java.util.concurrent.LinkedBlockingQueue.class);
+ *
+ * respondToConversation()
+ * {
+ * try {
+ * // Exchange greetings.
+ * Message greeting = conversation.receive();
+ * conversation.send(conversation.getSession().createTextMessage("Hello."));
+ *
+ * // Exchange goodbyes.
+ * Message goodbye = conversation.receive();
+ * conversation.send(conversation.getSession().createTextMessage("Goodbye."));
+ * } finally {
+ * conversation.end();
+ * }
+ * }
+ * }
+ * </pre>
+ *
+ * <p/>Conversation correlation id's are generated on a per thread basis.
+ *
+ * <p/>The same controlSession is shared amongst all conversations. Calls to send are therefore synchronized because JMS
+ * sessions are not multi-threaded.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Associate messages to an ongoing conversation using correlation ids.
+ * <tr><td> Auto manage sessions for conversations.
+ * <tr><td> Store messages not in a conversation in dead letter box.
+ * </table>
+ */
+public class ConversationFactory
+{
+ /** Used for debugging. */
+ private static final Logger log = Logger.getLogger(ConversationFactory.class);
+
+ /** Holds a map from correlation id's to queues. */
+ private Map<Long, BlockingQueue<Message>> idsToQueues = new HashMap<Long, BlockingQueue<Message>>();
+
+ /** Holds the connection over which the conversation is conducted. */
+ private Connection connection;
+
+ /** Holds the controlSession over which the conversation is conduxted. */
+ private Session session;
+
+ /** The message consumer for incoming messages. */
+ private MessageConsumer consumer;
+
+ /** The message producer for outgoing messages. */
+ private MessageProducer producer;
+
+ /** The well-known or temporary destination to receive replies on. */
+ private Destination receiveDestination;
+
+ /** Holds the queue implementation class for the reply queue. */
+ private Class<? extends BlockingQueue> queueClass;
+
+ /** Used to hold any replies that are received outside of the context of a conversation. */
+ private BlockingQueue<Message> deadLetterBox = new LinkedBlockingQueue<Message>();
+
+ /* Used to hold conversation state on a per thread basis. */
+ /*
+ ThreadLocal<Conversation> threadLocals =
+ new ThreadLocal<Conversation>()
+ {
+ protected Conversation initialValue()
+ {
+ Conversation settings = new Conversation();
+ settings.conversationId = conversationIdGenerator.getAndIncrement();
+
+ return settings;
+ }
+ };
+ */
+
+ /** Generates new coversation id's as needed. */
+ private AtomicLong conversationIdGenerator = new AtomicLong();
+
+ /**
+ * Creates a conversation helper on the specified connection with the default sending destination, and listening
+ * to the specified receiving destination.
+ *
+ * @param connection The connection to build the conversation helper on.
+ * @param receiveDestination The destination to listen to for incoming messages. This may be null to use a temporary
+ * queue.
+ * @param queueClass The queue implementation class.
+ *
+ * @throws JMSException All underlying JMSExceptions are allowed to fall through.
+ */
+ public ConversationFactory(Connection connection, Destination receiveDestination,
+ Class<? extends BlockingQueue> queueClass) throws JMSException
+ {
+ log.debug("public ConversationFactory(Connection connection, Destination receiveDestination = " + receiveDestination
+ + ", Class<? extends BlockingQueue> queueClass = " + queueClass + "): called");
+
+ this.connection = connection;
+ this.queueClass = queueClass;
+
+ session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ // Check if a well-known receive destination has been provided, or use a temporary queue if not.
+ this.receiveDestination = (receiveDestination != null) ? receiveDestination : session.createTemporaryQueue();
+
+ consumer = session.createConsumer(receiveDestination);
+ producer = session.createProducer(null);
+
+ consumer.setMessageListener(new Receiver());
+ }
+
+ /**
+ * Creates a new conversation context.
+ *
+ * @return A new conversation context.
+ */
+ public Conversation startConversation()
+ {
+ log.debug("public Conversation startConversation(): called");
+
+ Conversation conversation = new Conversation();
+ conversation.conversationId = conversationIdGenerator.getAndIncrement();
+
+ return conversation;
+ }
+
+ /**
+ * Ensures that the reply queue for a conversation exists.
+ *
+ * @param conversationId The conversation correlation id.
+ */
+ private void initQueueForId(long conversationId)
+ {
+ if (!idsToQueues.containsKey(conversationId))
+ {
+ idsToQueues.put(conversationId, ReflectionUtils.<BlockingQueue>newInstance(queueClass));
+ }
+ }
+
+ /**
+ * Clears the dead letter box, returning all messages that were in it.
+ *
+ * @return All messages in the dead letter box.
+ */
+ public Collection<Message> emptyDeadLetterBox()
+ {
+ log.debug("public Collection<Message> emptyDeadLetterBox(): called");
+
+ Collection<Message> result = new ArrayList<Message>();
+ deadLetterBox.drainTo(result);
+
+ return result;
+ }
+
+ /**
+ * Gets the controlSession over which the conversation is conducted.
+ *
+ * @return The controlSession over which the conversation is conducted.
+ */
+ public Session getSession()
+ {
+ // Conversation settings = threadLocals.get();
+
+ return session;
+ }
+
+ /**
+ * Used to hold a conversation context. This consists of a correlating id for the conversation, and a reply
+ * destination automatically updated to the last received reply-to destination.
+ */
+ public class Conversation
+ {
+ /** Holds the correlation id for the context. */
+ private long conversationId;
+
+ /**
+ * Holds the send destination for the context. This will automatically be updated to the most recently received
+ * reply-to destination.
+ */
+ private Destination sendDestination;
+
+ /**
+ * Sends a message to the default sending location. The correlation id of the message will be assigned by this
+ * method, overriding any previously set value.
+ *
+ * @param sendDestination The destination to send to. This may be null to use the last received reply-to
+ * destination.
+ * @param message The message to send.
+ *
+ * @throws JMSException All undelying JMSExceptions are allowed to fall through. This will also be thrown if no
+ * send destination is specified and there is no most recent reply-to destination available
+ * to use.
+ */
+ public void send(Destination sendDestination, Message message) throws JMSException
+ {
+ log.debug("public void send(Destination sendDestination = " + sendDestination + ", Message message = "
+ + message.getJMSMessageID() + "): called");
+
+ // Conversation settings = threadLocals.get();
+ // long conversationId = conversationId;
+ message.setJMSCorrelationID(Long.toString(conversationId));
+ message.setJMSReplyTo(receiveDestination);
+
+ // Ensure that the reply queue for this conversation exists.
+ initQueueForId(conversationId);
+
+ // Check if an overriding send to destination has been set or use the last reply-to if not.
+ Destination sendTo = null;
+
+ if (sendDestination != null)
+ {
+ sendTo = sendDestination;
+ }
+ else
+ {
+ throw new JMSException("The send destination was specified, and no most recent reply-to available to use.");
+ }
+
+ // Send the message.
+ synchronized (this)
+ {
+ producer.send(sendTo, message);
+ }
+ }
+
+ /**
+ * Gets the next message in an ongoing conversation. This method may block until such a message is received.
+ *
+ * @return The next incoming message in the conversation.
+ *
+ * @throws JMSException All undelying JMSExceptions are allowed to fall through. Thrown if the received message
+ * did not have its reply-to destination set up.
+ */
+ public Message receive() throws JMSException
+ {
+ log.debug("public Message receive(): called");
+
+ // Conversation settings = threadLocals.get();
+ // long conversationId = settings.conversationId;
+
+ // Ensure that the reply queue for this conversation exists.
+ initQueueForId(conversationId);
+
+ BlockingQueue<Message> queue = idsToQueues.get(conversationId);
+
+ try
+ {
+ Message result = queue.take();
+
+ // Keep the reply-to destination to send replies to.
+ sendDestination = result.getJMSReplyTo();
+
+ return result;
+ }
+ catch (InterruptedException e)
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Gets many messages in an ongoing conversation. If a limit is specified, then once that many messages are
+ * received they will be returned. If a timeout is specified, then all messages up to the limit, received within
+ * that timespan will be returned. At least one of the message count or timeout should be set to a value of
+ * 1 or greater.
+ *
+ * @param num The number of messages to receive, or all if this is less than 1.
+ * @param timeout The timeout in milliseconds to receive the messages in, or forever if this is less than 1.
+ *
+ * @return All messages received within the count limit and the timeout.
+ *
+ * @throws JMSException All undelying JMSExceptions are allowed to fall through.
+ */
+ public Collection<Message> receiveAll(int num, long timeout) throws JMSException
+ {
+ log.debug("public Collection<Message> receiveAll(int num = " + num + ", long timeout = " + timeout
+ + "): called");
+
+ // Check that a timeout or message count was set.
+ if ((num < 1) && (timeout < 1))
+ {
+ throw new IllegalArgumentException("At least one of message count (num) or timeout must be set.");
+ }
+
+ // Ensure that the reply queue for this conversation exists.
+ initQueueForId(conversationId);
+ BlockingQueue<Message> queue = idsToQueues.get(conversationId);
+
+ // Used to collect the received messages in.
+ Collection<Message> result = new ArrayList<Message>();
+
+ // Used to indicate when the timeout or message count has expired.
+ boolean receiveMore = true;
+
+ int messageCount = 0;
+
+ // Receive messages until the timeout or message count expires.
+ do
+ {
+ try
+ {
+ Message next = null;
+
+ // Try to receive the message with a timeout if one has been set.
+ if (timeout > 0)
+ {
+ next = queue.poll(timeout, TimeUnit.MILLISECONDS);
+
+ // Check if the timeout expired, and stop receiving if so.
+ if (next == null)
+ {
+ receiveMore = false;
+ }
+ }
+ // Receive the message without a timeout.
+ else
+ {
+ next = queue.take();
+ }
+
+ // Increment the message count if a message was received.
+ messageCount += (next != null) ? 1 : 0;
+
+ // Check if all the requested messages were received, and stop receiving if so.
+ if ((num > 0) && (messageCount >= num))
+ {
+ receiveMore = false;
+ }
+
+ // Keep the reply-to destination to send replies to.
+ sendDestination = (next != null) ? next.getJMSReplyTo() : sendDestination;
+
+ if (next != null)
+ {
+ result.add(next);
+ }
+ }
+ catch (InterruptedException e)
+ {
+ // Restore the threads interrupted status.
+ Thread.currentThread().interrupt();
+
+ // Stop receiving but return the messages received so far.
+ receiveMore = false;
+ }
+ }
+ while (receiveMore);
+
+ return result;
+ }
+
+ /**
+ * Completes the conversation. Any correlation id's pertaining to the conversation are no longer valid, and any
+ * incoming messages using them will go to the dead letter box.
+ */
+ public void end()
+ {
+ log.debug("public void end(): called");
+
+ // Ensure that the thread local for the current thread is cleaned up.
+ // Conversation settings = threadLocals.get();
+ // long conversationId = settings.conversationId;
+ // threadLocals.remove();
+
+ // Ensure that its queue is removed from the queue map.
+ BlockingQueue<Message> queue = idsToQueues.remove(conversationId);
+
+ // Move any outstanding messages on the threads conversation id into the dead letter box.
+ queue.drainTo(deadLetterBox);
+ }
+ }
+
+ /**
+ * Implements the message listener for this conversation handler.
+ */
+ protected class Receiver implements MessageListener
+ {
+ /**
+ * Handles all incoming messages in the ongoing conversations. These messages are split up by correaltion id
+ * and placed into queues.
+ *
+ * @param message The incoming message.
+ */
+ public void onMessage(Message message)
+ {
+ log.debug("public void onMessage(Message message = " + message + "): called");
+
+ try
+ {
+ Long conversationId = Long.parseLong(message.getJMSCorrelationID());
+
+ // Find the converstaion queue to place the message on. If there is no conversation for the message id,
+ // the the dead letter box queue is used.
+ BlockingQueue<Message> queue = idsToQueues.get(conversationId);
+ queue = (queue == null) ? deadLetterBox : queue;
+
+ queue.put(message);
+ }
+ catch (JMSException e)
+ {
+ throw new RuntimeException(e);
+ }
+ catch (InterruptedException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/utils/FailoverBaseCase.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/utils/FailoverBaseCase.java
new file mode 100644
index 0000000000..f6c481431a
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/utils/FailoverBaseCase.java
@@ -0,0 +1,94 @@
+/*
+ *
+ * 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.qpid.test.utils;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.qpid.client.AMQConnectionFactory;
+import org.apache.qpid.util.FileUtils;
+
+import javax.naming.NamingException;
+
+public class FailoverBaseCase extends QpidBrokerTestCase
+{
+ protected static final Logger _logger = LoggerFactory.getLogger(FailoverBaseCase.class);
+
+ public static final long DEFAULT_FAILOVER_TIME = 10000L;
+
+ protected void setUp() throws java.lang.Exception
+ {
+ super.setUp();
+ startBroker(getFailingPort());
+ }
+
+ /**
+ * We are using failover factories
+ *
+ * @return a connection
+ * @throws Exception
+ */
+ @Override
+ public AMQConnectionFactory getConnectionFactory() throws NamingException
+ {
+ _logger.info("get ConnectionFactory");
+ if (_connectionFactory == null)
+ {
+ if (Boolean.getBoolean("profile.use_ssl"))
+ {
+ _connectionFactory = getConnectionFactory("failover.ssl");
+ }
+ else
+ {
+ _connectionFactory = getConnectionFactory("failover");
+ }
+ }
+ return _connectionFactory;
+ }
+
+ public void tearDown() throws Exception
+ {
+ try
+ {
+ super.tearDown();
+ }
+ finally
+ {
+ // Ensure we shutdown any secondary brokers, even if we are unable
+ // to cleanly tearDown the QTC.
+ stopBroker(getFailingPort());
+ FileUtils.deleteDirectory(System.getProperty("QPID_WORK") + "/" + getFailingPort());
+ }
+ }
+
+ public void failBroker(int port)
+ {
+ try
+ {
+ //TODO: use killBroker instead
+ stopBroker(port);
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/qpid/java/systests/src/test/java/org/apache/qpid/test/utils/QpidClientConnection.java b/qpid/java/systests/src/test/java/org/apache/qpid/test/utils/QpidClientConnection.java
new file mode 100644
index 0000000000..0e0032da64
--- /dev/null
+++ b/qpid/java/systests/src/test/java/org/apache/qpid/test/utils/QpidClientConnection.java
@@ -0,0 +1,288 @@
+/*
+ *
+ * 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.qpid.test.utils;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.JMSAMQException;
+
+import javax.jms.Connection;
+import javax.jms.ExceptionListener;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+
+public class QpidClientConnection extends QpidBrokerTestCase implements ExceptionListener
+{
+ private static final Logger _logger = LoggerFactory.getLogger(QpidClientConnection.class);
+
+ private boolean transacted = true;
+ private int ackMode = Session.CLIENT_ACKNOWLEDGE;
+ private Connection connection;
+
+ private String virtualHost;
+ private String brokerlist;
+ private int prefetch;
+ protected Session session;
+ protected boolean connected;
+
+ public QpidClientConnection(String broker)
+ {
+ super();
+ setVirtualHost("/test");
+ setBrokerList(broker);
+ setPrefetch(5000);
+ }
+
+
+ public Connection getConnection()
+ {
+ return connection;
+ }
+
+ public void connect() throws JMSException
+ {
+ if (!connected)
+ {
+ /*
+ * amqp://[user:pass@][clientid]/virtualhost?
+ * brokerlist='[transport://]host[:port][?option='value'[&option='value']];'
+ * [&failover='method[?option='value'[&option='value']]']
+ * [&option='value']"
+ */
+ String brokerUrl = "amqp://guest:guest@" + virtualHost + "?brokerlist='" + brokerlist + "'";
+ try
+ {
+ _logger.info("connecting to Qpid :" + brokerUrl);
+ connection = getConnection("guest", "guest") ;
+ // register exception listener
+ connection.setExceptionListener(this);
+
+ session = ((AMQConnection) connection).createSession(transacted, ackMode, prefetch);
+
+ _logger.info("starting connection");
+ connection.start();
+
+ connected = true;
+ }
+ catch (Exception e)
+ {
+ throw new JMSAMQException("URL syntax error in [" + brokerUrl + "]: " + e.getMessage(), e);
+ }
+ }
+ }
+
+ public void disconnect() throws Exception
+ {
+ if (connected)
+ {
+ session.commit();
+ session.close();
+ connection.close();
+ connected = false;
+ _logger.info("disconnected");
+ }
+ }
+
+ public void disconnectWithoutCommit() throws JMSException
+ {
+ if (connected)
+ {
+ session.close();
+ connection.close();
+ connected = false;
+ _logger.info("disconnected without commit");
+ }
+ }
+
+ public String getBrokerList()
+ {
+ return brokerlist;
+ }
+
+ public void setBrokerList(String brokerlist)
+ {
+ this.brokerlist = brokerlist;
+ }
+
+ public String getVirtualHost()
+ {
+ return virtualHost;
+ }
+
+ public void setVirtualHost(String virtualHost)
+ {
+ this.virtualHost = virtualHost;
+ }
+
+ public void setPrefetch(int prefetch)
+ {
+ this.prefetch = prefetch;
+ }
+
+ /** override as necessary */
+ public void onException(JMSException exception)
+ {
+ _logger.info("ExceptionListener event: error " + exception.getErrorCode() + ", message: " + exception.getMessage());
+ }
+
+ public boolean isConnected()
+ {
+ return connected;
+ }
+
+ public Session getSession()
+ {
+ return session;
+ }
+
+ /**
+ * Put a String as a text messages, repeat n times. A null payload will result in a null message.
+ *
+ * @param queueName The queue name to put to
+ * @param payload the content of the payload
+ * @param copies the number of messages to put
+ *
+ * @throws javax.jms.JMSException any exception that occurs
+ */
+ public void put(String queueName, String payload, int copies) throws JMSException
+ {
+ if (!connected)
+ {
+ connect();
+ }
+
+ _logger.info("putting to queue " + queueName);
+ Queue queue = session.createQueue(queueName);
+
+ final MessageProducer sender = session.createProducer(queue);
+
+ for (int i = 0; i < copies; i++)
+ {
+ Message m = session.createTextMessage(payload + i);
+ m.setIntProperty("index", i + 1);
+ sender.send(m);
+ }
+
+ session.commit();
+ sender.close();
+ _logger.info("put " + copies + " copies");
+ }
+
+ /**
+ * GET the top message on a queue. Consumes the message. Accepts timeout value.
+ *
+ * @param queueName The quename to get from
+ * @param readTimeout The timeout to use
+ *
+ * @return the content of the text message if any
+ *
+ * @throws javax.jms.JMSException any exception that occured
+ */
+ public Message getNextMessage(String queueName, long readTimeout) throws JMSException
+ {
+ if (!connected)
+ {
+ connect();
+ }
+
+ Queue queue = session.createQueue(queueName);
+
+ final MessageConsumer consumer = session.createConsumer(queue);
+
+ Message message = consumer.receive(readTimeout);
+ session.commit();
+ consumer.close();
+
+ Message result;
+
+ // all messages we consume should be TextMessages
+ if (message instanceof TextMessage)
+ {
+ result = ((TextMessage) message);
+ }
+ else if (null == message)
+ {
+ result = null;
+ }
+ else
+ {
+ _logger.info("warning: received non-text message");
+ result = message;
+ }
+
+ return result;
+ }
+
+ /**
+ * GET the top message on a queue. Consumes the message.
+ *
+ * @param queueName The Queuename to get from
+ *
+ * @return The string content of the text message, if any received
+ *
+ * @throws javax.jms.JMSException any exception that occurs
+ */
+ public Message getNextMessage(String queueName) throws JMSException
+ {
+ return getNextMessage(queueName, 0);
+ }
+
+ /**
+ * Completely clears a queue. For readTimeout behaviour see Javadocs for javax.jms.MessageConsumer.
+ *
+ * @param queueName The Queue name to consume from
+ * @param readTimeout The timeout for each consume
+ *
+ * @throws javax.jms.JMSException Any exception that occurs during the consume
+ * @throws InterruptedException If the consume thread was interrupted during a consume.
+ */
+ public void consume(String queueName, int readTimeout) throws JMSException, InterruptedException
+ {
+ if (!connected)
+ {
+ connect();
+ }
+
+ _logger.info("consuming queue " + queueName);
+ Queue queue = session.createQueue(queueName);
+
+ final MessageConsumer consumer = session.createConsumer(queue);
+ int messagesReceived = 0;
+
+ _logger.info("consuming...");
+ while ((consumer.receive(readTimeout)) != null)
+ {
+ messagesReceived++;
+ }
+
+ session.commit();
+ consumer.close();
+ _logger.info("consumed: " + messagesReceived);
+ }
+}